Skip to content

Commit 16f100b

Browse files
authored
Merge pull request #5307 from ziglang/self-hosted-incremental-compilation
rework self-hosted compiler for incremental builds
2 parents 9a22c8b + b0968ab commit 16f100b

36 files changed

+5143
-5429
lines changed

README.md

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -71,40 +71,3 @@ can do about it. See that issue for a workaround you can do in the meantime.
7171
##### Windows
7272

7373
See https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows
74-
75-
### Stage 2: Build Self-Hosted Zig from Zig Source Code
76-
77-
*Note: Stage 2 compiler is not complete. Beta users of Zig should use the
78-
Stage 1 compiler for now.*
79-
80-
Dependencies are the same as Stage 1, except now you can use stage 1 to compile
81-
Zig code.
82-
83-
```
84-
bin/zig build --prefix $(pwd)/stage2
85-
```
86-
87-
This produces `./stage2/bin/zig` which can be used for testing and development.
88-
Once it is feature complete, it will be used to build stage 3 - the final compiler
89-
binary.
90-
91-
### Stage 3: Rebuild Self-Hosted Zig Using the Self-Hosted Compiler
92-
93-
*Note: Stage 2 compiler is not yet able to build Stage 3. Building Stage 3 is
94-
not yet supported.*
95-
96-
Once the self-hosted compiler can build itself, this will be the actual
97-
compiler binary that we will install to the system. Until then, users should
98-
use stage 1.
99-
100-
#### Debug / Development Build
101-
102-
```
103-
./stage2/bin/zig build --prefix $(pwd)/stage3
104-
```
105-
106-
#### Release / Install Build
107-
108-
```
109-
./stage2/bin/zig build install -Drelease
110-
```

build.zig

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,28 +51,29 @@ pub fn build(b: *Builder) !void {
5151

5252
var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
5353
exe.setBuildMode(mode);
54+
test_step.dependOn(&exe.step);
55+
b.default_step.dependOn(&exe.step);
5456

5557
const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
5658
const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
5759
const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release;
5860
const skip_release_safe = b.option(bool, "skip-release-safe", "Main test suite skips release-safe builds") orelse skip_release;
5961
const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false;
6062
const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false;
61-
const skip_self_hosted = (b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false) or true; // TODO evented I/O good enough that this passes everywhere
62-
if (!skip_self_hosted) {
63-
test_step.dependOn(&exe.step);
64-
}
6563

6664
const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
67-
if (!only_install_lib_files and !skip_self_hosted) {
65+
const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse false;
66+
if (enable_llvm) {
6867
var ctx = parseConfigH(b, config_h_text);
6968
ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
7069

7170
try configureStage2(b, exe, ctx);
72-
73-
b.default_step.dependOn(&exe.step);
71+
}
72+
if (!only_install_lib_files) {
7473
exe.install();
7574
}
75+
const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
76+
if (link_libc) exe.linkLibC();
7677

7778
b.installDirectory(InstallDirectoryOptions{
7879
.source_dir = "lib",

lib/std/array_list.zig

Lines changed: 242 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ const Allocator = mem.Allocator;
88
/// A contiguous, growable list of items in memory.
99
/// This is a wrapper around an array of T values. Initialize with `init`.
1010
pub fn ArrayList(comptime T: type) type {
11-
return AlignedArrayList(T, null);
11+
return ArrayListAligned(T, null);
1212
}
1313

14-
pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
14+
pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
1515
if (alignment) |a| {
1616
if (a == @alignOf(T)) {
17-
return AlignedArrayList(T, null);
17+
return ArrayListAligned(T, null);
1818
}
1919
}
2020
return struct {
@@ -76,6 +76,10 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
7676
};
7777
}
7878

79+
pub fn toUnmanaged(self: Self) ArrayListAlignedUnmanaged(T, alignment) {
80+
return .{ .items = self.items, .capacity = self.capacity };
81+
}
82+
7983
/// The caller owns the returned memory. ArrayList becomes empty.
8084
pub fn toOwnedSlice(self: *Self) Slice {
8185
const allocator = self.allocator;
@@ -84,8 +88,8 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
8488
return result;
8589
}
8690

87-
/// Insert `item` at index `n`. Moves `list[n .. list.len]`
88-
/// to make room.
91+
/// Insert `item` at index `n` by moving `list[n .. list.len]` to make room.
92+
/// This operation is O(N).
8993
pub fn insert(self: *Self, n: usize, item: T) !void {
9094
try self.ensureCapacity(self.items.len + 1);
9195
self.items.len += 1;
@@ -94,8 +98,7 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
9498
self.items[n] = item;
9599
}
96100

97-
/// Insert slice `items` at index `i`. Moves
98-
/// `list[i .. list.len]` to make room.
101+
/// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room.
99102
/// This operation is O(N).
100103
pub fn insertSlice(self: *Self, i: usize, items: SliceConst) !void {
101104
try self.ensureCapacity(self.items.len + items.len);
@@ -146,10 +149,15 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
146149
/// Append the slice of items to the list. Allocates more
147150
/// memory as necessary.
148151
pub fn appendSlice(self: *Self, items: SliceConst) !void {
152+
try self.ensureCapacity(self.items.len + items.len);
153+
self.appendSliceAssumeCapacity(items);
154+
}
155+
156+
/// Append the slice of items to the list, asserting the capacity is already
157+
/// enough to store the new items.
158+
pub fn appendSliceAssumeCapacity(self: *Self, items: SliceConst) void {
149159
const oldlen = self.items.len;
150160
const newlen = self.items.len + items.len;
151-
152-
try self.ensureCapacity(newlen);
153161
self.items.len = newlen;
154162
mem.copy(T, self.items[oldlen..], items);
155163
}
@@ -259,6 +267,231 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
259267
};
260268
}
261269

270+
/// Bring-your-own allocator with every function call.
271+
/// Initialize directly and deinitialize with `deinit` or use `toOwnedSlice`.
272+
pub fn ArrayListUnmanaged(comptime T: type) type {
273+
return ArrayListAlignedUnmanaged(T, null);
274+
}
275+
276+
pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) type {
277+
if (alignment) |a| {
278+
if (a == @alignOf(T)) {
279+
return ArrayListAlignedUnmanaged(T, null);
280+
}
281+
}
282+
return struct {
283+
const Self = @This();
284+
285+
/// Content of the ArrayList.
286+
items: Slice = &[_]T{},
287+
capacity: usize = 0,
288+
289+
pub const Slice = if (alignment) |a| ([]align(a) T) else []T;
290+
pub const SliceConst = if (alignment) |a| ([]align(a) const T) else []const T;
291+
292+
/// Initialize with capacity to hold at least num elements.
293+
/// Deinitialize with `deinit` or use `toOwnedSlice`.
294+
pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
295+
var self = Self.init(allocator);
296+
try self.ensureCapacity(allocator, num);
297+
return self;
298+
}
299+
300+
/// Release all allocated memory.
301+
pub fn deinit(self: *Self, allocator: *Allocator) void {
302+
allocator.free(self.allocatedSlice());
303+
self.* = undefined;
304+
}
305+
306+
pub fn toManaged(self: *Self, allocator: *Allocator) ArrayListAligned(T, alignment) {
307+
return .{ .items = self.items, .capacity = self.capacity, .allocator = allocator };
308+
}
309+
310+
/// The caller owns the returned memory. ArrayList becomes empty.
311+
pub fn toOwnedSlice(self: *Self, allocator: *Allocator) Slice {
312+
const result = allocator.shrink(self.allocatedSlice(), self.items.len);
313+
self.* = Self{};
314+
return result;
315+
}
316+
317+
/// Insert `item` at index `n`. Moves `list[n .. list.len]`
318+
/// to make room.
319+
pub fn insert(self: *Self, allocator: *Allocator, n: usize, item: T) !void {
320+
try self.ensureCapacity(allocator, self.items.len + 1);
321+
self.items.len += 1;
322+
323+
mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]);
324+
self.items[n] = item;
325+
}
326+
327+
/// Insert slice `items` at index `i`. Moves
328+
/// `list[i .. list.len]` to make room.
329+
/// This operation is O(N).
330+
pub fn insertSlice(self: *Self, allocator: *Allocator, i: usize, items: SliceConst) !void {
331+
try self.ensureCapacity(allocator, self.items.len + items.len);
332+
self.items.len += items.len;
333+
334+
mem.copyBackwards(T, self.items[i + items.len .. self.items.len], self.items[i .. self.items.len - items.len]);
335+
mem.copy(T, self.items[i .. i + items.len], items);
336+
}
337+
338+
/// Extend the list by 1 element. Allocates more memory as necessary.
339+
pub fn append(self: *Self, allocator: *Allocator, item: T) !void {
340+
const new_item_ptr = try self.addOne(allocator);
341+
new_item_ptr.* = item;
342+
}
343+
344+
/// Extend the list by 1 element, but asserting `self.capacity`
345+
/// is sufficient to hold an additional item.
346+
pub fn appendAssumeCapacity(self: *Self, item: T) void {
347+
const new_item_ptr = self.addOneAssumeCapacity();
348+
new_item_ptr.* = item;
349+
}
350+
351+
/// Remove the element at index `i` from the list and return its value.
352+
/// Asserts the array has at least one item.
353+
/// This operation is O(N).
354+
pub fn orderedRemove(self: *Self, i: usize) T {
355+
const newlen = self.items.len - 1;
356+
if (newlen == i) return self.pop();
357+
358+
const old_item = self.items[i];
359+
for (self.items[i..newlen]) |*b, j| b.* = self.items[i + 1 + j];
360+
self.items[newlen] = undefined;
361+
self.items.len = newlen;
362+
return old_item;
363+
}
364+
365+
/// Removes the element at the specified index and returns it.
366+
/// The empty slot is filled from the end of the list.
367+
/// This operation is O(1).
368+
pub fn swapRemove(self: *Self, i: usize) T {
369+
if (self.items.len - 1 == i) return self.pop();
370+
371+
const old_item = self.items[i];
372+
self.items[i] = self.pop();
373+
return old_item;
374+
}
375+
376+
/// Append the slice of items to the list. Allocates more
377+
/// memory as necessary.
378+
pub fn appendSlice(self: *Self, allocator: *Allocator, items: SliceConst) !void {
379+
try self.ensureCapacity(allocator, self.items.len + items.len);
380+
self.appendSliceAssumeCapacity(items);
381+
}
382+
383+
/// Append the slice of items to the list, asserting the capacity is enough
384+
/// to store the new items.
385+
pub fn appendSliceAssumeCapacity(self: *Self, items: SliceConst) void {
386+
const oldlen = self.items.len;
387+
const newlen = self.items.len + items.len;
388+
389+
self.items.len = newlen;
390+
mem.copy(T, self.items[oldlen..], items);
391+
}
392+
393+
/// Same as `append` except it returns the number of bytes written, which is always the same
394+
/// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
395+
/// This function may be called only when `T` is `u8`.
396+
fn appendWrite(self: *Self, allocator: *Allocator, m: []const u8) !usize {
397+
try self.appendSlice(allocator, m);
398+
return m.len;
399+
}
400+
401+
/// Append a value to the list `n` times.
402+
/// Allocates more memory as necessary.
403+
pub fn appendNTimes(self: *Self, allocator: *Allocator, value: T, n: usize) !void {
404+
const old_len = self.items.len;
405+
try self.resize(self.items.len + n);
406+
mem.set(T, self.items[old_len..self.items.len], value);
407+
}
408+
409+
/// Adjust the list's length to `new_len`.
410+
/// Does not initialize added items if any.
411+
pub fn resize(self: *Self, allocator: *Allocator, new_len: usize) !void {
412+
try self.ensureCapacity(allocator, new_len);
413+
self.items.len = new_len;
414+
}
415+
416+
/// Reduce allocated capacity to `new_len`.
417+
/// Invalidates element pointers.
418+
pub fn shrink(self: *Self, allocator: *Allocator, new_len: usize) void {
419+
assert(new_len <= self.items.len);
420+
421+
self.items = allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) {
422+
error.OutOfMemory => { // no problem, capacity is still correct then.
423+
self.items.len = new_len;
424+
return;
425+
},
426+
};
427+
self.capacity = new_len;
428+
}
429+
430+
pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void {
431+
var better_capacity = self.capacity;
432+
if (better_capacity >= new_capacity) return;
433+
434+
while (true) {
435+
better_capacity += better_capacity / 2 + 8;
436+
if (better_capacity >= new_capacity) break;
437+
}
438+
439+
const new_memory = try allocator.realloc(self.allocatedSlice(), better_capacity);
440+
self.items.ptr = new_memory.ptr;
441+
self.capacity = new_memory.len;
442+
}
443+
444+
/// Increases the array's length to match the full capacity that is already allocated.
445+
/// The new elements have `undefined` values.
446+
/// This operation does not invalidate any element pointers.
447+
pub fn expandToCapacity(self: *Self) void {
448+
self.items.len = self.capacity;
449+
}
450+
451+
/// Increase length by 1, returning pointer to the new item.
452+
/// The returned pointer becomes invalid when the list is resized.
453+
pub fn addOne(self: *Self, allocator: *Allocator) !*T {
454+
const newlen = self.items.len + 1;
455+
try self.ensureCapacity(allocator, newlen);
456+
return self.addOneAssumeCapacity();
457+
}
458+
459+
/// Increase length by 1, returning pointer to the new item.
460+
/// Asserts that there is already space for the new item without allocating more.
461+
/// The returned pointer becomes invalid when the list is resized.
462+
/// This operation does not invalidate any element pointers.
463+
pub fn addOneAssumeCapacity(self: *Self) *T {
464+
assert(self.items.len < self.capacity);
465+
466+
self.items.len += 1;
467+
return &self.items[self.items.len - 1];
468+
}
469+
470+
/// Remove and return the last element from the list.
471+
/// Asserts the list has at least one item.
472+
/// This operation does not invalidate any element pointers.
473+
pub fn pop(self: *Self) T {
474+
const val = self.items[self.items.len - 1];
475+
self.items.len -= 1;
476+
return val;
477+
}
478+
479+
/// Remove and return the last element from the list.
480+
/// If the list is empty, returns `null`.
481+
/// This operation does not invalidate any element pointers.
482+
pub fn popOrNull(self: *Self) ?T {
483+
if (self.items.len == 0) return null;
484+
return self.pop();
485+
}
486+
487+
/// For a nicer API, `items.len` is the length, not the capacity.
488+
/// This requires "unsafe" slicing.
489+
fn allocatedSlice(self: Self) Slice {
490+
return self.items.ptr[0..self.capacity];
491+
}
492+
};
493+
}
494+
262495
test "std.ArrayList.init" {
263496
var list = ArrayList(i32).init(testing.allocator);
264497
defer list.deinit();

0 commit comments

Comments
 (0)