authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-17 13:53:27-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-05-17 13:53:27-04:00
log16f100b82e4075a047f008c0de6c44fc418eb58e
tree93555fbcba29bcd1120349a03ae6904321cb7718
parent9a22c8b6ca98fd01795f8cd4f3e9d92311175f13
parentb0968abccbfb4072528c3b5e039bc03b27af89a1
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5307 from ziglang/self-hosted-incremental-compilation

rework self-hosted compiler for incremental builds

37 files changed, 6309 insertions(+), 6595 deletions(-)

README.md-37
......@@ -71,40 +71,3 @@ can do about it. See that issue for a workaround you can do in the meantime.
7171##### Windows
7272
7373See 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
78Stage 1 compiler for now.*
79
80Dependencies are the same as Stage 1, except now you can use stage 1 to compile
81Zig code.
82
83```
84bin/zig build --prefix $(pwd)/stage2
85```
86
87This produces `./stage2/bin/zig` which can be used for testing and development.
88Once it is feature complete, it will be used to build stage 3 - the final compiler
89binary.
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
94not yet supported.*
95
96Once the self-hosted compiler can build itself, this will be the actual
97compiler binary that we will install to the system. Until then, users should
98use 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+8-7
......@@ -51,6 +51,8 @@ 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;
......@@ -58,21 +60,20 @@ pub fn build(b: *Builder) !void {
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+242-9
......@@ -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`.
1010pub fn ArrayList(comptime T: type) type {
11 return AlignedArrayList(T, null);
11 return ArrayListAligned(T, null);
1212}
1313
14pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
14pub 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`.
272pub fn ArrayListUnmanaged(comptime T: type) type {
273 return ArrayListAlignedUnmanaged(T, null);
274}
275
276pub 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
262495test "std.ArrayList.init" {
263496 var list = ArrayList(i32).init(testing.allocator);
264497 defer list.deinit();
lib/std/fifo.zig+20-17
......@@ -191,8 +191,8 @@ pub fn LinearFifo(
191191 }
192192
193193 /// Read the next item from the fifo
194 pub fn readItem(self: *Self) !T {
195 if (self.count == 0) return error.EndOfStream;
194 pub fn readItem(self: *Self) ?T {
195 if (self.count == 0) return null;
196196
197197 const c = self.buf[self.head];
198198 self.discard(1);
......@@ -282,7 +282,10 @@ pub fn LinearFifo(
282282 /// Write a single item to the fifo
283283 pub fn writeItem(self: *Self, item: T) !void {
284284 try self.ensureUnusedCapacity(1);
285 return self.writeItemAssumeCapacity(item);
286 }
285287
288 pub fn writeItemAssumeCapacity(self: *Self, item: T) void {
286289 var tail = self.head + self.count;
287290 if (powers_of_two) {
288291 tail &= self.buf.len - 1;
......@@ -342,10 +345,10 @@ pub fn LinearFifo(
342345 }
343346 }
344347
345 /// Peek at the item at `offset`
346 pub fn peekItem(self: Self, offset: usize) error{EndOfStream}!T {
347 if (offset >= self.count)
348 return error.EndOfStream;
348 /// Returns the item at `offset`.
349 /// Asserts offset is within bounds.
350 pub fn peekItem(self: Self, offset: usize) T {
351 assert(offset < self.count);
349352
350353 var index = self.head + offset;
351354 if (powers_of_two) {
......@@ -369,18 +372,18 @@ test "LinearFifo(u8, .Dynamic)" {
369372 {
370373 var i: usize = 0;
371374 while (i < 5) : (i += 1) {
372 try fifo.write(&[_]u8{try fifo.peekItem(i)});
375 try fifo.write(&[_]u8{fifo.peekItem(i)});
373376 }
374377 testing.expectEqual(@as(usize, 10), fifo.readableLength());
375378 testing.expectEqualSlices(u8, "HELLOHELLO", fifo.readableSlice(0));
376379 }
377380
378381 {
379 testing.expectEqual(@as(u8, 'H'), try fifo.readItem());
380 testing.expectEqual(@as(u8, 'E'), try fifo.readItem());
381 testing.expectEqual(@as(u8, 'L'), try fifo.readItem());
382 testing.expectEqual(@as(u8, 'L'), try fifo.readItem());
383 testing.expectEqual(@as(u8, 'O'), try fifo.readItem());
382 testing.expectEqual(@as(u8, 'H'), fifo.readItem().?);
383 testing.expectEqual(@as(u8, 'E'), fifo.readItem().?);
384 testing.expectEqual(@as(u8, 'L'), fifo.readItem().?);
385 testing.expectEqual(@as(u8, 'L'), fifo.readItem().?);
386 testing.expectEqual(@as(u8, 'O'), fifo.readItem().?);
384387 }
385388 testing.expectEqual(@as(usize, 5), fifo.readableLength());
386389
......@@ -451,11 +454,11 @@ test "LinearFifo" {
451454 testing.expectEqual(@as(usize, 5), fifo.readableLength());
452455
453456 {
454 testing.expectEqual(@as(T, 0), try fifo.readItem());
455 testing.expectEqual(@as(T, 1), try fifo.readItem());
456 testing.expectEqual(@as(T, 1), try fifo.readItem());
457 testing.expectEqual(@as(T, 0), try fifo.readItem());
458 testing.expectEqual(@as(T, 1), try fifo.readItem());
457 testing.expectEqual(@as(T, 0), fifo.readItem().?);
458 testing.expectEqual(@as(T, 1), fifo.readItem().?);
459 testing.expectEqual(@as(T, 1), fifo.readItem().?);
460 testing.expectEqual(@as(T, 0), fifo.readItem().?);
461 testing.expectEqual(@as(T, 1), fifo.readItem().?);
459462 testing.expectEqual(@as(usize, 0), fifo.readableLength());
460463 }
461464
lib/std/fs/file.zig+27
......@@ -527,6 +527,33 @@ pub const File = struct {
527527 }
528528 }
529529
530 pub const CopyRangeError = PWriteError || PReadError;
531
532 pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) CopyRangeError!usize {
533 // TODO take advantage of copy_file_range OS APIs
534 var buf: [8 * 4096]u8 = undefined;
535 const adjusted_count = math.min(buf.len, len);
536 const amt_read = try in.pread(buf[0..adjusted_count], in_offset);
537 if (amt_read == 0) return @as(usize, 0);
538 return out.pwrite(buf[0..amt_read], out_offset);
539 }
540
541 /// Returns the number of bytes copied. If the number read is smaller than `buffer.len`, it
542 /// means the in file reached the end. Reaching the end of a file is not an error condition.
543 pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) CopyRangeError!usize {
544 var total_bytes_copied: usize = 0;
545 var in_off = in_offset;
546 var out_off = out_offset;
547 while (total_bytes_copied < len) {
548 const amt_copied = try copyRange(in, in_off, out, out_off, len - total_bytes_copied);
549 if (amt_copied == 0) return total_bytes_copied;
550 total_bytes_copied += amt_copied;
551 in_off += amt_copied;
552 out_off += amt_copied;
553 }
554 return total_bytes_copied;
555 }
556
530557 pub const WriteFileOptions = struct {
531558 in_offset: u64 = 0,
532559
lib/std/hash_map.zig+5-1
......@@ -10,7 +10,7 @@ const Wyhash = std.hash.Wyhash;
1010const Allocator = mem.Allocator;
1111const builtin = @import("builtin");
1212
13const want_modification_safety = builtin.mode != .ReleaseFast;
13const want_modification_safety = std.debug.runtime_safety;
1414const debug_u32 = if (want_modification_safety) u32 else void;
1515
1616pub fn AutoHashMap(comptime K: type, comptime V: type) type {
......@@ -219,6 +219,10 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
219219 return put_result.old_kv;
220220 }
221221
222 pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void {
223 assert(self.putAssumeCapacity(key, value) == null);
224 }
225
222226 pub fn get(hm: *const Self, key: K) ?*KV {
223227 if (hm.entries.len == 0) {
224228 return null;
lib/std/heap.zig+1-89
......@@ -11,6 +11,7 @@ const maxInt = std.math.maxInt;
1111
1212pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
1313pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
14pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
1415
1516const Allocator = mem.Allocator;
1617
......@@ -510,95 +511,6 @@ pub const HeapAllocator = switch (builtin.os.tag) {
510511 else => @compileError("Unsupported OS"),
511512};
512513
513/// This allocator takes an existing allocator, wraps it, and provides an interface
514/// where you can allocate without freeing, and then free it all together.
515pub const ArenaAllocator = struct {
516 allocator: Allocator,
517
518 child_allocator: *Allocator,
519 buffer_list: std.SinglyLinkedList([]u8),
520 end_index: usize,
521
522 const BufNode = std.SinglyLinkedList([]u8).Node;
523
524 pub fn init(child_allocator: *Allocator) ArenaAllocator {
525 return ArenaAllocator{
526 .allocator = Allocator{
527 .reallocFn = realloc,
528 .shrinkFn = shrink,
529 },
530 .child_allocator = child_allocator,
531 .buffer_list = std.SinglyLinkedList([]u8).init(),
532 .end_index = 0,
533 };
534 }
535
536 pub fn deinit(self: ArenaAllocator) void {
537 var it = self.buffer_list.first;
538 while (it) |node| {
539 // this has to occur before the free because the free frees node
540 const next_it = node.next;
541 self.child_allocator.free(node.data);
542 it = next_it;
543 }
544 }
545
546 fn createNode(self: *ArenaAllocator, prev_len: usize, minimum_size: usize) !*BufNode {
547 const actual_min_size = minimum_size + @sizeOf(BufNode);
548 var len = prev_len;
549 while (true) {
550 len += len / 2;
551 len += mem.page_size - @rem(len, mem.page_size);
552 if (len >= actual_min_size) break;
553 }
554 const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len);
555 const buf_node_slice = mem.bytesAsSlice(BufNode, buf[0..@sizeOf(BufNode)]);
556 const buf_node = &buf_node_slice[0];
557 buf_node.* = BufNode{
558 .data = buf,
559 .next = null,
560 };
561 self.buffer_list.prepend(buf_node);
562 self.end_index = 0;
563 return buf_node;
564 }
565
566 fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
567 const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);
568
569 var cur_node = if (self.buffer_list.first) |first_node| first_node else try self.createNode(0, n + alignment);
570 while (true) {
571 const cur_buf = cur_node.data[@sizeOf(BufNode)..];
572 const addr = @ptrToInt(cur_buf.ptr) + self.end_index;
573 const adjusted_addr = mem.alignForward(addr, alignment);
574 const adjusted_index = self.end_index + (adjusted_addr - addr);
575 const new_end_index = adjusted_index + n;
576 if (new_end_index > cur_buf.len) {
577 cur_node = try self.createNode(cur_buf.len, n + alignment);
578 continue;
579 }
580 const result = cur_buf[adjusted_index..new_end_index];
581 self.end_index = new_end_index;
582 return result;
583 }
584 }
585
586 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
587 if (new_size <= old_mem.len and new_align <= new_size) {
588 // We can't do anything with the memory, so tell the client to keep it.
589 return error.OutOfMemory;
590 } else {
591 const result = try alloc(allocator, new_size, new_align);
592 @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
593 return result;
594 }
595 }
596
597 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
598 return old_mem[0..new_size];
599 }
600};
601
602514pub const FixedBufferAllocator = struct {
603515 allocator: Allocator,
604516 end_index: usize,
lib/std/heap/arena_allocator.zig created+102
......@@ -0,0 +1,102 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const mem = std.mem;
4const Allocator = std.mem.Allocator;
5
6/// This allocator takes an existing allocator, wraps it, and provides an interface
7/// where you can allocate without freeing, and then free it all together.
8pub const ArenaAllocator = struct {
9 allocator: Allocator,
10
11 child_allocator: *Allocator,
12 state: State,
13
14 /// Inner state of ArenaAllocator. Can be stored rather than the entire ArenaAllocator
15 /// as a memory-saving optimization.
16 pub const State = struct {
17 buffer_list: std.SinglyLinkedList([]u8) = @as(std.SinglyLinkedList([]u8), .{}),
18 end_index: usize = 0,
19
20 pub fn promote(self: State, child_allocator: *Allocator) ArenaAllocator {
21 return .{
22 .allocator = Allocator{
23 .reallocFn = realloc,
24 .shrinkFn = shrink,
25 },
26 .child_allocator = child_allocator,
27 .state = self,
28 };
29 }
30 };
31
32 const BufNode = std.SinglyLinkedList([]u8).Node;
33
34 pub fn init(child_allocator: *Allocator) ArenaAllocator {
35 return (State{}).promote(child_allocator);
36 }
37
38 pub fn deinit(self: ArenaAllocator) void {
39 var it = self.state.buffer_list.first;
40 while (it) |node| {
41 // this has to occur before the free because the free frees node
42 const next_it = node.next;
43 self.child_allocator.free(node.data);
44 it = next_it;
45 }
46 }
47
48 fn createNode(self: *ArenaAllocator, prev_len: usize, minimum_size: usize) !*BufNode {
49 const actual_min_size = minimum_size + @sizeOf(BufNode);
50 var len = prev_len;
51 while (true) {
52 len += len / 2;
53 len += mem.page_size - @rem(len, mem.page_size);
54 if (len >= actual_min_size) break;
55 }
56 const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len);
57 const buf_node_slice = mem.bytesAsSlice(BufNode, buf[0..@sizeOf(BufNode)]);
58 const buf_node = &buf_node_slice[0];
59 buf_node.* = BufNode{
60 .data = buf,
61 .next = null,
62 };
63 self.state.buffer_list.prepend(buf_node);
64 self.state.end_index = 0;
65 return buf_node;
66 }
67
68 fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
69 const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);
70
71 var cur_node = if (self.state.buffer_list.first) |first_node| first_node else try self.createNode(0, n + alignment);
72 while (true) {
73 const cur_buf = cur_node.data[@sizeOf(BufNode)..];
74 const addr = @ptrToInt(cur_buf.ptr) + self.state.end_index;
75 const adjusted_addr = mem.alignForward(addr, alignment);
76 const adjusted_index = self.state.end_index + (adjusted_addr - addr);
77 const new_end_index = adjusted_index + n;
78 if (new_end_index > cur_buf.len) {
79 cur_node = try self.createNode(cur_buf.len, n + alignment);
80 continue;
81 }
82 const result = cur_buf[adjusted_index..new_end_index];
83 self.state.end_index = new_end_index;
84 return result;
85 }
86 }
87
88 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
89 if (new_size <= old_mem.len and new_align <= new_size) {
90 // We can't do anything with the memory, so tell the client to keep it.
91 return error.OutOfMemory;
92 } else {
93 const result = try alloc(allocator, new_size, new_align);
94 @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
95 return result;
96 }
97 }
98
99 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
100 return old_mem[0..new_size];
101 }
102};
lib/std/linked_list.zig+1-1
......@@ -49,7 +49,7 @@ pub fn SinglyLinkedList(comptime T: type) type {
4949 }
5050 };
5151
52 first: ?*Node,
52 first: ?*Node = null,
5353
5454 /// Initialize a linked list.
5555 ///
lib/std/mem.zig+24-10
......@@ -279,6 +279,21 @@ pub const Allocator = struct {
279279 const shrink_result = self.shrinkFn(self, non_const_ptr[0..bytes_len], Slice.alignment, 0, 1);
280280 assert(shrink_result.len == 0);
281281 }
282
283 /// Copies `m` to newly allocated memory. Caller owns the memory.
284 pub fn dupe(allocator: *Allocator, comptime T: type, m: []const T) ![]T {
285 const new_buf = try allocator.alloc(T, m.len);
286 copy(T, new_buf, m);
287 return new_buf;
288 }
289
290 /// Copies `m` to newly allocated memory, with a null-terminated element. Caller owns the memory.
291 pub fn dupeZ(allocator: *Allocator, comptime T: type, m: []const T) ![:0]T {
292 const new_buf = try allocator.alloc(T, m.len + 1);
293 copy(T, new_buf, m);
294 new_buf[m.len] = 0;
295 return new_buf[0..m.len :0];
296 }
282297};
283298
284299var failAllocator = Allocator {
......@@ -785,19 +800,14 @@ pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool {
785800 return true;
786801}
787802
788/// Copies `m` to newly allocated memory. Caller owns the memory.
803/// Deprecated, use `Allocator.dupe`.
789804pub fn dupe(allocator: *Allocator, comptime T: type, m: []const T) ![]T {
790 const new_buf = try allocator.alloc(T, m.len);
791 copy(T, new_buf, m);
792 return new_buf;
805 return allocator.dupe(T, m);
793806}
794807
795/// Copies `m` to newly allocated memory, with a null-terminated element. Caller owns the memory.
808/// Deprecated, use `Allocator.dupeZ`.
796809pub fn dupeZ(allocator: *Allocator, comptime T: type, m: []const T) ![:0]T {
797 const new_buf = try allocator.alloc(T, m.len + 1);
798 copy(T, new_buf, m);
799 new_buf[m.len] = 0;
800 return new_buf[0..m.len :0];
810 return allocator.dupeZ(T, m);
801811}
802812
803813/// Remove values from the beginning of a slice.
......@@ -2112,7 +2122,11 @@ pub fn alignBackwardGeneric(comptime T: type, addr: T, alignment: T) T {
21122122/// Given an address and an alignment, return true if the address is a multiple of the alignment
21132123/// The alignment must be a power of 2 and greater than 0.
21142124pub fn isAligned(addr: usize, alignment: usize) bool {
2115 return alignBackward(addr, alignment) == addr;
2125 return isAlignedGeneric(u64, addr, alignment);
2126}
2127
2128pub fn isAlignedGeneric(comptime T: type, addr: T, alignment: T) bool {
2129 return alignBackwardGeneric(T, addr, alignment) == addr;
21162130}
21172131
21182132test "isAligned" {
lib/std/std.zig+3-1
......@@ -1,6 +1,8 @@
1pub const AlignedArrayList = @import("array_list.zig").AlignedArrayList;
21pub const ArrayList = @import("array_list.zig").ArrayList;
2pub const ArrayListAligned = @import("array_list.zig").ArrayListAligned;
3pub const ArrayListAlignedUnmanaged = @import("array_list.zig").ArrayListAlignedUnmanaged;
34pub const ArrayListSentineled = @import("array_list_sentineled.zig").ArrayListSentineled;
5pub const ArrayListUnmanaged = @import("array_list.zig").ArrayListUnmanaged;
46pub const AutoHashMap = @import("hash_map.zig").AutoHashMap;
57pub const BloomFilter = @import("bloom_filter.zig").BloomFilter;
68pub const BufMap = @import("buf_map.zig").BufMap;
src-self-hosted/Module.zig created+2091
......@@ -0,0 +1,2091 @@
1const std = @import("std");
2const mem = std.mem;
3const Allocator = std.mem.Allocator;
4const ArrayListUnmanaged = std.ArrayListUnmanaged;
5const Value = @import("value.zig").Value;
6const Type = @import("type.zig").Type;
7const TypedValue = @import("TypedValue.zig");
8const assert = std.debug.assert;
9const BigIntConst = std.math.big.int.Const;
10const BigIntMutable = std.math.big.int.Mutable;
11const Target = std.Target;
12const Package = @import("Package.zig");
13const link = @import("link.zig");
14const ir = @import("ir.zig");
15const zir = @import("zir.zig");
16const Module = @This();
17const Inst = ir.Inst;
18
19/// General-purpose allocator.
20allocator: *Allocator,
21/// Pointer to externally managed resource.
22root_pkg: *Package,
23/// Module owns this resource.
24root_scope: *Scope.ZIRModule,
25bin_file: link.ElfFile,
26bin_file_dir: std.fs.Dir,
27bin_file_path: []const u8,
28/// It's rare for a decl to be exported, so we save memory by having a sparse map of
29/// Decl pointers to details about them being exported.
30/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
31decl_exports: std.AutoHashMap(*Decl, []*Export),
32/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
33/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
34/// is performing the export of another Decl.
35/// This table owns the Export memory.
36export_owners: std.AutoHashMap(*Decl, []*Export),
37/// Maps fully qualified namespaced names to the Decl struct for them.
38decl_table: std.AutoHashMap(Decl.Hash, *Decl),
39
40optimize_mode: std.builtin.Mode,
41link_error_flags: link.ElfFile.ErrorFlags = link.ElfFile.ErrorFlags{},
42
43work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
44
45/// We optimize memory usage for a compilation with no compile errors by storing the
46/// error messages and mapping outside of `Decl`.
47/// The ErrorMsg memory is owned by the decl, using Module's allocator.
48/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
49/// a Decl can have a failed_decls entry but have analysis status of success.
50failed_decls: std.AutoHashMap(*Decl, *ErrorMsg),
51/// Using a map here for consistency with the other fields here.
52/// The ErrorMsg memory is owned by the `Scope.ZIRModule`, using Module's allocator.
53failed_files: std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg),
54/// Using a map here for consistency with the other fields here.
55/// The ErrorMsg memory is owned by the `Export`, using Module's allocator.
56failed_exports: std.AutoHashMap(*Export, *ErrorMsg),
57
58pub const WorkItem = union(enum) {
59 /// Write the machine code for a Decl to the output file.
60 codegen_decl: *Decl,
61};
62
63pub const Export = struct {
64 options: std.builtin.ExportOptions,
65 /// Byte offset into the file that contains the export directive.
66 src: usize,
67 /// Represents the position of the export, if any, in the output file.
68 link: link.ElfFile.Export,
69 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
70 owner_decl: *Decl,
71 status: enum {
72 in_progress,
73 failed,
74 /// Indicates that the failure was due to a temporary issue, such as an I/O error
75 /// when writing to the output file. Retrying the export may succeed.
76 failed_retryable,
77 complete,
78 },
79};
80
81pub const Decl = struct {
82 /// This name is relative to the containing namespace of the decl. It uses a null-termination
83 /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
84 /// in symbol names, because executable file formats use null-terminated strings for symbol names.
85 /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for
86 /// mapping them to an address in the output file.
87 /// Memory owned by this decl, using Module's allocator.
88 name: [*:0]const u8,
89 /// The direct parent container of the Decl. This field will need to get more fleshed out when
90 /// self-hosted supports proper struct types and Zig AST => ZIR.
91 /// Reference to externally owned memory.
92 scope: *Scope.ZIRModule,
93 /// Byte offset into the source file that contains this declaration.
94 /// This is the base offset that src offsets within this Decl are relative to.
95 src: usize,
96 /// The most recent value of the Decl after a successful semantic analysis.
97 /// The tag for this union is determined by the tag value of the analysis field.
98 typed_value: union {
99 never_succeeded: void,
100 most_recent: TypedValue.Managed,
101 },
102 /// Represents the "shallow" analysis status. For example, for decls that are functions,
103 /// the function type is analyzed with this set to `in_progress`, however, the semantic
104 /// analysis of the function body is performed with this value set to `success`. Functions
105 /// have their own analysis status field.
106 analysis: enum {
107 initial_in_progress,
108 /// This Decl might be OK but it depends on another one which did not successfully complete
109 /// semantic analysis. This Decl never had a value computed.
110 initial_dependency_failure,
111 /// Semantic analysis failure. This Decl never had a value computed.
112 /// There will be a corresponding ErrorMsg in Module.failed_decls.
113 initial_sema_failure,
114 /// In this case the `typed_value.most_recent` can still be accessed.
115 /// There will be a corresponding ErrorMsg in Module.failed_decls.
116 codegen_failure,
117 /// In this case the `typed_value.most_recent` can still be accessed.
118 /// There will be a corresponding ErrorMsg in Module.failed_decls.
119 /// This indicates the failure was something like running out of disk space,
120 /// and attempting codegen again may succeed.
121 codegen_failure_retryable,
122 /// This Decl might be OK but it depends on another one which did not successfully complete
123 /// semantic analysis. There is a most recent value available.
124 repeat_dependency_failure,
125 /// Semantic anlaysis failure, but the `typed_value.most_recent` can be accessed.
126 /// There will be a corresponding ErrorMsg in Module.failed_decls.
127 repeat_sema_failure,
128 /// Completed successfully before; the `typed_value.most_recent` can be accessed, and
129 /// new semantic analysis is in progress.
130 repeat_in_progress,
131 /// Everything is done and updated.
132 complete,
133 },
134
135 /// Represents the position of the code in the output file.
136 /// This is populated regardless of semantic analysis and code generation.
137 link: link.ElfFile.Decl = link.ElfFile.Decl.empty,
138
139 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
140 /// typed_value is modified.
141 /// TODO look into using a lightweight map/set data structure rather than a linear array.
142 dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},
143
144 contents_hash: Hash,
145
146 pub fn destroy(self: *Decl, allocator: *Allocator) void {
147 allocator.free(mem.spanZ(self.name));
148 if (self.typedValueManaged()) |tvm| {
149 tvm.deinit(allocator);
150 }
151 allocator.destroy(self);
152 }
153
154 pub const Hash = [16]u8;
155
156 /// If the name is small enough, it is used directly as the hash.
157 /// If it is long, blake3 hash is computed.
158 pub fn hashSimpleName(name: []const u8) Hash {
159 var out: Hash = undefined;
160 if (name.len <= Hash.len) {
161 mem.copy(u8, &out, name);
162 mem.set(u8, out[name.len..], 0);
163 } else {
164 std.crypto.Blake3.hash(name, &out);
165 }
166 return out;
167 }
168
169 /// Must generate unique bytes with no collisions with other decls.
170 /// The point of hashing here is only to limit the number of bytes of
171 /// the unique identifier to a fixed size (16 bytes).
172 pub fn fullyQualifiedNameHash(self: Decl) Hash {
173 // Right now we only have ZIRModule as the source. So this is simply the
174 // relative name of the decl.
175 return hashSimpleName(mem.spanZ(u8, self.name));
176 }
177
178 pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
179 const tvm = self.typedValueManaged() orelse return error.AnalysisFail;
180 return tvm.typed_value;
181 }
182
183 pub fn value(self: *Decl) error{AnalysisFail}!Value {
184 return (try self.typedValue()).val;
185 }
186
187 pub fn dump(self: *Decl) void {
188 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
189 std.debug.warn("{}:{}:{} name={} status={}", .{
190 self.scope.sub_file_path,
191 loc.line + 1,
192 loc.column + 1,
193 mem.spanZ(self.name),
194 @tagName(self.analysis),
195 });
196 if (self.typedValueManaged()) |tvm| {
197 std.debug.warn(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
198 }
199 std.debug.warn("\n", .{});
200 }
201
202 fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
203 switch (self.analysis) {
204 .initial_in_progress,
205 .initial_dependency_failure,
206 .initial_sema_failure,
207 => return null,
208 .codegen_failure,
209 .codegen_failure_retryable,
210 .repeat_dependency_failure,
211 .repeat_sema_failure,
212 .repeat_in_progress,
213 .complete,
214 => return &self.typed_value.most_recent,
215 }
216 }
217};
218
219/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
220pub const Fn = struct {
221 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
222 fn_type: Type,
223 analysis: union(enum) {
224 /// The value is the source instruction.
225 queued: *zir.Inst.Fn,
226 in_progress: *Analysis,
227 /// There will be a corresponding ErrorMsg in Module.failed_decls
228 sema_failure,
229 /// This Fn might be OK but it depends on another Decl which did not successfully complete
230 /// semantic analysis.
231 dependency_failure,
232 success: Body,
233 },
234
235 /// This memory is temporary and points to stack memory for the duration
236 /// of Fn analysis.
237 pub const Analysis = struct {
238 inner_block: Scope.Block,
239 /// TODO Performance optimization idea: instead of this inst_table,
240 /// use a field in the zir.Inst instead to track corresponding instructions
241 inst_table: std.AutoHashMap(*zir.Inst, *Inst),
242 needed_inst_capacity: usize,
243 };
244};
245
246pub const Scope = struct {
247 tag: Tag,
248
249 pub fn cast(base: *Scope, comptime T: type) ?*T {
250 if (base.tag != T.base_tag)
251 return null;
252
253 return @fieldParentPtr(T, "base", base);
254 }
255
256 /// Asserts the scope has a parent which is a DeclAnalysis and
257 /// returns the arena Allocator.
258 pub fn arena(self: *Scope) *Allocator {
259 switch (self.tag) {
260 .block => return self.cast(Block).?.arena,
261 .decl => return &self.cast(DeclAnalysis).?.arena.allocator,
262 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
263 }
264 }
265
266 /// Asserts the scope has a parent which is a DeclAnalysis and
267 /// returns the Decl.
268 pub fn decl(self: *Scope) *Decl {
269 switch (self.tag) {
270 .block => return self.cast(Block).?.decl,
271 .decl => return self.cast(DeclAnalysis).?.decl,
272 .zir_module => unreachable,
273 }
274 }
275
276 /// Asserts the scope has a parent which is a ZIRModule and
277 /// returns it.
278 pub fn namespace(self: *Scope) *ZIRModule {
279 switch (self.tag) {
280 .block => return self.cast(Block).?.decl.scope,
281 .decl => return self.cast(DeclAnalysis).?.decl.scope,
282 .zir_module => return self.cast(ZIRModule).?,
283 }
284 }
285
286 pub fn dumpInst(self: *Scope, inst: *Inst) void {
287 const zir_module = self.namespace();
288 const loc = std.zig.findLineColumn(zir_module.source.bytes, inst.src);
289 std.debug.warn("{}:{}:{}: {}: ty={}\n", .{
290 zir_module.sub_file_path,
291 loc.line + 1,
292 loc.column + 1,
293 @tagName(inst.tag),
294 inst.ty,
295 });
296 }
297
298 pub const Tag = enum {
299 zir_module,
300 block,
301 decl,
302 };
303
304 pub const ZIRModule = struct {
305 pub const base_tag: Tag = .zir_module;
306 base: Scope = Scope{ .tag = base_tag },
307 /// Relative to the owning package's root_src_dir.
308 /// Reference to external memory, not owned by ZIRModule.
309 sub_file_path: []const u8,
310 source: union(enum) {
311 unloaded: void,
312 bytes: [:0]const u8,
313 },
314 contents: union {
315 not_available: void,
316 module: *zir.Module,
317 },
318 status: enum {
319 never_loaded,
320 unloaded_success,
321 unloaded_parse_failure,
322 unloaded_sema_failure,
323
324 loaded_sema_failure,
325 loaded_success,
326 },
327
328 pub fn unload(self: *ZIRModule, allocator: *Allocator) void {
329 switch (self.status) {
330 .never_loaded,
331 .unloaded_parse_failure,
332 .unloaded_sema_failure,
333 .unloaded_success,
334 => {},
335
336 .loaded_success => {
337 self.contents.module.deinit(allocator);
338 allocator.destroy(self.contents.module);
339 self.status = .unloaded_success;
340 },
341 .loaded_sema_failure => {
342 self.contents.module.deinit(allocator);
343 allocator.destroy(self.contents.module);
344 self.status = .unloaded_sema_failure;
345 },
346 }
347 switch (self.source) {
348 .bytes => |bytes| {
349 allocator.free(bytes);
350 self.source = .{ .unloaded = {} };
351 },
352 .unloaded => {},
353 }
354 }
355
356 pub fn deinit(self: *ZIRModule, allocator: *Allocator) void {
357 self.unload(allocator);
358 self.* = undefined;
359 }
360
361 pub fn dumpSrc(self: *ZIRModule, src: usize) void {
362 const loc = std.zig.findLineColumn(self.source.bytes, src);
363 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
364 }
365 };
366
367 /// This is a temporary structure, references to it are valid only
368 /// during semantic analysis of the block.
369 pub const Block = struct {
370 pub const base_tag: Tag = .block;
371 base: Scope = Scope{ .tag = base_tag },
372 func: *Fn,
373 decl: *Decl,
374 instructions: ArrayListUnmanaged(*Inst),
375 /// Points to the arena allocator of DeclAnalysis
376 arena: *Allocator,
377 };
378
379 /// This is a temporary structure, references to it are valid only
380 /// during semantic analysis of the decl.
381 pub const DeclAnalysis = struct {
382 pub const base_tag: Tag = .decl;
383 base: Scope = Scope{ .tag = base_tag },
384 decl: *Decl,
385 arena: std.heap.ArenaAllocator,
386 };
387};
388
389pub const Body = struct {
390 instructions: []*Inst,
391};
392
393pub const AllErrors = struct {
394 arena: std.heap.ArenaAllocator.State,
395 list: []const Message,
396
397 pub const Message = struct {
398 src_path: []const u8,
399 line: usize,
400 column: usize,
401 byte_offset: usize,
402 msg: []const u8,
403 };
404
405 pub fn deinit(self: *AllErrors, allocator: *Allocator) void {
406 self.arena.promote(allocator).deinit();
407 }
408
409 fn add(
410 arena: *std.heap.ArenaAllocator,
411 errors: *std.ArrayList(Message),
412 sub_file_path: []const u8,
413 source: []const u8,
414 simple_err_msg: ErrorMsg,
415 ) !void {
416 const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset);
417 try errors.append(.{
418 .src_path = try arena.allocator.dupe(u8, sub_file_path),
419 .msg = try arena.allocator.dupe(u8, simple_err_msg.msg),
420 .byte_offset = simple_err_msg.byte_offset,
421 .line = loc.line,
422 .column = loc.column,
423 });
424 }
425};
426
427pub const InitOptions = struct {
428 target: std.Target,
429 root_pkg: *Package,
430 output_mode: std.builtin.OutputMode,
431 bin_file_dir: ?std.fs.Dir = null,
432 bin_file_path: []const u8,
433 link_mode: ?std.builtin.LinkMode = null,
434 object_format: ?std.builtin.ObjectFormat = null,
435 optimize_mode: std.builtin.Mode = .Debug,
436};
437
438pub fn init(gpa: *Allocator, options: InitOptions) !Module {
439 const root_scope = try gpa.create(Scope.ZIRModule);
440 errdefer gpa.destroy(root_scope);
441
442 root_scope.* = .{
443 .sub_file_path = options.root_pkg.root_src_path,
444 .source = .{ .unloaded = {} },
445 .contents = .{ .not_available = {} },
446 .status = .never_loaded,
447 };
448
449 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
450 var bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
451 .target = options.target,
452 .output_mode = options.output_mode,
453 .link_mode = options.link_mode orelse .Static,
454 .object_format = options.object_format orelse options.target.getObjectFormat(),
455 });
456 errdefer bin_file.deinit();
457
458 return Module{
459 .allocator = gpa,
460 .root_pkg = options.root_pkg,
461 .root_scope = root_scope,
462 .bin_file_dir = bin_file_dir,
463 .bin_file_path = options.bin_file_path,
464 .bin_file = bin_file,
465 .optimize_mode = options.optimize_mode,
466 .decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(gpa),
467 .decl_exports = std.AutoHashMap(*Decl, []*Export).init(gpa),
468 .export_owners = std.AutoHashMap(*Decl, []*Export).init(gpa),
469 .failed_decls = std.AutoHashMap(*Decl, *ErrorMsg).init(gpa),
470 .failed_files = std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg).init(gpa),
471 .failed_exports = std.AutoHashMap(*Export, *ErrorMsg).init(gpa),
472 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
473 };
474}
475
476pub fn deinit(self: *Module) void {
477 self.bin_file.deinit();
478 const allocator = self.allocator;
479 self.work_queue.deinit();
480 {
481 var it = self.decl_table.iterator();
482 while (it.next()) |kv| {
483 kv.value.destroy(allocator);
484 }
485 self.decl_table.deinit();
486 }
487 {
488 var it = self.failed_decls.iterator();
489 while (it.next()) |kv| {
490 kv.value.destroy(allocator);
491 }
492 self.failed_decls.deinit();
493 }
494 {
495 var it = self.failed_files.iterator();
496 while (it.next()) |kv| {
497 kv.value.destroy(allocator);
498 }
499 self.failed_files.deinit();
500 }
501 {
502 var it = self.failed_exports.iterator();
503 while (it.next()) |kv| {
504 kv.value.destroy(allocator);
505 }
506 self.failed_exports.deinit();
507 }
508 {
509 var it = self.decl_exports.iterator();
510 while (it.next()) |kv| {
511 const export_list = kv.value;
512 allocator.free(export_list);
513 }
514 self.decl_exports.deinit();
515 }
516 {
517 var it = self.export_owners.iterator();
518 while (it.next()) |kv| {
519 const export_list = kv.value;
520 for (export_list) |exp| {
521 allocator.destroy(exp);
522 }
523 allocator.free(export_list);
524 }
525 self.export_owners.deinit();
526 }
527 {
528 self.root_scope.deinit(allocator);
529 allocator.destroy(self.root_scope);
530 }
531 self.* = undefined;
532}
533
534pub fn target(self: Module) std.Target {
535 return self.bin_file.options.target;
536}
537
538/// Detect changes to source files, perform semantic analysis, and update the output files.
539pub fn update(self: *Module) !void {
540 // TODO Use the cache hash file system to detect which source files changed.
541 // Here we simulate a full cache miss.
542 // Analyze the root source file now.
543 self.analyzeRoot(self.root_scope) catch |err| switch (err) {
544 error.AnalysisFail => {
545 assert(self.totalErrorCount() != 0);
546 },
547 else => |e| return e,
548 };
549
550 try self.performAllTheWork();
551
552 // Unload all the source files from memory.
553 self.root_scope.unload(self.allocator);
554
555 try self.bin_file.flush();
556 self.link_error_flags = self.bin_file.error_flags;
557}
558
559/// Having the file open for writing is problematic as far as executing the
560/// binary is concerned. This will remove the write flag, or close the file,
561/// or whatever is needed so that it can be executed.
562/// After this, one must call` makeFileWritable` before calling `update`.
563pub fn makeBinFileExecutable(self: *Module) !void {
564 return self.bin_file.makeExecutable();
565}
566
567pub fn makeBinFileWritable(self: *Module) !void {
568 return self.bin_file.makeWritable(self.bin_file_dir, self.bin_file_path);
569}
570
571pub fn totalErrorCount(self: *Module) usize {
572 return self.failed_decls.size +
573 self.failed_files.size +
574 self.failed_exports.size +
575 @boolToInt(self.link_error_flags.no_entry_point_found);
576}
577
578pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
579 var arena = std.heap.ArenaAllocator.init(self.allocator);
580 errdefer arena.deinit();
581
582 var errors = std.ArrayList(AllErrors.Message).init(self.allocator);
583 defer errors.deinit();
584
585 {
586 var it = self.failed_files.iterator();
587 while (it.next()) |kv| {
588 const scope = kv.key;
589 const err_msg = kv.value;
590 const source = try self.getSource(scope);
591 try AllErrors.add(&arena, &errors, scope.sub_file_path, source, err_msg.*);
592 }
593 }
594 {
595 var it = self.failed_decls.iterator();
596 while (it.next()) |kv| {
597 const decl = kv.key;
598 const err_msg = kv.value;
599 const source = try self.getSource(decl.scope);
600 try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*);
601 }
602 }
603 {
604 var it = self.failed_exports.iterator();
605 while (it.next()) |kv| {
606 const decl = kv.key.owner_decl;
607 const err_msg = kv.value;
608 const source = try self.getSource(decl.scope);
609 try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*);
610 }
611 }
612
613 if (self.link_error_flags.no_entry_point_found) {
614 try errors.append(.{
615 .src_path = self.root_pkg.root_src_path,
616 .line = 0,
617 .column = 0,
618 .byte_offset = 0,
619 .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}),
620 });
621 }
622
623 assert(errors.items.len == self.totalErrorCount());
624
625 return AllErrors{
626 .arena = arena.state,
627 .list = try arena.allocator.dupe(AllErrors.Message, errors.items),
628 };
629}
630
631const InnerError = error{ OutOfMemory, AnalysisFail };
632
633pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
634 while (self.work_queue.readItem()) |work_item| switch (work_item) {
635 .codegen_decl => |decl| switch (decl.analysis) {
636 .initial_in_progress,
637 .repeat_in_progress,
638 => unreachable,
639
640 .initial_sema_failure,
641 .repeat_sema_failure,
642 .codegen_failure,
643 .initial_dependency_failure,
644 .repeat_dependency_failure,
645 => continue,
646
647 .complete, .codegen_failure_retryable => {
648 if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| {
649 switch (payload.func.analysis) {
650 .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
651 error.AnalysisFail => {
652 if (payload.func.analysis == .queued) {
653 payload.func.analysis = .dependency_failure;
654 }
655 continue;
656 },
657 else => |e| return e,
658 },
659 .in_progress => unreachable,
660 .sema_failure, .dependency_failure => continue,
661 .success => {},
662 }
663 }
664
665 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
666
667 self.bin_file.updateDecl(self, decl) catch |err| switch (err) {
668 error.OutOfMemory => return error.OutOfMemory,
669 error.AnalysisFail => {
670 decl.analysis = .repeat_dependency_failure;
671 },
672 else => {
673 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
674 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
675 self.allocator,
676 decl.src,
677 "unable to codegen: {}",
678 .{@errorName(err)},
679 ));
680 decl.analysis = .codegen_failure_retryable;
681 },
682 };
683 },
684 },
685 };
686}
687
688fn getSource(self: *Module, root_scope: *Scope.ZIRModule) ![:0]const u8 {
689 switch (root_scope.source) {
690 .unloaded => {
691 const source = try self.root_pkg.root_src_dir.readFileAllocOptions(
692 self.allocator,
693 root_scope.sub_file_path,
694 std.math.maxInt(u32),
695 1,
696 0,
697 );
698 root_scope.source = .{ .bytes = source };
699 return source;
700 },
701 .bytes => |bytes| return bytes,
702 }
703}
704
705fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
706 switch (root_scope.status) {
707 .never_loaded, .unloaded_success => {
708 try self.failed_files.ensureCapacity(self.failed_files.size + 1);
709
710 const source = try self.getSource(root_scope);
711
712 var keep_zir_module = false;
713 const zir_module = try self.allocator.create(zir.Module);
714 defer if (!keep_zir_module) self.allocator.destroy(zir_module);
715
716 zir_module.* = try zir.parse(self.allocator, source);
717 defer if (!keep_zir_module) zir_module.deinit(self.allocator);
718
719 if (zir_module.error_msg) |src_err_msg| {
720 self.failed_files.putAssumeCapacityNoClobber(
721 root_scope,
722 try ErrorMsg.create(self.allocator, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
723 );
724 root_scope.status = .unloaded_parse_failure;
725 return error.AnalysisFail;
726 }
727
728 root_scope.status = .loaded_success;
729 root_scope.contents = .{ .module = zir_module };
730 keep_zir_module = true;
731
732 return zir_module;
733 },
734
735 .unloaded_parse_failure,
736 .unloaded_sema_failure,
737 => return error.AnalysisFail,
738
739 .loaded_success, .loaded_sema_failure => return root_scope.contents.module,
740 }
741}
742
743fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
744 // TODO use the cache to identify, from the modified source files, the decls which have
745 // changed based on the span of memory that represents the decl in the re-parsed source file.
746 // Use the cached dependency graph to recursively determine the set of decls which need
747 // regeneration.
748 // Here we simulate adding a source file which was previously not part of the compilation,
749 // which means scanning the decls looking for exports.
750 // TODO also identify decls that need to be deleted.
751 switch (root_scope.status) {
752 .never_loaded => {
753 const src_module = try self.getSrcModule(root_scope);
754
755 // Here we ensure enough queue capacity to store all the decls, so that later we can use
756 // appendAssumeCapacity.
757 try self.work_queue.ensureUnusedCapacity(src_module.decls.len);
758
759 for (src_module.decls) |decl| {
760 if (decl.cast(zir.Inst.Export)) |export_inst| {
761 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty);
762 }
763 }
764 },
765
766 .unloaded_parse_failure,
767 .unloaded_sema_failure,
768 .unloaded_success,
769 .loaded_sema_failure,
770 .loaded_success,
771 => {
772 const src_module = try self.getSrcModule(root_scope);
773
774 // Look for changed decls.
775 for (src_module.decls) |src_decl| {
776 const name_hash = Decl.hashSimpleName(src_decl.name);
777 if (self.decl_table.get(name_hash)) |kv| {
778 const decl = kv.value;
779 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);
780 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {
781 // TODO recursive dependency management
782 //std.debug.warn("noticed that '{}' changed\n", .{src_decl.name});
783 self.decl_table.removeAssertDiscard(name_hash);
784 const saved_link = decl.link;
785 decl.destroy(self.allocator);
786 if (self.export_owners.getValue(decl)) |exports| {
787 @panic("TODO handle updating a decl that does an export");
788 }
789 const new_decl = self.resolveDecl(
790 &root_scope.base,
791 src_decl,
792 saved_link,
793 ) catch |err| switch (err) {
794 error.OutOfMemory => return error.OutOfMemory,
795 error.AnalysisFail => continue,
796 };
797 if (self.decl_exports.remove(decl)) |entry| {
798 self.decl_exports.putAssumeCapacityNoClobber(new_decl, entry.value);
799 }
800 }
801 } else if (src_decl.cast(zir.Inst.Export)) |export_inst| {
802 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty);
803 }
804 }
805 },
806 }
807}
808
809fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
810 // Use the Decl's arena for function memory.
811 var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator);
812 defer decl.typed_value.most_recent.arena.?.* = arena.state;
813 var analysis: Fn.Analysis = .{
814 .inner_block = .{
815 .func = func,
816 .decl = decl,
817 .instructions = .{},
818 .arena = &arena.allocator,
819 },
820 .needed_inst_capacity = 0,
821 .inst_table = std.AutoHashMap(*zir.Inst, *Inst).init(self.allocator),
822 };
823 defer analysis.inner_block.instructions.deinit(self.allocator);
824 defer analysis.inst_table.deinit();
825
826 const fn_inst = func.analysis.queued;
827 func.analysis = .{ .in_progress = &analysis };
828
829 try self.analyzeBody(&analysis.inner_block.base, fn_inst.positionals.body);
830
831 func.analysis = .{
832 .success = .{
833 .instructions = try arena.allocator.dupe(*Inst, analysis.inner_block.instructions.items),
834 },
835 };
836}
837
838fn resolveDecl(
839 self: *Module,
840 scope: *Scope,
841 old_inst: *zir.Inst,
842 bin_file_link: link.ElfFile.Decl,
843) InnerError!*Decl {
844 const hash = Decl.hashSimpleName(old_inst.name);
845 if (self.decl_table.get(hash)) |kv| {
846 return kv.value;
847 } else {
848 const new_decl = blk: {
849 try self.decl_table.ensureCapacity(self.decl_table.size + 1);
850 const new_decl = try self.allocator.create(Decl);
851 errdefer self.allocator.destroy(new_decl);
852 const name = try mem.dupeZ(self.allocator, u8, old_inst.name);
853 errdefer self.allocator.free(name);
854 new_decl.* = .{
855 .name = name,
856 .scope = scope.namespace(),
857 .src = old_inst.src,
858 .typed_value = .{ .never_succeeded = {} },
859 .analysis = .initial_in_progress,
860 .contents_hash = Decl.hashSimpleName(old_inst.contents),
861 .link = bin_file_link,
862 };
863 self.decl_table.putAssumeCapacityNoClobber(hash, new_decl);
864 break :blk new_decl;
865 };
866
867 var decl_scope: Scope.DeclAnalysis = .{
868 .decl = new_decl,
869 .arena = std.heap.ArenaAllocator.init(self.allocator),
870 };
871 errdefer decl_scope.arena.deinit();
872
873 const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) {
874 error.OutOfMemory => return error.OutOfMemory,
875 error.AnalysisFail => {
876 switch (new_decl.analysis) {
877 .initial_in_progress => new_decl.analysis = .initial_dependency_failure,
878 .repeat_in_progress => new_decl.analysis = .repeat_dependency_failure,
879 else => {},
880 }
881 return error.AnalysisFail;
882 },
883 };
884 const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
885
886 const has_codegen_bits = typed_value.ty.hasCodeGenBits();
887 if (has_codegen_bits) {
888 // We don't fully codegen the decl until later, but we do need to reserve a global
889 // offset table index for it. This allows us to codegen decls out of dependency order,
890 // increasing how many computations can be done in parallel.
891 try self.bin_file.allocateDeclIndexes(new_decl);
892 }
893
894 arena_state.* = decl_scope.arena.state;
895
896 new_decl.typed_value = .{
897 .most_recent = .{
898 .typed_value = typed_value,
899 .arena = arena_state,
900 },
901 };
902 new_decl.analysis = .complete;
903 if (has_codegen_bits) {
904 // We ensureCapacity when scanning for decls.
905 self.work_queue.writeItemAssumeCapacity(.{ .codegen_decl = new_decl });
906 }
907 return new_decl;
908 }
909}
910
911fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {
912 const decl = try self.resolveDecl(scope, old_inst, link.ElfFile.Decl.empty);
913 switch (decl.analysis) {
914 .initial_in_progress => unreachable,
915 .repeat_in_progress => unreachable,
916 .initial_dependency_failure,
917 .repeat_dependency_failure,
918 .initial_sema_failure,
919 .repeat_sema_failure,
920 .codegen_failure,
921 .codegen_failure_retryable,
922 => return error.AnalysisFail,
923
924 .complete => return decl,
925 }
926}
927
928fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
929 if (scope.cast(Scope.Block)) |block| {
930 if (block.func.analysis.in_progress.inst_table.get(old_inst)) |kv| {
931 return kv.value;
932 }
933 }
934
935 const decl = try self.resolveCompleteDecl(scope, old_inst);
936 const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);
937 return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
938}
939
940fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
941 return scope.cast(Scope.Block) orelse
942 return self.fail(scope, src, "instruction illegal outside function body", .{});
943}
944
945fn resolveInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
946 const new_inst = try self.resolveInst(scope, old_inst);
947 const val = try self.resolveConstValue(scope, new_inst);
948 return TypedValue{
949 .ty = new_inst.ty,
950 .val = val,
951 };
952}
953
954fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
955 return (try self.resolveDefinedValue(scope, base)) orelse
956 return self.fail(scope, base.src, "unable to resolve comptime value", .{});
957}
958
959fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
960 if (base.value()) |val| {
961 if (val.isUndef()) {
962 return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});
963 }
964 return val;
965 }
966 return null;
967}
968
969fn resolveConstString(self: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 {
970 const new_inst = try self.resolveInst(scope, old_inst);
971 const wanted_type = Type.initTag(.const_slice_u8);
972 const coerced_inst = try self.coerce(scope, wanted_type, new_inst);
973 const val = try self.resolveConstValue(scope, coerced_inst);
974 return val.toAllocatedBytes(scope.arena());
975}
976
977fn resolveType(self: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
978 const new_inst = try self.resolveInst(scope, old_inst);
979 const wanted_type = Type.initTag(.@"type");
980 const coerced_inst = try self.coerce(scope, wanted_type, new_inst);
981 const val = try self.resolveConstValue(scope, coerced_inst);
982 return val.toType();
983}
984
985fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!void {
986 try self.decl_exports.ensureCapacity(self.decl_exports.size + 1);
987 try self.export_owners.ensureCapacity(self.export_owners.size + 1);
988 const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);
989 const exported_decl = try self.resolveCompleteDecl(scope, export_inst.positionals.value);
990 const typed_value = exported_decl.typed_value.most_recent.typed_value;
991 switch (typed_value.ty.zigTypeTag()) {
992 .Fn => {},
993 else => return self.fail(
994 scope,
995 export_inst.positionals.value.src,
996 "unable to export type '{}'",
997 .{typed_value.ty},
998 ),
999 }
1000 const new_export = try self.allocator.create(Export);
1001 errdefer self.allocator.destroy(new_export);
1002
1003 const owner_decl = scope.decl();
1004
1005 new_export.* = .{
1006 .options = .{ .name = symbol_name },
1007 .src = export_inst.base.src,
1008 .link = .{},
1009 .owner_decl = owner_decl,
1010 .status = .in_progress,
1011 };
1012
1013 // Add to export_owners table.
1014 const eo_gop = self.export_owners.getOrPut(owner_decl) catch unreachable;
1015 if (!eo_gop.found_existing) {
1016 eo_gop.kv.value = &[0]*Export{};
1017 }
1018 eo_gop.kv.value = try self.allocator.realloc(eo_gop.kv.value, eo_gop.kv.value.len + 1);
1019 eo_gop.kv.value[eo_gop.kv.value.len - 1] = new_export;
1020 errdefer eo_gop.kv.value = self.allocator.shrink(eo_gop.kv.value, eo_gop.kv.value.len - 1);
1021
1022 // Add to exported_decl table.
1023 const de_gop = self.decl_exports.getOrPut(exported_decl) catch unreachable;
1024 if (!de_gop.found_existing) {
1025 de_gop.kv.value = &[0]*Export{};
1026 }
1027 de_gop.kv.value = try self.allocator.realloc(de_gop.kv.value, de_gop.kv.value.len + 1);
1028 de_gop.kv.value[de_gop.kv.value.len - 1] = new_export;
1029 errdefer de_gop.kv.value = self.allocator.shrink(de_gop.kv.value, de_gop.kv.value.len - 1);
1030
1031 self.bin_file.updateDeclExports(self, exported_decl, de_gop.kv.value) catch |err| switch (err) {
1032 error.OutOfMemory => return error.OutOfMemory,
1033 else => {
1034 try self.failed_exports.ensureCapacity(self.failed_exports.size + 1);
1035 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
1036 self.allocator,
1037 export_inst.base.src,
1038 "unable to export: {}",
1039 .{@errorName(err)},
1040 ));
1041 new_export.status = .failed_retryable;
1042 },
1043 };
1044}
1045
1046/// TODO should not need the cast on the last parameter at the callsites
1047fn addNewInstArgs(
1048 self: *Module,
1049 block: *Scope.Block,
1050 src: usize,
1051 ty: Type,
1052 comptime T: type,
1053 args: Inst.Args(T),
1054) !*Inst {
1055 const inst = try self.addNewInst(block, src, ty, T);
1056 inst.args = args;
1057 return &inst.base;
1058}
1059
1060fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T {
1061 const inst = try block.arena.create(T);
1062 inst.* = .{
1063 .base = .{
1064 .tag = T.base_tag,
1065 .ty = ty,
1066 .src = src,
1067 },
1068 .args = undefined,
1069 };
1070 try block.instructions.append(self.allocator, &inst.base);
1071 return inst;
1072}
1073
1074fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
1075 const const_inst = try scope.arena().create(Inst.Constant);
1076 const_inst.* = .{
1077 .base = .{
1078 .tag = Inst.Constant.base_tag,
1079 .ty = typed_value.ty,
1080 .src = src,
1081 },
1082 .val = typed_value.val,
1083 };
1084 return &const_inst.base;
1085}
1086
1087fn constStr(self: *Module, scope: *Scope, src: usize, str: []const u8) !*Inst {
1088 const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
1089 ty_payload.* = .{ .len = str.len };
1090
1091 const bytes_payload = try scope.arena().create(Value.Payload.Bytes);
1092 bytes_payload.* = .{ .data = str };
1093
1094 return self.constInst(scope, src, .{
1095 .ty = Type.initPayload(&ty_payload.base),
1096 .val = Value.initPayload(&bytes_payload.base),
1097 });
1098}
1099
1100fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
1101 return self.constInst(scope, src, .{
1102 .ty = Type.initTag(.type),
1103 .val = try ty.toValue(scope.arena()),
1104 });
1105}
1106
1107fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
1108 return self.constInst(scope, src, .{
1109 .ty = Type.initTag(.void),
1110 .val = Value.initTag(.the_one_possible_value),
1111 });
1112}
1113
1114fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
1115 return self.constInst(scope, src, .{
1116 .ty = ty,
1117 .val = Value.initTag(.undef),
1118 });
1119}
1120
1121fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
1122 return self.constInst(scope, src, .{
1123 .ty = Type.initTag(.bool),
1124 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
1125 });
1126}
1127
1128fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
1129 const int_payload = try scope.arena().create(Value.Payload.Int_u64);
1130 int_payload.* = .{ .int = int };
1131
1132 return self.constInst(scope, src, .{
1133 .ty = ty,
1134 .val = Value.initPayload(&int_payload.base),
1135 });
1136}
1137
1138fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
1139 const int_payload = try scope.arena().create(Value.Payload.Int_i64);
1140 int_payload.* = .{ .int = int };
1141
1142 return self.constInst(scope, src, .{
1143 .ty = ty,
1144 .val = Value.initPayload(&int_payload.base),
1145 });
1146}
1147
1148fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
1149 const val_payload = if (big_int.positive) blk: {
1150 if (big_int.to(u64)) |x| {
1151 return self.constIntUnsigned(scope, src, ty, x);
1152 } else |err| switch (err) {
1153 error.NegativeIntoUnsigned => unreachable,
1154 error.TargetTooSmall => {}, // handled below
1155 }
1156 const big_int_payload = try scope.arena().create(Value.Payload.IntBigPositive);
1157 big_int_payload.* = .{ .limbs = big_int.limbs };
1158 break :blk &big_int_payload.base;
1159 } else blk: {
1160 if (big_int.to(i64)) |x| {
1161 return self.constIntSigned(scope, src, ty, x);
1162 } else |err| switch (err) {
1163 error.NegativeIntoUnsigned => unreachable,
1164 error.TargetTooSmall => {}, // handled below
1165 }
1166 const big_int_payload = try scope.arena().create(Value.Payload.IntBigNegative);
1167 big_int_payload.* = .{ .limbs = big_int.limbs };
1168 break :blk &big_int_payload.base;
1169 };
1170
1171 return self.constInst(scope, src, .{
1172 .ty = ty,
1173 .val = Value.initPayload(val_payload),
1174 });
1175}
1176
1177fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
1178 const new_inst = try self.analyzeInst(scope, old_inst);
1179 return TypedValue{
1180 .ty = new_inst.ty,
1181 .val = try self.resolveConstValue(scope, new_inst),
1182 };
1183}
1184
1185fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
1186 switch (old_inst.tag) {
1187 .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?),
1188 .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?),
1189 .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?),
1190 .str => {
1191 const bytes = old_inst.cast(zir.Inst.Str).?.positionals.bytes;
1192 // The bytes references memory inside the ZIR module, which can get deallocated
1193 // after semantic analysis is complete. We need the memory to be in the Decl's arena.
1194 const arena_bytes = try scope.arena().dupe(u8, bytes);
1195 return self.constStr(scope, old_inst.src, arena_bytes);
1196 },
1197 .int => {
1198 const big_int = old_inst.cast(zir.Inst.Int).?.positionals.int;
1199 return self.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);
1200 },
1201 .ptrtoint => return self.analyzeInstPtrToInt(scope, old_inst.cast(zir.Inst.PtrToInt).?),
1202 .fieldptr => return self.analyzeInstFieldPtr(scope, old_inst.cast(zir.Inst.FieldPtr).?),
1203 .deref => return self.analyzeInstDeref(scope, old_inst.cast(zir.Inst.Deref).?),
1204 .as => return self.analyzeInstAs(scope, old_inst.cast(zir.Inst.As).?),
1205 .@"asm" => return self.analyzeInstAsm(scope, old_inst.cast(zir.Inst.Asm).?),
1206 .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.cast(zir.Inst.Unreachable).?),
1207 .@"return" => return self.analyzeInstRet(scope, old_inst.cast(zir.Inst.Return).?),
1208 .@"fn" => return self.analyzeInstFn(scope, old_inst.cast(zir.Inst.Fn).?),
1209 .@"export" => {
1210 try self.analyzeExport(scope, old_inst.cast(zir.Inst.Export).?);
1211 return self.constVoid(scope, old_inst.src);
1212 },
1213 .primitive => return self.analyzeInstPrimitive(scope, old_inst.cast(zir.Inst.Primitive).?),
1214 .ref => return self.analyzeInstRef(scope, old_inst.cast(zir.Inst.Ref).?),
1215 .fntype => return self.analyzeInstFnType(scope, old_inst.cast(zir.Inst.FnType).?),
1216 .intcast => return self.analyzeInstIntCast(scope, old_inst.cast(zir.Inst.IntCast).?),
1217 .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(zir.Inst.BitCast).?),
1218 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.cast(zir.Inst.ElemPtr).?),
1219 .add => return self.analyzeInstAdd(scope, old_inst.cast(zir.Inst.Add).?),
1220 .cmp => return self.analyzeInstCmp(scope, old_inst.cast(zir.Inst.Cmp).?),
1221 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?),
1222 .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(zir.Inst.IsNull).?),
1223 .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.cast(zir.Inst.IsNonNull).?),
1224 }
1225}
1226
1227fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {
1228 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1229 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});
1230}
1231
1232fn analyzeInstRef(self: *Module, scope: *Scope, inst: *zir.Inst.Ref) InnerError!*Inst {
1233 const decl = try self.resolveCompleteDecl(scope, inst.positionals.operand);
1234 return self.analyzeDeclRef(scope, inst.base.src, decl);
1235}
1236
1237fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
1238 const decl_name = try self.resolveConstString(scope, inst.positionals.name);
1239 // This will need to get more fleshed out when there are proper structs & namespaces.
1240 const zir_module = scope.namespace();
1241 for (zir_module.contents.module.decls) |src_decl| {
1242 if (mem.eql(u8, src_decl.name, decl_name)) {
1243 const decl = try self.resolveCompleteDecl(scope, src_decl);
1244 return self.analyzeDeclRef(scope, inst.base.src, decl);
1245 }
1246 }
1247 return self.fail(scope, inst.positionals.name.src, "use of undeclared identifier '{}'", .{decl_name});
1248}
1249
1250fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
1251 const decl_tv = try decl.typedValue();
1252 const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
1253 ty_payload.* = .{ .pointee_type = decl_tv.ty };
1254 const val_payload = try scope.arena().create(Value.Payload.DeclRef);
1255 val_payload.* = .{ .decl = decl };
1256 return self.constInst(scope, src, .{
1257 .ty = Type.initPayload(&ty_payload.base),
1258 .val = Value.initPayload(&val_payload.base),
1259 });
1260}
1261
1262fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
1263 const func = try self.resolveInst(scope, inst.positionals.func);
1264 if (func.ty.zigTypeTag() != .Fn)
1265 return self.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty});
1266
1267 const cc = func.ty.fnCallingConvention();
1268 if (cc == .Naked) {
1269 // TODO add error note: declared here
1270 return self.fail(
1271 scope,
1272 inst.positionals.func.src,
1273 "unable to call function with naked calling convention",
1274 .{},
1275 );
1276 }
1277 const call_params_len = inst.positionals.args.len;
1278 const fn_params_len = func.ty.fnParamLen();
1279 if (func.ty.fnIsVarArgs()) {
1280 if (call_params_len < fn_params_len) {
1281 // TODO add error note: declared here
1282 return self.fail(
1283 scope,
1284 inst.positionals.func.src,
1285 "expected at least {} arguments, found {}",
1286 .{ fn_params_len, call_params_len },
1287 );
1288 }
1289 return self.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{});
1290 } else if (fn_params_len != call_params_len) {
1291 // TODO add error note: declared here
1292 return self.fail(
1293 scope,
1294 inst.positionals.func.src,
1295 "expected {} arguments, found {}",
1296 .{ fn_params_len, call_params_len },
1297 );
1298 }
1299
1300 if (inst.kw_args.modifier == .compile_time) {
1301 return self.fail(scope, inst.base.src, "TODO implement comptime function calls", .{});
1302 }
1303 if (inst.kw_args.modifier != .auto) {
1304 return self.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier});
1305 }
1306
1307 // TODO handle function calls of generic functions
1308
1309 const fn_param_types = try self.allocator.alloc(Type, fn_params_len);
1310 defer self.allocator.free(fn_param_types);
1311 func.ty.fnParamTypes(fn_param_types);
1312
1313 const casted_args = try scope.arena().alloc(*Inst, fn_params_len);
1314 for (inst.positionals.args) |src_arg, i| {
1315 const uncasted_arg = try self.resolveInst(scope, src_arg);
1316 casted_args[i] = try self.coerce(scope, fn_param_types[i], uncasted_arg);
1317 }
1318
1319 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1320 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, Inst.Args(Inst.Call){
1321 .func = func,
1322 .args = casted_args,
1323 });
1324}
1325
1326fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
1327 const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type);
1328 const new_func = try scope.arena().create(Fn);
1329 new_func.* = .{
1330 .fn_type = fn_type,
1331 .analysis = .{ .queued = fn_inst },
1332 };
1333 const fn_payload = try scope.arena().create(Value.Payload.Function);
1334 fn_payload.* = .{ .func = new_func };
1335 return self.constInst(scope, fn_inst.base.src, .{
1336 .ty = fn_type,
1337 .val = Value.initPayload(&fn_payload.base),
1338 });
1339}
1340
1341fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
1342 const return_type = try self.resolveType(scope, fntype.positionals.return_type);
1343
1344 if (return_type.zigTypeTag() == .NoReturn and
1345 fntype.positionals.param_types.len == 0 and
1346 fntype.kw_args.cc == .Unspecified)
1347 {
1348 return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));
1349 }
1350
1351 if (return_type.zigTypeTag() == .NoReturn and
1352 fntype.positionals.param_types.len == 0 and
1353 fntype.kw_args.cc == .Naked)
1354 {
1355 return self.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
1356 }
1357
1358 if (return_type.zigTypeTag() == .Void and
1359 fntype.positionals.param_types.len == 0 and
1360 fntype.kw_args.cc == .C)
1361 {
1362 return self.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
1363 }
1364
1365 return self.fail(scope, fntype.base.src, "TODO implement fntype instruction more", .{});
1366}
1367
1368fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
1369 return self.constType(scope, primitive.base.src, primitive.positionals.tag.toType());
1370}
1371
1372fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Inst {
1373 const dest_type = try self.resolveType(scope, as.positionals.dest_type);
1374 const new_inst = try self.resolveInst(scope, as.positionals.value);
1375 return self.coerce(scope, dest_type, new_inst);
1376}
1377
1378fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToInt) InnerError!*Inst {
1379 const ptr = try self.resolveInst(scope, ptrtoint.positionals.ptr);
1380 if (ptr.ty.zigTypeTag() != .Pointer) {
1381 return self.fail(scope, ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
1382 }
1383 // TODO handle known-pointer-address
1384 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);
1385 const ty = Type.initTag(.usize);
1386 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });
1387}
1388
1389fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
1390 const object_ptr = try self.resolveInst(scope, fieldptr.positionals.object_ptr);
1391 const field_name = try self.resolveConstString(scope, fieldptr.positionals.field_name);
1392
1393 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
1394 .Pointer => object_ptr.ty.elemType(),
1395 else => return self.fail(scope, fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
1396 };
1397 switch (elem_ty.zigTypeTag()) {
1398 .Array => {
1399 if (mem.eql(u8, field_name, "len")) {
1400 const len_payload = try scope.arena().create(Value.Payload.Int_u64);
1401 len_payload.* = .{ .int = elem_ty.arrayLen() };
1402
1403 const ref_payload = try scope.arena().create(Value.Payload.RefVal);
1404 ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
1405
1406 return self.constInst(scope, fieldptr.base.src, .{
1407 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
1408 .val = Value.initPayload(&ref_payload.base),
1409 });
1410 } else {
1411 return self.fail(
1412 scope,
1413 fieldptr.positionals.field_name.src,
1414 "no member named '{}' in '{}'",
1415 .{ field_name, elem_ty },
1416 );
1417 }
1418 },
1419 else => return self.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}),
1420 }
1421}
1422
1423fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *zir.Inst.IntCast) InnerError!*Inst {
1424 const dest_type = try self.resolveType(scope, intcast.positionals.dest_type);
1425 const new_inst = try self.resolveInst(scope, intcast.positionals.value);
1426
1427 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
1428 .ComptimeInt => true,
1429 .Int => false,
1430 else => return self.fail(
1431 scope,
1432 intcast.positionals.dest_type.src,
1433 "expected integer type, found '{}'",
1434 .{
1435 dest_type,
1436 },
1437 ),
1438 };
1439
1440 switch (new_inst.ty.zigTypeTag()) {
1441 .ComptimeInt, .Int => {},
1442 else => return self.fail(
1443 scope,
1444 intcast.positionals.value.src,
1445 "expected integer type, found '{}'",
1446 .{new_inst.ty},
1447 ),
1448 }
1449
1450 if (dest_is_comptime_int or new_inst.value() != null) {
1451 return self.coerce(scope, dest_type, new_inst);
1452 }
1453
1454 return self.fail(scope, intcast.base.src, "TODO implement analyze widen or shorten int", .{});
1455}
1456
1457fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BitCast) InnerError!*Inst {
1458 const dest_type = try self.resolveType(scope, inst.positionals.dest_type);
1459 const operand = try self.resolveInst(scope, inst.positionals.operand);
1460 return self.bitcast(scope, dest_type, operand);
1461}
1462
1463fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst {
1464 const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr);
1465 const uncasted_index = try self.resolveInst(scope, inst.positionals.index);
1466 const elem_index = try self.coerce(scope, Type.initTag(.usize), uncasted_index);
1467
1468 if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {
1469 if (array_ptr.value()) |array_ptr_val| {
1470 if (elem_index.value()) |index_val| {
1471 // Both array pointer and index are compile-time known.
1472 const index_u64 = index_val.toUnsignedInt();
1473 // @intCast here because it would have been impossible to construct a value that
1474 // required a larger index.
1475 const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
1476
1477 const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
1478 type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() };
1479
1480 return self.constInst(scope, inst.base.src, .{
1481 .ty = Type.initPayload(&type_payload.base),
1482 .val = elem_ptr,
1483 });
1484 }
1485 }
1486 }
1487
1488 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
1489}
1490
1491fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!*Inst {
1492 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
1493 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
1494
1495 if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) {
1496 if (lhs.value()) |lhs_val| {
1497 if (rhs.value()) |rhs_val| {
1498 // TODO is this a performance issue? maybe we should try the operation without
1499 // resorting to BigInt first.
1500 var lhs_space: Value.BigIntSpace = undefined;
1501 var rhs_space: Value.BigIntSpace = undefined;
1502 const lhs_bigint = lhs_val.toBigInt(&lhs_space);
1503 const rhs_bigint = rhs_val.toBigInt(&rhs_space);
1504 const limbs = try scope.arena().alloc(
1505 std.math.big.Limb,
1506 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
1507 );
1508 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1509 result_bigint.add(lhs_bigint, rhs_bigint);
1510 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1511
1512 if (!lhs.ty.eql(rhs.ty)) {
1513 return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{});
1514 }
1515
1516 const val_payload = if (result_bigint.positive) blk: {
1517 const val_payload = try scope.arena().create(Value.Payload.IntBigPositive);
1518 val_payload.* = .{ .limbs = result_limbs };
1519 break :blk &val_payload.base;
1520 } else blk: {
1521 const val_payload = try scope.arena().create(Value.Payload.IntBigNegative);
1522 val_payload.* = .{ .limbs = result_limbs };
1523 break :blk &val_payload.base;
1524 };
1525
1526 return self.constInst(scope, inst.base.src, .{
1527 .ty = lhs.ty,
1528 .val = Value.initPayload(val_payload),
1529 });
1530 }
1531 }
1532 }
1533
1534 return self.fail(scope, inst.base.src, "TODO implement more analyze add", .{});
1535}
1536
1537fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.Deref) InnerError!*Inst {
1538 const ptr = try self.resolveInst(scope, deref.positionals.ptr);
1539 return self.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.ptr.src);
1540}
1541
1542fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
1543 const elem_ty = switch (ptr.ty.zigTypeTag()) {
1544 .Pointer => ptr.ty.elemType(),
1545 else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
1546 };
1547 if (ptr.value()) |val| {
1548 return self.constInst(scope, src, .{
1549 .ty = elem_ty,
1550 .val = try val.pointerDeref(scope.arena()),
1551 });
1552 }
1553
1554 return self.fail(scope, src, "TODO implement runtime deref", .{});
1555}
1556
1557fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst {
1558 const return_type = try self.resolveType(scope, assembly.positionals.return_type);
1559 const asm_source = try self.resolveConstString(scope, assembly.positionals.asm_source);
1560 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(scope, o) else null;
1561
1562 const inputs = try scope.arena().alloc([]const u8, assembly.kw_args.inputs.len);
1563 const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len);
1564 const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.len);
1565
1566 for (inputs) |*elem, i| {
1567 elem.* = try self.resolveConstString(scope, assembly.kw_args.inputs[i]);
1568 }
1569 for (clobbers) |*elem, i| {
1570 elem.* = try self.resolveConstString(scope, assembly.kw_args.clobbers[i]);
1571 }
1572 for (args) |*elem, i| {
1573 const arg = try self.resolveInst(scope, assembly.kw_args.args[i]);
1574 elem.* = try self.coerce(scope, Type.initTag(.usize), arg);
1575 }
1576
1577 const b = try self.requireRuntimeBlock(scope, assembly.base.src);
1578 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
1579 .asm_source = asm_source,
1580 .is_volatile = assembly.kw_args.@"volatile",
1581 .output = output,
1582 .inputs = inputs,
1583 .clobbers = clobbers,
1584 .args = args,
1585 });
1586}
1587
1588fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!*Inst {
1589 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
1590 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
1591 const op = inst.positionals.op;
1592
1593 const is_equality_cmp = switch (op) {
1594 .eq, .neq => true,
1595 else => false,
1596 };
1597 const lhs_ty_tag = lhs.ty.zigTypeTag();
1598 const rhs_ty_tag = rhs.ty.zigTypeTag();
1599 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
1600 // null == null, null != null
1601 return self.constBool(scope, inst.base.src, op == .eq);
1602 } else if (is_equality_cmp and
1603 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
1604 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
1605 {
1606 // comparing null with optionals
1607 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
1608 if (opt_operand.value()) |opt_val| {
1609 const is_null = opt_val.isNull();
1610 return self.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null);
1611 }
1612 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1613 switch (op) {
1614 .eq => return self.addNewInstArgs(
1615 b,
1616 inst.base.src,
1617 Type.initTag(.bool),
1618 Inst.IsNull,
1619 Inst.Args(Inst.IsNull){ .operand = opt_operand },
1620 ),
1621 .neq => return self.addNewInstArgs(
1622 b,
1623 inst.base.src,
1624 Type.initTag(.bool),
1625 Inst.IsNonNull,
1626 Inst.Args(Inst.IsNonNull){ .operand = opt_operand },
1627 ),
1628 else => unreachable,
1629 }
1630 } else if (is_equality_cmp and
1631 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
1632 {
1633 return self.fail(scope, inst.base.src, "TODO implement C pointer cmp", .{});
1634 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
1635 const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
1636 return self.fail(scope, inst.base.src, "comparison of '{}' with null", .{non_null_type});
1637 } else if (is_equality_cmp and
1638 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
1639 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
1640 {
1641 return self.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
1642 } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
1643 if (!is_equality_cmp) {
1644 return self.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)});
1645 }
1646 return self.fail(scope, inst.base.src, "TODO implement equality comparison between errors", .{});
1647 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
1648 // This operation allows any combination of integer and float types, regardless of the
1649 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
1650 // numeric types.
1651 return self.cmpNumeric(scope, inst.base.src, lhs, rhs, op);
1652 }
1653 return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
1654}
1655
1656fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {
1657 const operand = try self.resolveInst(scope, inst.positionals.operand);
1658 return self.analyzeIsNull(scope, inst.base.src, operand, true);
1659}
1660
1661fn analyzeInstIsNonNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNonNull) InnerError!*Inst {
1662 const operand = try self.resolveInst(scope, inst.positionals.operand);
1663 return self.analyzeIsNull(scope, inst.base.src, operand, false);
1664}
1665
1666fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {
1667 const uncasted_cond = try self.resolveInst(scope, inst.positionals.condition);
1668 const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond);
1669
1670 if (try self.resolveDefinedValue(scope, cond)) |cond_val| {
1671 const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body;
1672 try self.analyzeBody(scope, body.*);
1673 return self.constVoid(scope, inst.base.src);
1674 }
1675
1676 const parent_block = try self.requireRuntimeBlock(scope, inst.base.src);
1677
1678 var true_block: Scope.Block = .{
1679 .func = parent_block.func,
1680 .decl = parent_block.decl,
1681 .instructions = .{},
1682 .arena = parent_block.arena,
1683 };
1684 defer true_block.instructions.deinit(self.allocator);
1685 try self.analyzeBody(&true_block.base, inst.positionals.true_body);
1686
1687 var false_block: Scope.Block = .{
1688 .func = parent_block.func,
1689 .decl = parent_block.decl,
1690 .instructions = .{},
1691 .arena = parent_block.arena,
1692 };
1693 defer false_block.instructions.deinit(self.allocator);
1694 try self.analyzeBody(&false_block.base, inst.positionals.false_body);
1695
1696 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.void), Inst.CondBr, Inst.Args(Inst.CondBr){
1697 .condition = cond,
1698 .true_body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) },
1699 .false_body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) },
1700 });
1701}
1702
1703fn wantSafety(self: *Module, scope: *Scope) bool {
1704 return switch (self.optimize_mode) {
1705 .Debug => true,
1706 .ReleaseSafe => true,
1707 .ReleaseFast => false,
1708 .ReleaseSmall => false,
1709 };
1710}
1711
1712fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unreachable) InnerError!*Inst {
1713 const b = try self.requireRuntimeBlock(scope, unreach.base.src);
1714 if (self.wantSafety(scope)) {
1715 // TODO Once we have a panic function to call, call it here instead of this.
1716 _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {});
1717 }
1718 return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
1719}
1720
1721fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst {
1722 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1723 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {});
1724}
1725
1726fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {
1727 if (scope.cast(Scope.Block)) |b| {
1728 const analysis = b.func.analysis.in_progress;
1729 analysis.needed_inst_capacity += body.instructions.len;
1730 try analysis.inst_table.ensureCapacity(analysis.needed_inst_capacity);
1731 for (body.instructions) |src_inst| {
1732 const new_inst = try self.analyzeInst(scope, src_inst);
1733 analysis.inst_table.putAssumeCapacityNoClobber(src_inst, new_inst);
1734 }
1735 } else {
1736 for (body.instructions) |src_inst| {
1737 _ = try self.analyzeInst(scope, src_inst);
1738 }
1739 }
1740}
1741
1742fn analyzeIsNull(
1743 self: *Module,
1744 scope: *Scope,
1745 src: usize,
1746 operand: *Inst,
1747 invert_logic: bool,
1748) InnerError!*Inst {
1749 return self.fail(scope, src, "TODO implement analysis of isnull and isnotnull", .{});
1750}
1751
1752/// Asserts that lhs and rhs types are both numeric.
1753fn cmpNumeric(
1754 self: *Module,
1755 scope: *Scope,
1756 src: usize,
1757 lhs: *Inst,
1758 rhs: *Inst,
1759 op: std.math.CompareOperator,
1760) !*Inst {
1761 assert(lhs.ty.isNumeric());
1762 assert(rhs.ty.isNumeric());
1763
1764 const lhs_ty_tag = lhs.ty.zigTypeTag();
1765 const rhs_ty_tag = rhs.ty.zigTypeTag();
1766
1767 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
1768 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
1769 return self.fail(scope, src, "vector length mismatch: {} and {}", .{
1770 lhs.ty.arrayLen(),
1771 rhs.ty.arrayLen(),
1772 });
1773 }
1774 return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{});
1775 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
1776 return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
1777 lhs.ty,
1778 rhs.ty,
1779 });
1780 }
1781
1782 if (lhs.value()) |lhs_val| {
1783 if (rhs.value()) |rhs_val| {
1784 return self.constBool(scope, src, Value.compare(lhs_val, op, rhs_val));
1785 }
1786 }
1787
1788 // TODO handle comparisons against lazy zero values
1789 // Some values can be compared against zero without being runtime known or without forcing
1790 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
1791 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
1792 // of this function if we don't need to.
1793
1794 // It must be a runtime comparison.
1795 const b = try self.requireRuntimeBlock(scope, src);
1796 // For floats, emit a float comparison instruction.
1797 const lhs_is_float = switch (lhs_ty_tag) {
1798 .Float, .ComptimeFloat => true,
1799 else => false,
1800 };
1801 const rhs_is_float = switch (rhs_ty_tag) {
1802 .Float, .ComptimeFloat => true,
1803 else => false,
1804 };
1805 if (lhs_is_float and rhs_is_float) {
1806 // Implicit cast the smaller one to the larger one.
1807 const dest_type = x: {
1808 if (lhs_ty_tag == .ComptimeFloat) {
1809 break :x rhs.ty;
1810 } else if (rhs_ty_tag == .ComptimeFloat) {
1811 break :x lhs.ty;
1812 }
1813 if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) {
1814 break :x lhs.ty;
1815 } else {
1816 break :x rhs.ty;
1817 }
1818 };
1819 const casted_lhs = try self.coerce(scope, dest_type, lhs);
1820 const casted_rhs = try self.coerce(scope, dest_type, rhs);
1821 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
1822 .lhs = casted_lhs,
1823 .rhs = casted_rhs,
1824 .op = op,
1825 });
1826 }
1827 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
1828 // For mixed signed and unsigned integers, implicit cast both operands to a signed
1829 // integer with + 1 bit.
1830 // For mixed floats and integers, extract the integer part from the float, cast that to
1831 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
1832 // add/subtract 1.
1833 const lhs_is_signed = if (lhs.value()) |lhs_val|
1834 lhs_val.compareWithZero(.lt)
1835 else
1836 (lhs.ty.isFloat() or lhs.ty.isSignedInt());
1837 const rhs_is_signed = if (rhs.value()) |rhs_val|
1838 rhs_val.compareWithZero(.lt)
1839 else
1840 (rhs.ty.isFloat() or rhs.ty.isSignedInt());
1841 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
1842
1843 var dest_float_type: ?Type = null;
1844
1845 var lhs_bits: usize = undefined;
1846 if (lhs.value()) |lhs_val| {
1847 if (lhs_val.isUndef())
1848 return self.constUndef(scope, src, Type.initTag(.bool));
1849 const is_unsigned = if (lhs_is_float) x: {
1850 var bigint_space: Value.BigIntSpace = undefined;
1851 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
1852 defer bigint.deinit();
1853 const zcmp = lhs_val.orderAgainstZero();
1854 if (lhs_val.floatHasFraction()) {
1855 switch (op) {
1856 .eq => return self.constBool(scope, src, false),
1857 .neq => return self.constBool(scope, src, true),
1858 else => {},
1859 }
1860 if (zcmp == .lt) {
1861 try bigint.addScalar(bigint.toConst(), -1);
1862 } else {
1863 try bigint.addScalar(bigint.toConst(), 1);
1864 }
1865 }
1866 lhs_bits = bigint.toConst().bitCountTwosComp();
1867 break :x (zcmp != .lt);
1868 } else x: {
1869 lhs_bits = lhs_val.intBitCountTwosComp();
1870 break :x (lhs_val.orderAgainstZero() != .lt);
1871 };
1872 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
1873 } else if (lhs_is_float) {
1874 dest_float_type = lhs.ty;
1875 } else {
1876 const int_info = lhs.ty.intInfo(self.target());
1877 lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
1878 }
1879
1880 var rhs_bits: usize = undefined;
1881 if (rhs.value()) |rhs_val| {
1882 if (rhs_val.isUndef())
1883 return self.constUndef(scope, src, Type.initTag(.bool));
1884 const is_unsigned = if (rhs_is_float) x: {
1885 var bigint_space: Value.BigIntSpace = undefined;
1886 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
1887 defer bigint.deinit();
1888 const zcmp = rhs_val.orderAgainstZero();
1889 if (rhs_val.floatHasFraction()) {
1890 switch (op) {
1891 .eq => return self.constBool(scope, src, false),
1892 .neq => return self.constBool(scope, src, true),
1893 else => {},
1894 }
1895 if (zcmp == .lt) {
1896 try bigint.addScalar(bigint.toConst(), -1);
1897 } else {
1898 try bigint.addScalar(bigint.toConst(), 1);
1899 }
1900 }
1901 rhs_bits = bigint.toConst().bitCountTwosComp();
1902 break :x (zcmp != .lt);
1903 } else x: {
1904 rhs_bits = rhs_val.intBitCountTwosComp();
1905 break :x (rhs_val.orderAgainstZero() != .lt);
1906 };
1907 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
1908 } else if (rhs_is_float) {
1909 dest_float_type = rhs.ty;
1910 } else {
1911 const int_info = rhs.ty.intInfo(self.target());
1912 rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
1913 }
1914
1915 const dest_type = if (dest_float_type) |ft| ft else blk: {
1916 const max_bits = std.math.max(lhs_bits, rhs_bits);
1917 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
1918 error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}),
1919 };
1920 break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);
1921 };
1922 const casted_lhs = try self.coerce(scope, dest_type, lhs);
1923 const casted_rhs = try self.coerce(scope, dest_type, lhs);
1924
1925 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
1926 .lhs = casted_lhs,
1927 .rhs = casted_rhs,
1928 .op = op,
1929 });
1930}
1931
1932fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
1933 if (signed) {
1934 const int_payload = try scope.arena().create(Type.Payload.IntSigned);
1935 int_payload.* = .{ .bits = bits };
1936 return Type.initPayload(&int_payload.base);
1937 } else {
1938 const int_payload = try scope.arena().create(Type.Payload.IntUnsigned);
1939 int_payload.* = .{ .bits = bits };
1940 return Type.initPayload(&int_payload.base);
1941 }
1942}
1943
1944fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
1945 // If the types are the same, we can return the operand.
1946 if (dest_type.eql(inst.ty))
1947 return inst;
1948
1949 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
1950 if (in_memory_result == .ok) {
1951 return self.bitcast(scope, dest_type, inst);
1952 }
1953
1954 // *[N]T to []T
1955 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
1956 (!inst.ty.pointerIsConst() or dest_type.pointerIsConst()))
1957 {
1958 const array_type = inst.ty.elemType();
1959 const dst_elem_type = dest_type.elemType();
1960 if (array_type.zigTypeTag() == .Array and
1961 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
1962 {
1963 return self.coerceArrayPtrToSlice(scope, dest_type, inst);
1964 }
1965 }
1966
1967 // comptime_int to fixed-width integer
1968 if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) {
1969 // The representation is already correct; we only need to make sure it fits in the destination type.
1970 const val = inst.value().?; // comptime_int always has comptime known value
1971 if (!val.intFitsInType(dest_type, self.target())) {
1972 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
1973 }
1974 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
1975 }
1976
1977 // integer widening
1978 if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
1979 const src_info = inst.ty.intInfo(self.target());
1980 const dst_info = dest_type.intInfo(self.target());
1981 if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) {
1982 if (inst.value()) |val| {
1983 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
1984 } else {
1985 return self.fail(scope, inst.src, "TODO implement runtime integer widening", .{});
1986 }
1987 } else {
1988 return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type });
1989 }
1990 }
1991
1992 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
1993}
1994
1995fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
1996 if (inst.value()) |val| {
1997 // Keep the comptime Value representation; take the new type.
1998 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
1999 }
2000 // TODO validate the type size and other compile errors
2001 const b = try self.requireRuntimeBlock(scope, inst.src);
2002 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
2003}
2004
2005fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2006 if (inst.value()) |val| {
2007 // The comptime Value representation is compatible with both types.
2008 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2009 }
2010 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
2011}
2012
2013fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: var) InnerError {
2014 @setCold(true);
2015 const err_msg = try ErrorMsg.create(self.allocator, src, format, args);
2016 return self.failWithOwnedErrorMsg(scope, src, err_msg);
2017}
2018
2019fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError {
2020 {
2021 errdefer err_msg.destroy(self.allocator);
2022 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
2023 try self.failed_files.ensureCapacity(self.failed_files.size + 1);
2024 }
2025 switch (scope.tag) {
2026 .decl => {
2027 const decl = scope.cast(Scope.DeclAnalysis).?.decl;
2028 switch (decl.analysis) {
2029 .initial_in_progress => decl.analysis = .initial_sema_failure,
2030 .repeat_in_progress => decl.analysis = .repeat_sema_failure,
2031 else => unreachable,
2032 }
2033 self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
2034 },
2035 .block => {
2036 const block = scope.cast(Scope.Block).?;
2037 block.func.analysis = .sema_failure;
2038 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
2039 },
2040 .zir_module => {
2041 const zir_module = scope.cast(Scope.ZIRModule).?;
2042 zir_module.status = .loaded_sema_failure;
2043 self.failed_files.putAssumeCapacityNoClobber(zir_module, err_msg);
2044 },
2045 }
2046 return error.AnalysisFail;
2047}
2048
2049const InMemoryCoercionResult = enum {
2050 ok,
2051 no_match,
2052};
2053
2054fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
2055 if (dest_type.eql(src_type))
2056 return .ok;
2057
2058 // TODO: implement more of this function
2059
2060 return .no_match;
2061}
2062
2063pub const ErrorMsg = struct {
2064 byte_offset: usize,
2065 msg: []const u8,
2066
2067 pub fn create(allocator: *Allocator, byte_offset: usize, comptime format: []const u8, args: var) !*ErrorMsg {
2068 const self = try allocator.create(ErrorMsg);
2069 errdefer allocator.destroy(self);
2070 self.* = try init(allocator, byte_offset, format, args);
2071 return self;
2072 }
2073
2074 /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
2075 pub fn destroy(self: *ErrorMsg, allocator: *Allocator) void {
2076 self.deinit(allocator);
2077 allocator.destroy(self);
2078 }
2079
2080 pub fn init(allocator: *Allocator, byte_offset: usize, comptime format: []const u8, args: var) !ErrorMsg {
2081 return ErrorMsg{
2082 .byte_offset = byte_offset,
2083 .msg = try std.fmt.allocPrint(allocator, format, args),
2084 };
2085 }
2086
2087 pub fn deinit(self: *ErrorMsg, allocator: *Allocator) void {
2088 allocator.free(self.msg);
2089 self.* = undefined;
2090 }
2091};
src-self-hosted/Package.zig created+53
......@@ -0,0 +1,53 @@
1pub const Table = std.StringHashMap(*Package);
2
3root_src_dir: std.fs.Dir,
4/// Relative to `root_src_dir`.
5root_src_path: []const u8,
6table: Table,
7
8/// No references to `root_src_dir` and `root_src_path` are kept.
9pub fn create(
10 allocator: *mem.Allocator,
11 base_dir: std.fs.Dir,
12 /// Relative to `base_dir`.
13 root_src_dir: []const u8,
14 /// Relative to `root_src_dir`.
15 root_src_path: []const u8,
16) !*Package {
17 const ptr = try allocator.create(Package);
18 errdefer allocator.destroy(ptr);
19 const root_src_path_dupe = try mem.dupe(allocator, u8, root_src_path);
20 errdefer allocator.free(root_src_path_dupe);
21 ptr.* = .{
22 .root_src_dir = try base_dir.openDir(root_src_dir, .{}),
23 .root_src_path = root_src_path_dupe,
24 .table = Table.init(allocator),
25 };
26 return ptr;
27}
28
29pub fn destroy(self: *Package) void {
30 const allocator = self.table.allocator;
31 self.root_src_dir.close();
32 allocator.free(self.root_src_path);
33 {
34 var it = self.table.iterator();
35 while (it.next()) |kv| {
36 allocator.free(kv.key);
37 }
38 }
39 self.table.deinit();
40 allocator.destroy(self);
41}
42
43pub fn add(self: *Package, name: []const u8, package: *Package) !void {
44 const name_dupe = try mem.dupe(self.table.allocator, u8, name);
45 errdefer self.table.allocator.deinit(name_dupe);
46 const entry = try self.table.put(name_dupe, package);
47 assert(entry == null);
48}
49
50const std = @import("std");
51const mem = std.mem;
52const assert = std.debug.assert;
53const Package = @This();
src-self-hosted/TypedValue.zig created+23
......@@ -0,0 +1,23 @@
1const std = @import("std");
2const Type = @import("type.zig").Type;
3const Value = @import("value.zig").Value;
4const Allocator = std.mem.Allocator;
5const TypedValue = @This();
6
7ty: Type,
8val: Value,
9
10/// Memory management for TypedValue. The main purpose of this type
11/// is to be small and have a deinit() function to free associated resources.
12pub const Managed = struct {
13 /// If the tag value is less than Tag.no_payload_count, then no pointer
14 /// dereference is needed.
15 typed_value: TypedValue,
16 /// If this is `null` then there is no memory management needed.
17 arena: ?*std.heap.ArenaAllocator.State = null,
18
19 pub fn deinit(self: *Managed, allocator: *Allocator) void {
20 if (self.arena) |a| a.promote(allocator).deinit();
21 self.* = undefined;
22 }
23};
src-self-hosted/c.zig deleted-7
......@@ -1,7 +0,0 @@
1pub usingnamespace @cImport({
2 @cDefine("__STDC_CONSTANT_MACROS", "");
3 @cDefine("__STDC_LIMIT_MACROS", "");
4 @cInclude("inttypes.h");
5 @cInclude("config.h");
6 @cInclude("zig_llvm.h");
7});
src-self-hosted/codegen.zig+349-295
......@@ -4,64 +4,155 @@ const assert = std.debug.assert;
44const ir = @import("ir.zig");
55const Type = @import("type.zig").Type;
66const Value = @import("value.zig").Value;
7const TypedValue = @import("TypedValue.zig");
8const link = @import("link.zig");
9const Module = @import("Module.zig");
10const ErrorMsg = Module.ErrorMsg;
711const Target = std.Target;
8
9pub const ErrorMsg = struct {
10 byte_offset: usize,
11 msg: []const u8,
12};
13
14pub const Symbol = struct {
15 errors: []ErrorMsg,
16
17 pub fn deinit(self: *Symbol, allocator: *mem.Allocator) void {
18 for (self.errors) |err| {
19 allocator.free(err.msg);
20 }
21 allocator.free(self.errors);
22 self.* = undefined;
23 }
12const Allocator = mem.Allocator;
13
14pub const Result = union(enum) {
15 /// The `code` parameter passed to `generateSymbol` has the value appended.
16 appended: void,
17 /// The value is available externally, `code` is unused.
18 externally_managed: []const u8,
19 fail: *Module.ErrorMsg,
2420};
2521
26pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.ArrayList(u8)) !Symbol {
22pub fn generateSymbol(
23 bin_file: *link.ElfFile,
24 src: usize,
25 typed_value: TypedValue,
26 code: *std.ArrayList(u8),
27) error{
28 OutOfMemory,
29 /// A Decl that this symbol depends on had a semantic analysis failure.
30 AnalysisFail,
31}!Result {
2732 switch (typed_value.ty.zigTypeTag()) {
2833 .Fn => {
29 const index = typed_value.val.cast(Value.Payload.Function).?.index;
30 const module_fn = module.fns[index];
34 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
3135
3236 var function = Function{
33 .module = &module,
34 .mod_fn = &module_fn,
37 .target = &bin_file.options.target,
38 .bin_file = bin_file,
39 .mod_fn = module_fn,
3540 .code = code,
36 .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator),
37 .errors = std.ArrayList(ErrorMsg).init(code.allocator),
41 .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(bin_file.allocator),
42 .err_msg = null,
3843 };
3944 defer function.inst_table.deinit();
40 defer function.errors.deinit();
4145
42 for (module_fn.body.instructions) |inst| {
46 for (module_fn.analysis.success.instructions) |inst| {
4347 const new_inst = function.genFuncInst(inst) catch |err| switch (err) {
44 error.CodegenFail => {
45 assert(function.errors.items.len != 0);
46 break;
47 },
48 error.CodegenFail => return Result{ .fail = function.err_msg.? },
4849 else => |e| return e,
4950 };
5051 try function.inst_table.putNoClobber(inst, new_inst);
5152 }
5253
53 return Symbol{ .errors = function.errors.toOwnedSlice() };
54 if (function.err_msg) |em| {
55 return Result{ .fail = em };
56 } else {
57 return Result{ .appended = {} };
58 }
59 },
60 .Array => {
61 if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {
62 if (typed_value.ty.arraySentinel()) |sentinel| {
63 try code.ensureCapacity(code.items.len + payload.data.len + 1);
64 code.appendSliceAssumeCapacity(payload.data);
65 const prev_len = code.items.len;
66 switch (try generateSymbol(bin_file, src, .{
67 .ty = typed_value.ty.elemType(),
68 .val = sentinel,
69 }, code)) {
70 .appended => return Result{ .appended = {} },
71 .externally_managed => |slice| {
72 code.appendSliceAssumeCapacity(slice);
73 return Result{ .appended = {} };
74 },
75 .fail => |em| return Result{ .fail = em },
76 }
77 } else {
78 return Result{ .externally_managed = payload.data };
79 }
80 }
81 return Result{
82 .fail = try ErrorMsg.create(
83 bin_file.allocator,
84 src,
85 "TODO implement generateSymbol for more kinds of arrays",
86 .{},
87 ),
88 };
89 },
90 .Pointer => {
91 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
92 const decl = payload.decl;
93 if (decl.analysis != .complete) return error.AnalysisFail;
94 assert(decl.link.local_sym_index != 0);
95 // TODO handle the dependency of this symbol on the decl's vaddr.
96 // If the decl changes vaddr, then this symbol needs to get regenerated.
97 const vaddr = bin_file.local_symbols.items[decl.link.local_sym_index].st_value;
98 const endian = bin_file.options.target.cpu.arch.endian();
99 switch (bin_file.ptr_width) {
100 .p32 => {
101 try code.resize(4);
102 mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
103 },
104 .p64 => {
105 try code.resize(8);
106 mem.writeInt(u64, code.items[0..8], vaddr, endian);
107 },
108 }
109 return Result{ .appended = {} };
110 }
111 return Result{
112 .fail = try ErrorMsg.create(
113 bin_file.allocator,
114 src,
115 "TODO implement generateSymbol for pointer {}",
116 .{typed_value.val},
117 ),
118 };
119 },
120 .Int => {
121 const info = typed_value.ty.intInfo(bin_file.options.target);
122 if (info.bits == 8 and !info.signed) {
123 const x = typed_value.val.toUnsignedInt();
124 try code.append(@intCast(u8, x));
125 return Result{ .appended = {} };
126 }
127 return Result{
128 .fail = try ErrorMsg.create(
129 bin_file.allocator,
130 src,
131 "TODO implement generateSymbol for int type '{}'",
132 .{typed_value.ty},
133 ),
134 };
135 },
136 else => |t| {
137 return Result{
138 .fail = try ErrorMsg.create(
139 bin_file.allocator,
140 src,
141 "TODO implement generateSymbol for type '{}'",
142 .{@tagName(t)},
143 ),
144 };
54145 },
55 else => @panic("TODO implement generateSymbol for non-function types"),
56146 }
57147}
58148
59149const Function = struct {
60 module: *const ir.Module,
61 mod_fn: *const ir.Module.Fn,
150 bin_file: *link.ElfFile,
151 target: *const std.Target,
152 mod_fn: *const Module.Fn,
62153 code: *std.ArrayList(u8),
63154 inst_table: std.AutoHashMap(*ir.Inst, MCValue),
64 errors: std.ArrayList(ErrorMsg),
155 err_msg: ?*ErrorMsg,
65156
66157 const MCValue = union(enum) {
67158 none,
......@@ -73,11 +164,14 @@ const Function = struct {
73164 /// The value is in a target-specific register. The value can
74165 /// be @intToEnum casted to the respective Reg enum.
75166 register: usize,
167 /// The value is in memory at a hard-coded address.
168 memory: u64,
76169 };
77170
78171 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {
79172 switch (inst.tag) {
80173 .breakpoint => return self.genBreakpoint(inst.src),
174 .call => return self.genCall(inst.cast(ir.Inst.Call).?),
81175 .unreach => return MCValue{ .unreach = {} },
82176 .constant => unreachable, // excluded from function bodies
83177 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
......@@ -92,54 +186,76 @@ const Function = struct {
92186 }
93187
94188 fn genBreakpoint(self: *Function, src: usize) !MCValue {
95 switch (self.module.target.cpu.arch) {
189 switch (self.target.cpu.arch) {
96190 .i386, .x86_64 => {
97191 try self.code.append(0xcc); // int3
98192 },
99 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.module.target.cpu.arch}),
193 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
194 }
195 return .none;
196 }
197
198 fn genCall(self: *Function, inst: *ir.Inst.Call) !MCValue {
199 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {
200 if (inst.args.args.len != 0) {
201 return self.fail(inst.base.src, "TODO implement call with more than 0 parameters", .{});
202 }
203
204 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
205 const func = func_val.func;
206 return self.fail(inst.base.src, "TODO implement calling function", .{});
207 } else {
208 return self.fail(inst.base.src, "TODO implement calling weird function values", .{});
209 }
210 } else {
211 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
212 }
213
214 switch (self.target.cpu.arch) {
215 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
100216 }
101 return .unreach;
102217 }
103218
104219 fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
105 switch (self.module.target.cpu.arch) {
220 switch (self.target.cpu.arch) {
106221 .i386, .x86_64 => {
107222 try self.code.append(0xc3); // ret
108223 },
109 else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.module.target.cpu.arch}),
224 else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.target.cpu.arch}),
110225 }
111226 return .unreach;
112227 }
113228
114229 fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue {
115 switch (self.module.target.cpu.arch) {
116 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.module.target.cpu.arch}),
230 switch (self.target.cpu.arch) {
231 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
117232 }
118233 }
119234
120235 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr) !MCValue {
121 switch (self.module.target.cpu.arch) {
122 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.module.target.cpu.arch}),
236 switch (self.target.cpu.arch) {
237 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
123238 }
124239 }
125240
126241 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull) !MCValue {
127 switch (self.module.target.cpu.arch) {
128 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.module.target.cpu.arch}),
242 switch (self.target.cpu.arch) {
243 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
129244 }
130245 }
131246
132247 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull) !MCValue {
133248 // Here you can specialize this instruction if it makes sense to, otherwise the default
134249 // will call genIsNull and invert the result.
135 switch (self.module.target.cpu.arch) {
250 switch (self.target.cpu.arch) {
136251 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
137252 }
138253 }
139254
140255 fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void {
141 switch (self.module.target.cpu.arch) {
256 switch (self.target.cpu.arch) {
142257 .i386, .x86_64 => {
258 // TODO x86 treats the operands as signed
143259 if (amount <= std.math.maxInt(u8)) {
144260 try self.code.resize(self.code.items.len + 2);
145261 self.code.items[self.code.items.len - 2] = 0xeb;
......@@ -151,13 +267,13 @@ const Function = struct {
151267 mem.writeIntLittle(u32, imm_ptr, amount);
152268 }
153269 },
154 else => return self.fail(src, "TODO implement relative forward jump for {}", .{self.module.target.cpu.arch}),
270 else => return self.fail(src, "TODO implement relative forward jump for {}", .{self.target.cpu.arch}),
155271 }
156272 }
157273
158274 fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue {
159275 // TODO convert to inline function
160 switch (self.module.target.cpu.arch) {
276 switch (self.target.cpu.arch) {
161277 .arm => return self.genAsmArch(.arm, inst),
162278 .armeb => return self.genAsmArch(.armeb, inst),
163279 .aarch64 => return self.genAsmArch(.aarch64, inst),
......@@ -246,128 +362,182 @@ const Function = struct {
246362 }
247363 }
248364
249 fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) !void {
365 fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {
250366 switch (arch) {
251 .x86_64 => switch (reg) {
252 .rax => switch (mcv) {
253 .none, .unreach => unreachable,
254 .immediate => |x| {
255 // Setting the eax register zeroes the upper part of rax, so if the number is small
256 // enough, that is preferable.
257 // Best case: zero
258 // 31 c0 xor eax,eax
259 if (x == 0) {
260 return self.code.appendSlice(&[_]u8{ 0x31, 0xc0 });
367 .x86_64 => switch (mcv) {
368 .none, .unreach => unreachable,
369 .immediate => |x| {
370 if (reg.size() != 64) {
371 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
372 }
373 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
374 // register is the fastest way to zero a register.
375 if (x == 0) {
376 // The encoding for `xor r32, r32` is `0x31 /r`.
377 // Section 3.1.1.1 of the Intel x64 Manual states that "/r indicates that the
378 // ModR/M byte of the instruction contains a register operand and an r/m operand."
379 //
380 // R/M bytes are composed of two bits for the mode, then three bits for the register,
381 // then three bits for the operand. Since we're zeroing a register, the two three-bit
382 // values will be identical, and the mode is three (the raw register value).
383 //
384 if (reg.isExtended()) {
385 // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since
386 // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB.
387 // Both R and B are set, as we're extending, in effect, the register bits *and* the operand.
388 //
389 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB. In this case, that's
390 // b01000101, or 0x45.
391 return self.code.appendSlice(&[_]u8{
392 0x45,
393 0x31,
394 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id()),
395 });
396 } else {
397 return self.code.appendSlice(&[_]u8{
398 0x31,
399 0xC0 | (@as(u8, reg.id()) << 3) | reg.id(),
400 });
261401 }
262 // Next best case: set eax with 4 bytes
263 // b8 04 03 02 01 mov eax,0x01020304
264 if (x <= std.math.maxInt(u32)) {
402 }
403 if (x <= std.math.maxInt(u32)) {
404 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
405 //
406 // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.
407 if (reg.isExtended()) {
408 // Just as with XORing, we need a REX prefix. This time though, we only
409 // need the B bit set, as we're extending the opcode's register field,
410 // and there is no Mod R/M byte.
411 //
412 // Thus, we need b01000001, or 0x41.
413 try self.code.resize(self.code.items.len + 6);
414 self.code.items[self.code.items.len - 6] = 0x41;
415 } else {
265416 try self.code.resize(self.code.items.len + 5);
266 self.code.items[self.code.items.len - 5] = 0xb8;
267 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
268 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
269 return;
270417 }
271 // Worst case: set rax with 8 bytes
272 // 48 b8 08 07 06 05 04 03 02 01 movabs rax,0x0102030405060708
273 try self.code.resize(self.code.items.len + 10);
274 self.code.items[self.code.items.len - 10] = 0x48;
275 self.code.items[self.code.items.len - 9] = 0xb8;
276 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
277 mem.writeIntLittle(u64, imm_ptr, x);
418 self.code.items[self.code.items.len - 5] = 0xB8 | @as(u8, reg.id() & 0b111);
419 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
420 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
278421 return;
279 },
280 .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rax = embedded_in_code", .{}),
281 .register => return self.fail(src, "TODO implement x86_64 genSetReg %rax = register", .{}),
422 }
423 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls
424 // this `movabs`, though this is officially just a different variant of the plain `mov`
425 // instruction.
426 //
427 // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only
428 // difference is that we set REX.W before the instruction, which extends the load to
429 // 64-bit and uses the full bit-width of the register.
430 //
431 // Since we always need a REX here, let's just check if we also need to set REX.B.
432 //
433 // In this case, the encoding of the REX byte is 0b0100100B
434 const REX = 0x48 | (if (reg.isExtended()) @as(u8, 0x01) else 0);
435 try self.code.resize(self.code.items.len + 10);
436 self.code.items[self.code.items.len - 10] = REX;
437 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);
438 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
439 mem.writeIntLittle(u64, imm_ptr, x);
282440 },
283 .rdx => switch (mcv) {
284 .none, .unreach => unreachable,
285 .immediate => |x| {
286 // Setting the edx register zeroes the upper part of rdx, so if the number is small
287 // enough, that is preferable.
288 // Best case: zero
289 // 31 d2 xor edx,edx
290 if (x == 0) {
291 return self.code.appendSlice(&[_]u8{ 0x31, 0xd2 });
292 }
293 // Next best case: set edx with 4 bytes
294 // ba 04 03 02 01 mov edx,0x1020304
295 if (x <= std.math.maxInt(u32)) {
296 try self.code.resize(self.code.items.len + 5);
297 self.code.items[self.code.items.len - 5] = 0xba;
298 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
299 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
300 return;
301 }
302 // Worst case: set rdx with 8 bytes
303 // 48 ba 08 07 06 05 04 03 02 01 movabs rdx,0x0102030405060708
304 try self.code.resize(self.code.items.len + 10);
305 self.code.items[self.code.items.len - 10] = 0x48;
306 self.code.items[self.code.items.len - 9] = 0xba;
307 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
308 mem.writeIntLittle(u64, imm_ptr, x);
309 return;
310 },
311 .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = embedded_in_code", .{}),
312 .register => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = register", .{}),
441 .embedded_in_code => |code_offset| {
442 if (reg.size() != 64) {
443 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
444 }
445 // We need the offset from RIP in a signed i32 twos complement.
446 // The instruction is 7 bytes long and RIP points to the next instruction.
447 //
448 // 64-bit LEA is encoded as REX.W 8D /r. If the register is extended, the REX byte is modified,
449 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three
450 // bits as five.
451 // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.
452 try self.code.resize(self.code.items.len + 7);
453 const REX = 0x48 | if (reg.isExtended()) @as(u8, 1) else 0;
454 const rip = self.code.items.len;
455 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
456 const offset = @intCast(i32, big_offset);
457 self.code.items[self.code.items.len - 7] = REX;
458 self.code.items[self.code.items.len - 6] = 0x8D;
459 self.code.items[self.code.items.len - 5] = 0b101 | (@as(u8, reg.id() & 0b111) << 3);
460 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
461 mem.writeIntLittle(i32, imm_ptr, offset);
313462 },
314 .rdi => switch (mcv) {
315 .none, .unreach => unreachable,
316 .immediate => |x| {
317 // Setting the edi register zeroes the upper part of rdi, so if the number is small
318 // enough, that is preferable.
319 // Best case: zero
320 // 31 ff xor edi,edi
321 if (x == 0) {
322 return self.code.appendSlice(&[_]u8{ 0x31, 0xff });
323 }
324 // Next best case: set edi with 4 bytes
325 // bf 04 03 02 01 mov edi,0x1020304
326 if (x <= std.math.maxInt(u32)) {
327 try self.code.resize(self.code.items.len + 5);
328 self.code.items[self.code.items.len - 5] = 0xbf;
329 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
330 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
331 return;
332 }
333 // Worst case: set rdi with 8 bytes
334 // 48 bf 08 07 06 05 04 03 02 01 movabs rax,0x0102030405060708
335 try self.code.resize(self.code.items.len + 10);
336 self.code.items[self.code.items.len - 10] = 0x48;
337 self.code.items[self.code.items.len - 9] = 0xbf;
338 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
339 mem.writeIntLittle(u64, imm_ptr, x);
340 return;
341 },
342 .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = embedded_in_code", .{}),
343 .register => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = register", .{}),
463 .register => |r| {
464 if (reg.size() != 64) {
465 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
466 }
467 const src_reg = @intToEnum(Reg(arch), @intCast(u8, r));
468 // This is a varient of 8B /r. Since we're using 64-bit moves, we require a REX.
469 // This is thus three bytes: REX 0x8B R/M.
470 // If the destination is extended, the R field must be 1.
471 // If the *source* is extended, the B field must be 1.
472 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle
473 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.
474 const REX = 0x48 | (if (reg.isExtended()) @as(u8, 4) else 0) | (if (src_reg.isExtended()) @as(u8, 1) else 0);
475 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, src_reg.id());
476 try self.code.appendSlice(&[_]u8{ REX, 0x8B, R });
344477 },
345 .rsi => switch (mcv) {
346 .none, .unreach => unreachable,
347 .immediate => return self.fail(src, "TODO implement x86_64 genSetReg %rsi = immediate", .{}),
348 .embedded_in_code => |code_offset| {
349 // Examples:
350 // lea rsi, [rip + 0x01020304]
351 // lea rsi, [rip - 7]
352 // f: 48 8d 35 04 03 02 01 lea rsi,[rip+0x1020304] # 102031a <_start+0x102031a>
353 // 16: 48 8d 35 f9 ff ff ff lea rsi,[rip+0xfffffffffffffff9] # 16 <_start+0x16>
354 //
355 // We need the offset from RIP in a signed i32 twos complement.
356 // The instruction is 7 bytes long and RIP points to the next instruction.
357 try self.code.resize(self.code.items.len + 7);
358 const rip = self.code.items.len;
359 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
360 const offset = @intCast(i32, big_offset);
361 self.code.items[self.code.items.len - 7] = 0x48;
362 self.code.items[self.code.items.len - 6] = 0x8d;
363 self.code.items[self.code.items.len - 5] = 0x35;
478 .memory => |x| {
479 if (reg.size() != 64) {
480 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
481 }
482 if (x <= std.math.maxInt(u32)) {
483 // Moving from memory to a register is a variant of `8B /r`.
484 // Since we're using 64-bit moves, we require a REX.
485 // This variant also requires a SIB, as it would otherwise be RIP-relative.
486 // We want mode zero with the lower three bits set to four to indicate an SIB with no other displacement.
487 // The SIB must be 0x25, to indicate a disp32 with no scaled index.
488 // 0b00RRR100, where RRR is the lower three bits of the register ID.
489 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
490 try self.code.resize(self.code.items.len + 8);
491 const REX = 0x48 | if (reg.isExtended()) @as(u8, 1) else 0;
492 const r = 0x04 | (@as(u8, reg.id() & 0b111) << 3);
493 self.code.items[self.code.items.len - 8] = REX;
494 self.code.items[self.code.items.len - 7] = 0x8B;
495 self.code.items[self.code.items.len - 6] = r;
496 self.code.items[self.code.items.len - 5] = 0x25;
364497 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
365 mem.writeIntLittle(i32, imm_ptr, offset);
366 return;
367 },
368 .register => return self.fail(src, "TODO implement x86_64 genSetReg %rsi = register", .{}),
498 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
499 } else {
500 // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load
501 // the value.
502 if (reg.id() == 0) {
503 // REX.W 0xA1 moffs64*
504 // moffs64* is a 64-bit offset "relative to segment base", which really just means the
505 // absolute address for all practical purposes.
506 try self.code.resize(self.code.items.len + 10);
507 // REX.W == 0x48
508 self.code.items[self.code.items.len - 10] = 0x48;
509 self.code.items[self.code.items.len - 9] = 0xA1;
510 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
511 mem.writeIntLittle(u64, imm_ptr, x);
512 } else {
513 // This requires two instructions; a move imm as used above, followed by an indirect load using the register
514 // as the address and the register as the destination.
515 //
516 // This cannot be used if the lower three bits of the id are equal to four or five, as there
517 // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with
518 // this instruction.
519 const id3 = @truncate(u3, reg.id());
520 std.debug.assert(id3 != 4 and id3 != 5);
521
522 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
523 try self.genSetReg(src, arch, reg, MCValue{ .immediate = x });
524
525 // Now, the register contains the address of the value to load into it
526 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
527 // TODO: determine whether to allow other sized registers, and if so, handle them properly.
528 // This operation requires three bytes: REX 0x8B R/M
529 //
530 // For this operation, we want R/M mode *zero* (use register indirectly), and the two register
531 // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID.
532 //
533 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*
534 // register operands need to be marked as extended.
535 const REX = 0x48 | if (reg.isExtended()) @as(u8, 0b0101) else 0;
536 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
537 try self.code.appendSlice(&[_]u8{ REX, 0x8B, RM });
538 }
539 }
369540 },
370 else => return self.fail(src, "TODO implement genSetReg for x86_64 '{}'", .{@tagName(reg)}),
371541 },
372542 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
373543 }
......@@ -396,30 +566,22 @@ const Function = struct {
396566 }
397567 }
398568
399 fn genTypedValue(self: *Function, src: usize, typed_value: ir.TypedValue) !MCValue {
569 fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue {
570 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
571 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
572 const allocator = self.code.allocator;
400573 switch (typed_value.ty.zigTypeTag()) {
401574 .Pointer => {
402 const ptr_elem_type = typed_value.ty.elemType();
403 switch (ptr_elem_type.zigTypeTag()) {
404 .Array => {
405 // TODO more checks to make sure this can be emitted as a string literal
406 const bytes = try typed_value.val.toAllocatedBytes(self.code.allocator);
407 defer self.code.allocator.free(bytes);
408 const smaller_len = std.math.cast(u32, bytes.len) catch
409 return self.fail(src, "TODO handle a larger string constant", .{});
410
411 // Emit the string literal directly into the code; jump over it.
412 try self.genRelativeFwdJump(src, smaller_len);
413 const offset = self.code.items.len;
414 try self.code.appendSlice(bytes);
415 return MCValue{ .embedded_in_code = offset };
416 },
417 else => |t| return self.fail(src, "TODO implement emitTypedValue for pointer to '{}'", .{@tagName(t)}),
575 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
576 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
577 const decl = payload.decl;
578 const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;
579 return MCValue{ .memory = got_addr };
418580 }
581 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
419582 },
420583 .Int => {
421 const info = typed_value.ty.intInfo(self.module.target);
422 const ptr_bits = self.module.target.cpu.arch.ptrBitWidth();
584 const info = typed_value.ty.intInfo(self.target.*);
423585 if (info.bits > ptr_bits or info.signed) {
424586 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});
425587 }
......@@ -433,127 +595,19 @@ const Function = struct {
433595
434596 fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } {
435597 @setCold(true);
436 const msg = try std.fmt.allocPrint(self.errors.allocator, format, args);
437 {
438 errdefer self.errors.allocator.free(msg);
439 (try self.errors.addOne()).* = .{
440 .byte_offset = src,
441 .msg = msg,
442 };
443 }
598 assert(self.err_msg == null);
599 self.err_msg = try ErrorMsg.create(self.code.allocator, src, format, args);
444600 return error.CodegenFail;
445601 }
446602};
447603
604const x86_64 = @import("codegen/x86_64.zig");
605const x86 = @import("codegen/x86.zig");
606
448607fn Reg(comptime arch: Target.Cpu.Arch) type {
449608 return switch (arch) {
450 .i386 => enum {
451 eax,
452 ebx,
453 ecx,
454 edx,
455 ebp,
456 esp,
457 esi,
458 edi,
459
460 ax,
461 bx,
462 cx,
463 dx,
464 bp,
465 sp,
466 si,
467 di,
468
469 ah,
470 bh,
471 ch,
472 dh,
473
474 al,
475 bl,
476 cl,
477 dl,
478 },
479 .x86_64 => enum {
480 rax,
481 rbx,
482 rcx,
483 rdx,
484 rbp,
485 rsp,
486 rsi,
487 rdi,
488 r8,
489 r9,
490 r10,
491 r11,
492 r12,
493 r13,
494 r14,
495 r15,
496
497 eax,
498 ebx,
499 ecx,
500 edx,
501 ebp,
502 esp,
503 esi,
504 edi,
505 r8d,
506 r9d,
507 r10d,
508 r11d,
509 r12d,
510 r13d,
511 r14d,
512 r15d,
513
514 ax,
515 bx,
516 cx,
517 dx,
518 bp,
519 sp,
520 si,
521 di,
522 r8w,
523 r9w,
524 r10w,
525 r11w,
526 r12w,
527 r13w,
528 r14w,
529 r15w,
530
531 ah,
532 bh,
533 ch,
534 dh,
535 bph,
536 sph,
537 sih,
538 dih,
539
540 al,
541 bl,
542 cl,
543 dl,
544 bpl,
545 spl,
546 sil,
547 dil,
548 r8b,
549 r9b,
550 r10b,
551 r11b,
552 r12b,
553 r13b,
554 r14b,
555 r15b,
556 },
609 .i386 => x86.Register,
610 .x86_64 => x86_64.Register,
557611 else => @compileError("TODO add more register enums"),
558612 };
559613}
src-self-hosted/codegen/x86.zig created+30
......@@ -0,0 +1,30 @@
1// zig fmt: off
2pub const Register = enum(u8) {
3 // 0 through 7, 32-bit registers. id is int value
4 eax, ecx, edx, ebx, esp, ebp, esi, edi,
5
6 // 8-15, 16-bit registers. id is int value - 8.
7 ax, cx, dx, bx, sp, bp, si, di,
8
9 // 16-23, 8-bit registers. id is int value - 16.
10 al, bl, cl, dl, ah, ch, dh, bh,
11
12 /// Returns the bit-width of the register.
13 pub fn size(self: @This()) u7 {
14 return switch (@enumToInt(self)) {
15 0...7 => 32,
16 8...15 => 16,
17 16...23 => 8,
18 else => unreachable,
19 };
20 }
21
22 /// Returns the register's id. This is used in practically every opcode the
23 /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move
24 /// instruction, and is used in the R/M byte.
25 pub fn id(self: @This()) u3 {
26 return @truncate(u3, @enumToInt(self));
27 }
28};
29
30// zig fmt: on
src-self-hosted/codegen/x86_64.zig created+53
......@@ -0,0 +1,53 @@
1// zig fmt: off
2pub const Register = enum(u8) {
3 // 0 through 15, 64-bit registers. 8-15 are extended.
4 // id is just the int value.
5 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
6 r8, r9, r10, r11, r12, r13, r14, r15,
7
8 // 16 through 31, 32-bit registers. 24-31 are extended.
9 // id is int value - 16.
10 eax, ecx, edx, ebx, esp, ebp, esi, edi,
11 r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d,
12
13 // 32-47, 16-bit registers. 40-47 are extended.
14 // id is int value - 32.
15 ax, cx, dx, bx, sp, bp, si, di,
16 r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w,
17
18 // 48-63, 8-bit registers. 56-63 are extended.
19 // id is int value - 48.
20 al, bl, cl, dl, ah, ch, dh, bh,
21 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
22
23 /// Returns the bit-width of the register.
24 pub fn size(self: @This()) u7 {
25 return switch (@enumToInt(self)) {
26 0...15 => 64,
27 16...31 => 32,
28 32...47 => 16,
29 48...64 => 8,
30 else => unreachable,
31 };
32 }
33
34 /// Returns whether the register is *extended*. Extended registers are the
35 /// new registers added with amd64, r8 through r15. This also includes any
36 /// other variant of access to those registers, such as r8b, r15d, and so
37 /// on. This is needed because access to these registers requires special
38 /// handling via the REX prefix, via the B or R bits, depending on context.
39 pub fn isExtended(self: @This()) bool {
40 return @enumToInt(self) & 0x08 != 0;
41 }
42
43 /// This returns the 4-bit register ID, which is used in practically every
44 /// opcode. Note that bit 3 (the highest bit) is *never* used directly in
45 /// an instruction (@see isExtended), and requires special handling. The
46 /// lower three bits are often embedded directly in instructions (such as
47 /// the B8 variant of moves), or used in R/M bytes.
48 pub fn id(self: @This()) u4 {
49 return @truncate(u4, @enumToInt(self));
50 }
51};
52
53// zig fmt: on
src-self-hosted/compilation.zig deleted-1434
......@@ -1,1434 +0,0 @@
1const std = @import("std");
2const io = std.io;
3const mem = std.mem;
4const Allocator = mem.Allocator;
5const ArrayListSentineled = std.ArrayListSentineled;
6const llvm = @import("llvm.zig");
7const c = @import("c.zig");
8const builtin = std.builtin;
9const Target = std.Target;
10const warn = std.debug.warn;
11const Token = std.zig.Token;
12const ArrayList = std.ArrayList;
13const errmsg = @import("errmsg.zig");
14const ast = std.zig.ast;
15const event = std.event;
16const assert = std.debug.assert;
17const AtomicRmwOp = builtin.AtomicRmwOp;
18const AtomicOrder = builtin.AtomicOrder;
19const Scope = @import("scope.zig").Scope;
20const Decl = @import("decl.zig").Decl;
21const ir = @import("ir.zig");
22const Visib = @import("visib.zig").Visib;
23const Value = @import("value.zig").Value;
24const Type = Value.Type;
25const Span = errmsg.Span;
26const Msg = errmsg.Msg;
27const codegen = @import("codegen.zig");
28const Package = @import("package.zig").Package;
29const link = @import("link.zig").link;
30const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
31const CInt = @import("c_int.zig").CInt;
32const fs = std.fs;
33const util = @import("util.zig");
34
35const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
36
37/// Data that is local to the event loop.
38pub const ZigCompiler = struct {
39 llvm_handle_pool: std.atomic.Stack(*llvm.Context),
40 lld_lock: event.Lock,
41 allocator: *Allocator,
42
43 /// TODO pool these so that it doesn't have to lock
44 prng: event.Locked(std.rand.DefaultPrng),
45
46 native_libc: event.Future(LibCInstallation),
47
48 var lazy_init_targets = std.once(util.initializeAllTargets);
49
50 pub fn init(allocator: *Allocator) !ZigCompiler {
51 lazy_init_targets.call();
52
53 var seed_bytes: [@sizeOf(u64)]u8 = undefined;
54 try std.crypto.randomBytes(seed_bytes[0..]);
55 const seed = mem.readIntNative(u64, &seed_bytes);
56
57 return ZigCompiler{
58 .allocator = allocator,
59 .lld_lock = event.Lock.init(),
60 .llvm_handle_pool = std.atomic.Stack(*llvm.Context).init(),
61 .prng = event.Locked(std.rand.DefaultPrng).init(std.rand.DefaultPrng.init(seed)),
62 .native_libc = event.Future(LibCInstallation).init(),
63 };
64 }
65
66 /// Must be called only after EventLoop.run completes.
67 fn deinit(self: *ZigCompiler) void {
68 self.lld_lock.deinit();
69 while (self.llvm_handle_pool.pop()) |node| {
70 llvm.ContextDispose(node.data);
71 self.allocator.destroy(node);
72 }
73 }
74
75 /// Gets an exclusive handle on any LlvmContext.
76 /// Caller must release the handle when done.
77 pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle {
78 if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
79
80 const context_ref = llvm.ContextCreate() orelse return error.OutOfMemory;
81 errdefer llvm.ContextDispose(context_ref);
82
83 const node = try self.allocator.create(std.atomic.Stack(*llvm.Context).Node);
84 node.* = std.atomic.Stack(*llvm.Context).Node{
85 .next = undefined,
86 .data = context_ref,
87 };
88 errdefer self.allocator.destroy(node);
89
90 return LlvmHandle{ .node = node };
91 }
92
93 pub fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
94 if (self.native_libc.start()) |ptr| return ptr;
95 self.native_libc.data = try LibCInstallation.findNative(.{ .allocator = self.allocator });
96 self.native_libc.resolve();
97 return &self.native_libc.data;
98 }
99
100 /// Must be called only once, ever. Sets global state.
101 pub fn setLlvmArgv(allocator: *Allocator, llvm_argv: []const []const u8) !void {
102 if (llvm_argv.len != 0) {
103 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(allocator, &[_][]const []const u8{
104 &[_][]const u8{"zig (LLVM option parsing)"},
105 llvm_argv,
106 });
107 defer c_compatible_args.deinit();
108 c.ZigLLVMParseCommandLineOptions(llvm_argv.len + 1, c_compatible_args.ptr);
109 }
110 }
111};
112
113pub const LlvmHandle = struct {
114 node: *std.atomic.Stack(*llvm.Context).Node,
115
116 pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void {
117 zig_compiler.llvm_handle_pool.push(self.node);
118 }
119};
120
121pub const Compilation = struct {
122 zig_compiler: *ZigCompiler,
123 name: ArrayListSentineled(u8, 0),
124 llvm_triple: ArrayListSentineled(u8, 0),
125 root_src_path: ?[]const u8,
126 target: std.Target,
127 llvm_target: *llvm.Target,
128 build_mode: builtin.Mode,
129 zig_lib_dir: []const u8,
130 zig_std_dir: []const u8,
131
132 /// lazily created when we need it
133 tmp_dir: event.Future(BuildError![]u8) = event.Future(BuildError![]u8).init(),
134
135 version: builtin.Version = builtin.Version{ .major = 0, .minor = 0, .patch = 0 },
136
137 linker_script: ?[]const u8 = null,
138 out_h_path: ?[]const u8 = null,
139
140 is_test: bool = false,
141 strip: bool = false,
142 is_static: bool,
143 linker_rdynamic: bool = false,
144
145 clang_argv: []const []const u8 = &[_][]const u8{},
146 assembly_files: []const []const u8 = &[_][]const u8{},
147
148 /// paths that are explicitly provided by the user to link against
149 link_objects: []const []const u8 = &[_][]const u8{},
150
151 /// functions that have their own objects that we need to link
152 /// it uses an optional pointer so that tombstone removals are possible
153 fn_link_set: event.Locked(FnLinkSet) = event.Locked(FnLinkSet).init(FnLinkSet.init()),
154
155 pub const FnLinkSet = std.TailQueue(?*Value.Fn);
156
157 link_libs_list: ArrayList(*LinkLib),
158 libc_link_lib: ?*LinkLib = null,
159
160 err_color: errmsg.Color = .Auto,
161
162 verbose_tokenize: bool = false,
163 verbose_ast_tree: bool = false,
164 verbose_ast_fmt: bool = false,
165 verbose_cimport: bool = false,
166 verbose_ir: bool = false,
167 verbose_llvm_ir: bool = false,
168 verbose_link: bool = false,
169
170 link_eh_frame_hdr: bool = false,
171
172 darwin_version_min: DarwinVersionMin = .None,
173
174 test_filters: []const []const u8 = &[_][]const u8{},
175 test_name_prefix: ?[]const u8 = null,
176
177 emit_bin: bool = true,
178 emit_asm: bool = false,
179 emit_llvm_ir: bool = false,
180 emit_h: bool = false,
181
182 kind: Kind,
183
184 events: *event.Channel(Event),
185
186 exported_symbol_names: event.Locked(Decl.Table),
187
188 /// Before code generation starts, must wait on this group to make sure
189 /// the build is complete.
190 prelink_group: event.Group(BuildError!void),
191
192 compile_errors: event.Locked(CompileErrList),
193
194 meta_type: *Type.MetaType,
195 void_type: *Type.Void,
196 bool_type: *Type.Bool,
197 noreturn_type: *Type.NoReturn,
198 comptime_int_type: *Type.ComptimeInt,
199 u8_type: *Type.Int,
200
201 void_value: *Value.Void,
202 true_value: *Value.Bool,
203 false_value: *Value.Bool,
204 noreturn_value: *Value.NoReturn,
205
206 target_machine: *llvm.TargetMachine,
207 target_data_ref: *llvm.TargetData,
208 target_layout_str: [*:0]u8,
209 target_ptr_bits: u32,
210
211 /// for allocating things which have the same lifetime as this Compilation
212 arena_allocator: std.heap.ArenaAllocator,
213
214 root_package: *Package,
215 std_package: *Package,
216
217 override_libc: ?*LibCInstallation = null,
218
219 /// need to wait on this group before deinitializing
220 deinit_group: event.Group(void),
221
222 destroy_frame: *@Frame(createAsync),
223 main_loop_frame: *@Frame(Compilation.mainLoop),
224 main_loop_future: event.Future(void) = event.Future(void).init(),
225
226 have_err_ret_tracing: bool = false,
227
228 /// not locked because it is read-only
229 primitive_type_table: TypeTable,
230
231 int_type_table: event.Locked(IntTypeTable),
232 array_type_table: event.Locked(ArrayTypeTable),
233 ptr_type_table: event.Locked(PtrTypeTable),
234 fn_type_table: event.Locked(FnTypeTable),
235
236 c_int_types: [CInt.list.len]*Type.Int,
237
238 fs_watch: *fs.Watch(*Scope.Root),
239
240 cancelled: bool = false,
241
242 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
243 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
244 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
245 const FnTypeTable = std.HashMap(*const Type.Fn.Key, *Type.Fn, Type.Fn.Key.hash, Type.Fn.Key.eql);
246 const TypeTable = std.StringHashMap(*Type);
247
248 const CompileErrList = std.ArrayList(*Msg);
249
250 // TODO handle some of these earlier and report them in a way other than error codes
251 pub const BuildError = error{
252 OutOfMemory,
253 EndOfStream,
254 IsDir,
255 Unexpected,
256 SystemResources,
257 SharingViolation,
258 PathAlreadyExists,
259 FileNotFound,
260 AccessDenied,
261 PipeBusy,
262 FileTooBig,
263 SymLinkLoop,
264 ProcessFdQuotaExceeded,
265 NameTooLong,
266 SystemFdQuotaExceeded,
267 NoDevice,
268 NoSpaceLeft,
269 NotDir,
270 FileSystem,
271 OperationAborted,
272 IoPending,
273 BrokenPipe,
274 WouldBlock,
275 FileClosed,
276 DestinationAddressRequired,
277 DiskQuota,
278 InputOutput,
279 NoStdHandles,
280 Overflow,
281 NotSupported,
282 BufferTooSmall,
283 Unimplemented, // TODO remove this one
284 SemanticAnalysisFailed, // TODO remove this one
285 ReadOnlyFileSystem,
286 LinkQuotaExceeded,
287 EnvironmentVariableNotFound,
288 AppDataDirUnavailable,
289 LinkFailed,
290 LibCRequiredButNotProvidedOrFound,
291 LibCMissingDynamicLinker,
292 InvalidDarwinVersionString,
293 UnsupportedLinkArchitecture,
294 UserResourceLimitReached,
295 InvalidUtf8,
296 BadPathName,
297 DeviceBusy,
298 CurrentWorkingDirectoryUnlinked,
299 };
300
301 pub const Event = union(enum) {
302 Ok,
303 Error: BuildError,
304 Fail: []*Msg,
305 };
306
307 pub const DarwinVersionMin = union(enum) {
308 None,
309 MacOS: []const u8,
310 Ios: []const u8,
311 };
312
313 pub const Kind = enum {
314 Exe,
315 Lib,
316 Obj,
317 };
318
319 pub const LinkLib = struct {
320 name: []const u8,
321 path: ?[]const u8,
322
323 /// the list of symbols we depend on from this lib
324 symbols: ArrayList([]u8),
325 provided_explicitly: bool,
326 };
327
328 pub const Emit = enum {
329 Binary,
330 Assembly,
331 LlvmIr,
332 };
333
334 pub fn create(
335 zig_compiler: *ZigCompiler,
336 name: []const u8,
337 root_src_path: ?[]const u8,
338 target: std.zig.CrossTarget,
339 kind: Kind,
340 build_mode: builtin.Mode,
341 is_static: bool,
342 zig_lib_dir: []const u8,
343 ) !*Compilation {
344 var optional_comp: ?*Compilation = null;
345 var frame = try zig_compiler.allocator.create(@Frame(createAsync));
346 errdefer zig_compiler.allocator.destroy(frame);
347 frame.* = async createAsync(
348 &optional_comp,
349 zig_compiler,
350 name,
351 root_src_path,
352 target,
353 kind,
354 build_mode,
355 is_static,
356 zig_lib_dir,
357 );
358 // TODO causes segfault
359 // return optional_comp orelse if (await frame) |_| unreachable else |err| err;
360 if (optional_comp) |comp| {
361 return comp;
362 } else if (await frame) |_| unreachable else |err| return err;
363 }
364
365 async fn createAsync(
366 out_comp: *?*Compilation,
367 zig_compiler: *ZigCompiler,
368 name: []const u8,
369 root_src_path: ?[]const u8,
370 cross_target: std.zig.CrossTarget,
371 kind: Kind,
372 build_mode: builtin.Mode,
373 is_static: bool,
374 zig_lib_dir: []const u8,
375 ) !void {
376 const allocator = zig_compiler.allocator;
377
378 // TODO merge this line with stage2.zig crossTargetToTarget
379 const target_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target);
380 const target = target_info.target;
381
382 var comp = Compilation{
383 .arena_allocator = std.heap.ArenaAllocator.init(allocator),
384 .zig_compiler = zig_compiler,
385 .events = undefined,
386 .root_src_path = root_src_path,
387 .target = target,
388 .llvm_target = undefined,
389 .kind = kind,
390 .build_mode = build_mode,
391 .zig_lib_dir = zig_lib_dir,
392 .zig_std_dir = undefined,
393 .destroy_frame = @frame(),
394 .main_loop_frame = undefined,
395
396 .name = undefined,
397 .llvm_triple = undefined,
398 .is_static = is_static,
399 .link_libs_list = undefined,
400 .exported_symbol_names = event.Locked(Decl.Table).init(Decl.Table.init(allocator)),
401 .prelink_group = event.Group(BuildError!void).init(allocator),
402 .deinit_group = event.Group(void).init(allocator),
403 .compile_errors = event.Locked(CompileErrList).init(CompileErrList.init(allocator)),
404 .int_type_table = event.Locked(IntTypeTable).init(IntTypeTable.init(allocator)),
405 .array_type_table = event.Locked(ArrayTypeTable).init(ArrayTypeTable.init(allocator)),
406 .ptr_type_table = event.Locked(PtrTypeTable).init(PtrTypeTable.init(allocator)),
407 .fn_type_table = event.Locked(FnTypeTable).init(FnTypeTable.init(allocator)),
408 .c_int_types = undefined,
409
410 .meta_type = undefined,
411 .void_type = undefined,
412 .void_value = undefined,
413 .bool_type = undefined,
414 .true_value = undefined,
415 .false_value = undefined,
416 .noreturn_type = undefined,
417 .noreturn_value = undefined,
418 .comptime_int_type = undefined,
419 .u8_type = undefined,
420
421 .target_machine = undefined,
422 .target_data_ref = undefined,
423 .target_layout_str = undefined,
424 .target_ptr_bits = target.cpu.arch.ptrBitWidth(),
425
426 .root_package = undefined,
427 .std_package = undefined,
428
429 .primitive_type_table = undefined,
430
431 .fs_watch = undefined,
432 };
433 comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
434 comp.primitive_type_table = TypeTable.init(comp.arena());
435
436 defer {
437 comp.int_type_table.private_data.deinit();
438 comp.array_type_table.private_data.deinit();
439 comp.ptr_type_table.private_data.deinit();
440 comp.fn_type_table.private_data.deinit();
441 comp.arena_allocator.deinit();
442 }
443
444 comp.name = try ArrayListSentineled(u8, 0).init(comp.arena(), name);
445 comp.llvm_triple = try util.getLLVMTriple(comp.arena(), target);
446 comp.llvm_target = try util.llvmTargetFromTriple(comp.llvm_triple);
447 comp.zig_std_dir = try fs.path.join(comp.arena(), &[_][]const u8{ zig_lib_dir, "std" });
448
449 const opt_level = switch (build_mode) {
450 .Debug => llvm.CodeGenLevelNone,
451 else => llvm.CodeGenLevelAggressive,
452 };
453
454 const reloc_mode = if (is_static) llvm.RelocStatic else llvm.RelocPIC;
455
456 var target_specific_cpu_args: ?[*:0]u8 = null;
457 var target_specific_cpu_features: ?[*:0]u8 = null;
458 defer llvm.DisposeMessage(target_specific_cpu_args);
459 defer llvm.DisposeMessage(target_specific_cpu_features);
460
461 // TODO detect native CPU & features here
462
463 comp.target_machine = llvm.CreateTargetMachine(
464 comp.llvm_target,
465 comp.llvm_triple.span(),
466 target_specific_cpu_args orelse "",
467 target_specific_cpu_features orelse "",
468 opt_level,
469 reloc_mode,
470 llvm.CodeModelDefault,
471 false, // TODO: add -ffunction-sections option
472 ) orelse return error.OutOfMemory;
473 defer llvm.DisposeTargetMachine(comp.target_machine);
474
475 comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory;
476 defer llvm.DisposeTargetData(comp.target_data_ref);
477
478 comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory;
479 defer llvm.DisposeMessage(comp.target_layout_str);
480
481 comp.events = try allocator.create(event.Channel(Event));
482 defer allocator.destroy(comp.events);
483
484 comp.events.init(&[0]Event{});
485 defer comp.events.deinit();
486
487 if (root_src_path) |root_src| {
488 const dirname = fs.path.dirname(root_src) orelse ".";
489 const basename = fs.path.basename(root_src);
490
491 comp.root_package = try Package.create(comp.arena(), dirname, basename);
492 comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "std.zig");
493 try comp.root_package.add("std", comp.std_package);
494 } else {
495 comp.root_package = try Package.create(comp.arena(), ".", "");
496 }
497
498 comp.fs_watch = try fs.Watch(*Scope.Root).init(allocator, 16);
499 defer comp.fs_watch.deinit();
500
501 try comp.initTypes();
502 defer comp.primitive_type_table.deinit();
503
504 comp.main_loop_frame = try allocator.create(@Frame(mainLoop));
505 defer allocator.destroy(comp.main_loop_frame);
506
507 comp.main_loop_frame.* = async comp.mainLoop();
508 // Set this to indicate that initialization completed successfully.
509 // from here on out we must not return an error.
510 // This must occur before the first suspend/await.
511 out_comp.* = &comp;
512 // This suspend is resumed by destroy()
513 suspend;
514 // From here on is cleanup.
515
516 comp.deinit_group.wait();
517
518 if (comp.tmp_dir.getOrNull()) |tmp_dir_result|
519 if (tmp_dir_result.*) |tmp_dir| {
520 fs.cwd().deleteTree(tmp_dir) catch {};
521 } else |_| {};
522 }
523
524 /// it does ref the result because it could be an arbitrary integer size
525 pub fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
526 if (name.len >= 2) {
527 switch (name[0]) {
528 'i', 'u' => blk: {
529 for (name[1..]) |byte|
530 switch (byte) {
531 '0'...'9' => {},
532 else => break :blk,
533 };
534 const is_signed = name[0] == 'i';
535 const bit_count = std.fmt.parseUnsigned(u32, name[1..], 10) catch |err| switch (err) {
536 error.Overflow => return error.Overflow,
537 error.InvalidCharacter => unreachable, // we just checked the characters above
538 };
539 const int_type = try Type.Int.get(comp, Type.Int.Key{
540 .bit_count = bit_count,
541 .is_signed = is_signed,
542 });
543 errdefer int_type.base.base.deref();
544 return &int_type.base;
545 },
546 else => {},
547 }
548 }
549
550 if (comp.primitive_type_table.get(name)) |entry| {
551 entry.value.base.ref();
552 return entry.value;
553 }
554
555 return null;
556 }
557
558 fn initTypes(comp: *Compilation) !void {
559 comp.meta_type = try comp.arena().create(Type.MetaType);
560 comp.meta_type.* = Type.MetaType{
561 .base = Type{
562 .name = "type",
563 .base = Value{
564 .id = .Type,
565 .typ = undefined,
566 .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice
567 },
568 .id = .Type,
569 .abi_alignment = Type.AbiAlignment.init(),
570 },
571 .value = undefined,
572 };
573 comp.meta_type.value = &comp.meta_type.base;
574 comp.meta_type.base.base.typ = &comp.meta_type.base;
575 assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null);
576
577 comp.void_type = try comp.arena().create(Type.Void);
578 comp.void_type.* = Type.Void{
579 .base = Type{
580 .name = "void",
581 .base = Value{
582 .id = .Type,
583 .typ = &Type.MetaType.get(comp).base,
584 .ref_count = std.atomic.Int(usize).init(1),
585 },
586 .id = .Void,
587 .abi_alignment = Type.AbiAlignment.init(),
588 },
589 };
590 assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null);
591
592 comp.noreturn_type = try comp.arena().create(Type.NoReturn);
593 comp.noreturn_type.* = Type.NoReturn{
594 .base = Type{
595 .name = "noreturn",
596 .base = Value{
597 .id = .Type,
598 .typ = &Type.MetaType.get(comp).base,
599 .ref_count = std.atomic.Int(usize).init(1),
600 },
601 .id = .NoReturn,
602 .abi_alignment = Type.AbiAlignment.init(),
603 },
604 };
605 assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null);
606
607 comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt);
608 comp.comptime_int_type.* = Type.ComptimeInt{
609 .base = Type{
610 .name = "comptime_int",
611 .base = Value{
612 .id = .Type,
613 .typ = &Type.MetaType.get(comp).base,
614 .ref_count = std.atomic.Int(usize).init(1),
615 },
616 .id = .ComptimeInt,
617 .abi_alignment = Type.AbiAlignment.init(),
618 },
619 };
620 assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null);
621
622 comp.bool_type = try comp.arena().create(Type.Bool);
623 comp.bool_type.* = Type.Bool{
624 .base = Type{
625 .name = "bool",
626 .base = Value{
627 .id = .Type,
628 .typ = &Type.MetaType.get(comp).base,
629 .ref_count = std.atomic.Int(usize).init(1),
630 },
631 .id = .Bool,
632 .abi_alignment = Type.AbiAlignment.init(),
633 },
634 };
635 assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null);
636
637 comp.void_value = try comp.arena().create(Value.Void);
638 comp.void_value.* = Value.Void{
639 .base = Value{
640 .id = .Void,
641 .typ = &Type.Void.get(comp).base,
642 .ref_count = std.atomic.Int(usize).init(1),
643 },
644 };
645
646 comp.true_value = try comp.arena().create(Value.Bool);
647 comp.true_value.* = Value.Bool{
648 .base = Value{
649 .id = .Bool,
650 .typ = &Type.Bool.get(comp).base,
651 .ref_count = std.atomic.Int(usize).init(1),
652 },
653 .x = true,
654 };
655
656 comp.false_value = try comp.arena().create(Value.Bool);
657 comp.false_value.* = Value.Bool{
658 .base = Value{
659 .id = .Bool,
660 .typ = &Type.Bool.get(comp).base,
661 .ref_count = std.atomic.Int(usize).init(1),
662 },
663 .x = false,
664 };
665
666 comp.noreturn_value = try comp.arena().create(Value.NoReturn);
667 comp.noreturn_value.* = Value.NoReturn{
668 .base = Value{
669 .id = .NoReturn,
670 .typ = &Type.NoReturn.get(comp).base,
671 .ref_count = std.atomic.Int(usize).init(1),
672 },
673 };
674
675 for (CInt.list) |cint, i| {
676 const c_int_type = try comp.arena().create(Type.Int);
677 c_int_type.* = Type.Int{
678 .base = Type{
679 .name = cint.zig_name,
680 .base = Value{
681 .id = .Type,
682 .typ = &Type.MetaType.get(comp).base,
683 .ref_count = std.atomic.Int(usize).init(1),
684 },
685 .id = .Int,
686 .abi_alignment = Type.AbiAlignment.init(),
687 },
688 .key = Type.Int.Key{
689 .is_signed = cint.is_signed,
690 .bit_count = cint.sizeInBits(comp.target),
691 },
692 .garbage_node = undefined,
693 };
694 comp.c_int_types[i] = c_int_type;
695 assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null);
696 }
697 comp.u8_type = try comp.arena().create(Type.Int);
698 comp.u8_type.* = Type.Int{
699 .base = Type{
700 .name = "u8",
701 .base = Value{
702 .id = .Type,
703 .typ = &Type.MetaType.get(comp).base,
704 .ref_count = std.atomic.Int(usize).init(1),
705 },
706 .id = .Int,
707 .abi_alignment = Type.AbiAlignment.init(),
708 },
709 .key = Type.Int.Key{
710 .is_signed = false,
711 .bit_count = 8,
712 },
713 .garbage_node = undefined,
714 };
715 assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
716 }
717
718 pub fn destroy(self: *Compilation) void {
719 const allocator = self.gpa();
720 self.cancelled = true;
721 await self.main_loop_frame;
722 resume self.destroy_frame;
723 allocator.destroy(self.destroy_frame);
724 }
725
726 fn start(self: *Compilation) void {
727 self.main_loop_future.resolve();
728 }
729
730 async fn mainLoop(self: *Compilation) void {
731 // wait until start() is called
732 _ = self.main_loop_future.get();
733
734 var build_result = self.initialCompile();
735
736 while (!self.cancelled) {
737 const link_result = if (build_result) blk: {
738 break :blk self.maybeLink();
739 } else |err| err;
740 // this makes a handy error return trace and stack trace in debug mode
741 if (std.debug.runtime_safety) {
742 link_result catch unreachable;
743 }
744
745 const compile_errors = blk: {
746 const held = self.compile_errors.acquire();
747 defer held.release();
748 break :blk held.value.toOwnedSlice();
749 };
750
751 if (link_result) |_| {
752 if (compile_errors.len == 0) {
753 self.events.put(Event.Ok);
754 } else {
755 self.events.put(Event{ .Fail = compile_errors });
756 }
757 } else |err| {
758 // if there's an error then the compile errors have dangling references
759 self.gpa().free(compile_errors);
760
761 self.events.put(Event{ .Error = err });
762 }
763
764 // First, get an item from the watch channel, waiting on the channel.
765 var group = event.Group(BuildError!void).init(self.gpa());
766 {
767 const ev = (self.fs_watch.channel.get()) catch |err| {
768 build_result = err;
769 continue;
770 };
771 const root_scope = ev.data;
772 group.call(rebuildFile, .{ self, root_scope }) catch |err| {
773 build_result = err;
774 continue;
775 };
776 }
777 // Next, get all the items from the channel that are buffered up.
778 while (self.fs_watch.channel.getOrNull()) |ev_or_err| {
779 if (ev_or_err) |ev| {
780 const root_scope = ev.data;
781 group.call(rebuildFile, .{ self, root_scope }) catch |err| {
782 build_result = err;
783 continue;
784 };
785 } else |err| {
786 build_result = err;
787 continue;
788 }
789 }
790 build_result = group.wait();
791 }
792 }
793
794 async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) BuildError!void {
795 const tree_scope = blk: {
796 const source_code = fs.cwd().readFileAlloc(
797 self.gpa(),
798 root_scope.realpath,
799 max_src_size,
800 ) catch |err| {
801 try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", .{@errorName(err)});
802 return;
803 };
804 errdefer self.gpa().free(source_code);
805
806 const tree = try std.zig.parse(self.gpa(), source_code);
807 errdefer {
808 tree.deinit();
809 }
810
811 break :blk try Scope.AstTree.create(self, tree, root_scope);
812 };
813 defer tree_scope.base.deref(self);
814
815 var error_it = tree_scope.tree.errors.iterator(0);
816 while (error_it.next()) |parse_error| {
817 const msg = try Msg.createFromParseErrorAndScope(self, tree_scope, parse_error);
818 errdefer msg.destroy();
819
820 try self.addCompileErrorAsync(msg);
821 }
822 if (tree_scope.tree.errors.len != 0) {
823 return;
824 }
825
826 const locked_table = root_scope.decls.table.acquireWrite();
827 defer locked_table.release();
828
829 var decl_group = event.Group(BuildError!void).init(self.gpa());
830
831 try self.rebuildChangedDecls(
832 &decl_group,
833 locked_table.value,
834 root_scope.decls,
835 &tree_scope.tree.root_node.decls,
836 tree_scope,
837 );
838
839 try decl_group.wait();
840 }
841
842 fn rebuildChangedDecls(
843 self: *Compilation,
844 group: *event.Group(BuildError!void),
845 locked_table: *Decl.Table,
846 decl_scope: *Scope.Decls,
847 ast_decls: *ast.Node.Root.DeclList,
848 tree_scope: *Scope.AstTree,
849 ) !void {
850 var existing_decls = try locked_table.clone();
851 defer existing_decls.deinit();
852
853 var ast_it = ast_decls.iterator(0);
854 while (ast_it.next()) |decl_ptr| {
855 const decl = decl_ptr.*;
856 switch (decl.id) {
857 .Comptime => {
858 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
859
860 // TODO connect existing comptime decls to updated source files
861
862 try self.prelink_group.call(addCompTimeBlock, .{ self, tree_scope, &decl_scope.base, comptime_node });
863 },
864 .VarDecl => @panic("TODO"),
865 .FnProto => {
866 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
867
868 const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else {
869 try self.addCompileError(tree_scope, Span{
870 .first = fn_proto.fn_token,
871 .last = fn_proto.fn_token + 1,
872 }, "missing function name", .{});
873 continue;
874 };
875
876 if (existing_decls.remove(name)) |entry| {
877 // compare new code to existing
878 if (entry.value.cast(Decl.Fn)) |existing_fn_decl| {
879 // Just compare the old bytes to the new bytes of the top level decl.
880 // Even if the AST is technically the same, we want error messages to display
881 // from the most recent source.
882 const old_decl_src = existing_fn_decl.base.tree_scope.tree.getNodeSource(
883 &existing_fn_decl.fn_proto.base,
884 );
885 const new_decl_src = tree_scope.tree.getNodeSource(&fn_proto.base);
886 if (mem.eql(u8, old_decl_src, new_decl_src)) {
887 // it's the same, we can skip this decl
888 continue;
889 } else {
890 @panic("TODO decl changed implementation");
891 // Add the new thing before dereferencing the old thing. This way we don't end
892 // up pointlessly re-creating things we end up using in the new thing.
893 }
894 } else {
895 @panic("TODO decl changed kind");
896 }
897 } else {
898 // add new decl
899 const fn_decl = try self.gpa().create(Decl.Fn);
900 fn_decl.* = Decl.Fn{
901 .base = Decl{
902 .id = Decl.Id.Fn,
903 .name = name,
904 .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
905 .resolution = event.Future(BuildError!void).init(),
906 .parent_scope = &decl_scope.base,
907 .tree_scope = tree_scope,
908 },
909 .value = .Unresolved,
910 .fn_proto = fn_proto,
911 };
912 tree_scope.base.ref();
913 errdefer self.gpa().destroy(fn_decl);
914
915 try group.call(addTopLevelDecl, .{ self, &fn_decl.base, locked_table });
916 }
917 },
918 .TestDecl => @panic("TODO"),
919 else => unreachable,
920 }
921 }
922
923 var existing_decl_it = existing_decls.iterator();
924 while (existing_decl_it.next()) |entry| {
925 // this decl was deleted
926 const existing_decl = entry.value;
927 @panic("TODO handle decl deletion");
928 }
929 }
930
931 fn initialCompile(self: *Compilation) !void {
932 if (self.root_src_path) |root_src_path| {
933 const root_scope = blk: {
934 // TODO async/await fs.realpath
935 const root_src_real_path = fs.realpathAlloc(self.gpa(), root_src_path) catch |err| {
936 try self.addCompileErrorCli(root_src_path, "unable to open: {}", .{@errorName(err)});
937 return;
938 };
939 errdefer self.gpa().free(root_src_real_path);
940
941 break :blk try Scope.Root.create(self, root_src_real_path);
942 };
943 defer root_scope.base.deref(self);
944
945 // assert((try self.fs_watch.addFile(root_scope.realpath, root_scope)) == null);
946 try self.rebuildFile(root_scope);
947 }
948 }
949
950 fn maybeLink(self: *Compilation) !void {
951 (self.prelink_group.wait()) catch |err| switch (err) {
952 error.SemanticAnalysisFailed => {},
953 else => return err,
954 };
955
956 const any_prelink_errors = blk: {
957 const compile_errors = self.compile_errors.acquire();
958 defer compile_errors.release();
959
960 break :blk compile_errors.value.len != 0;
961 };
962
963 if (!any_prelink_errors) {
964 try link(self);
965 }
966 }
967
968 /// caller takes ownership of resulting Code
969 async fn genAndAnalyzeCode(
970 comp: *Compilation,
971 tree_scope: *Scope.AstTree,
972 scope: *Scope,
973 node: *ast.Node,
974 expected_type: ?*Type,
975 ) !*ir.Code {
976 const unanalyzed_code = try ir.gen(
977 comp,
978 node,
979 tree_scope,
980 scope,
981 );
982 defer unanalyzed_code.destroy(comp.gpa());
983
984 if (comp.verbose_ir) {
985 std.debug.warn("unanalyzed:\n", .{});
986 unanalyzed_code.dump();
987 }
988
989 const analyzed_code = try ir.analyze(
990 comp,
991 unanalyzed_code,
992 expected_type,
993 );
994 errdefer analyzed_code.destroy(comp.gpa());
995
996 if (comp.verbose_ir) {
997 std.debug.warn("analyzed:\n", .{});
998 analyzed_code.dump();
999 }
1000
1001 return analyzed_code;
1002 }
1003
1004 async fn addCompTimeBlock(
1005 comp: *Compilation,
1006 tree_scope: *Scope.AstTree,
1007 scope: *Scope,
1008 comptime_node: *ast.Node.Comptime,
1009 ) BuildError!void {
1010 const void_type = Type.Void.get(comp);
1011 defer void_type.base.base.deref(comp);
1012
1013 const analyzed_code = genAndAnalyzeCode(
1014 comp,
1015 tree_scope,
1016 scope,
1017 comptime_node.expr,
1018 &void_type.base,
1019 ) catch |err| switch (err) {
1020 // This poison value should not cause the errdefers to run. It simply means
1021 // that comp.compile_errors is populated.
1022 error.SemanticAnalysisFailed => return {},
1023 else => return err,
1024 };
1025 analyzed_code.destroy(comp.gpa());
1026 }
1027
1028 async fn addTopLevelDecl(
1029 self: *Compilation,
1030 decl: *Decl,
1031 locked_table: *Decl.Table,
1032 ) BuildError!void {
1033 const is_export = decl.isExported(decl.tree_scope.tree);
1034
1035 if (is_export) {
1036 try self.prelink_group.call(verifyUniqueSymbol, .{ self, decl });
1037 try self.prelink_group.call(resolveDecl, .{ self, decl });
1038 }
1039
1040 const gop = try locked_table.getOrPut(decl.name);
1041 if (gop.found_existing) {
1042 try self.addCompileError(decl.tree_scope, decl.getSpan(), "redefinition of '{}'", .{decl.name});
1043 // TODO note: other definition here
1044 } else {
1045 gop.kv.value = decl;
1046 }
1047 }
1048
1049 fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: var) !void {
1050 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
1051 errdefer self.gpa().free(text);
1052
1053 const msg = try Msg.createFromScope(self, tree_scope, span, text);
1054 errdefer msg.destroy();
1055
1056 try self.prelink_group.call(addCompileErrorAsync, .{ self, msg });
1057 }
1058
1059 fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: var) !void {
1060 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
1061 errdefer self.gpa().free(text);
1062
1063 const msg = try Msg.createFromCli(self, realpath, text);
1064 errdefer msg.destroy();
1065
1066 try self.prelink_group.call(addCompileErrorAsync, .{ self, msg });
1067 }
1068
1069 async fn addCompileErrorAsync(
1070 self: *Compilation,
1071 msg: *Msg,
1072 ) BuildError!void {
1073 errdefer msg.destroy();
1074
1075 const compile_errors = self.compile_errors.acquire();
1076 defer compile_errors.release();
1077
1078 try compile_errors.value.append(msg);
1079 }
1080
1081 async fn verifyUniqueSymbol(self: *Compilation, decl: *Decl) BuildError!void {
1082 const exported_symbol_names = self.exported_symbol_names.acquire();
1083 defer exported_symbol_names.release();
1084
1085 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
1086 try self.addCompileError(decl.tree_scope, decl.getSpan(), "exported symbol collision: '{}'", .{
1087 decl.name,
1088 });
1089 // TODO add error note showing location of other symbol
1090 }
1091 }
1092
1093 pub fn haveLibC(self: *Compilation) bool {
1094 return self.libc_link_lib != null;
1095 }
1096
1097 pub fn addLinkLib(self: *Compilation, name: []const u8, provided_explicitly: bool) !*LinkLib {
1098 const is_libc = mem.eql(u8, name, "c");
1099
1100 if (is_libc) {
1101 if (self.libc_link_lib) |libc_link_lib| {
1102 return libc_link_lib;
1103 }
1104 }
1105
1106 for (self.link_libs_list.span()) |existing_lib| {
1107 if (mem.eql(u8, name, existing_lib.name)) {
1108 return existing_lib;
1109 }
1110 }
1111
1112 const link_lib = try self.gpa().create(LinkLib);
1113 link_lib.* = LinkLib{
1114 .name = name,
1115 .path = null,
1116 .provided_explicitly = provided_explicitly,
1117 .symbols = ArrayList([]u8).init(self.gpa()),
1118 };
1119 try self.link_libs_list.append(link_lib);
1120 if (is_libc) {
1121 self.libc_link_lib = link_lib;
1122
1123 // get a head start on looking for the native libc
1124 // TODO this is missing a bunch of logic related to whether the target is native
1125 // and whether we can build libc
1126 if (self.override_libc == null) {
1127 try self.deinit_group.call(startFindingNativeLibC, .{self});
1128 }
1129 }
1130 return link_lib;
1131 }
1132
1133 async fn startFindingNativeLibC(self: *Compilation) void {
1134 event.Loop.startCpuBoundOperation();
1135 // we don't care if it fails, we're just trying to kick off the future resolution
1136 _ = self.zig_compiler.getNativeLibC() catch return;
1137 }
1138
1139 /// General Purpose Allocator. Must free when done.
1140 fn gpa(self: Compilation) *mem.Allocator {
1141 return self.zig_compiler.allocator;
1142 }
1143
1144 /// Arena Allocator. Automatically freed when the Compilation is destroyed.
1145 fn arena(self: *Compilation) *mem.Allocator {
1146 return &self.arena_allocator.allocator;
1147 }
1148
1149 /// If the temporary directory for this compilation has not been created, it creates it.
1150 /// Then it creates a random file name in that dir and returns it.
1151 pub fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !ArrayListSentineled(u8, 0) {
1152 const tmp_dir = try self.getTmpDir();
1153 const file_prefix = self.getRandomFileName();
1154
1155 const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", .{ file_prefix[0..], suffix });
1156 defer self.gpa().free(file_name);
1157
1158 const full_path = try fs.path.join(self.gpa(), &[_][]const u8{ tmp_dir, file_name[0..] });
1159 errdefer self.gpa().free(full_path);
1160
1161 return ArrayListSentineled(u8, 0).fromOwnedSlice(self.gpa(), full_path);
1162 }
1163
1164 /// If the temporary directory for this Compilation has not been created, creates it.
1165 /// Then returns it. The directory is unique to this Compilation and cleaned up when
1166 /// the Compilation deinitializes.
1167 fn getTmpDir(self: *Compilation) ![]const u8 {
1168 if (self.tmp_dir.start()) |ptr| return ptr.*;
1169 self.tmp_dir.data = self.getTmpDirImpl();
1170 self.tmp_dir.resolve();
1171 return self.tmp_dir.data;
1172 }
1173
1174 fn getTmpDirImpl(self: *Compilation) ![]u8 {
1175 const comp_dir_name = self.getRandomFileName();
1176 const zig_dir_path = try getZigDir(self.gpa());
1177 defer self.gpa().free(zig_dir_path);
1178
1179 const tmp_dir = try fs.path.join(self.arena(), &[_][]const u8{ zig_dir_path, comp_dir_name[0..] });
1180 try fs.cwd().makePath(tmp_dir);
1181 return tmp_dir;
1182 }
1183
1184 fn getRandomFileName(self: *Compilation) [12]u8 {
1185 // here we replace the standard +/ with -_ so that it can be used in a file name
1186 const b64_fs_encoder = std.base64.Base64Encoder.init(
1187 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
1188 std.base64.standard_pad_char,
1189 );
1190
1191 var rand_bytes: [9]u8 = undefined;
1192
1193 {
1194 const held = self.zig_compiler.prng.acquire();
1195 defer held.release();
1196
1197 held.value.random.bytes(rand_bytes[0..]);
1198 }
1199
1200 var result: [12]u8 = undefined;
1201 b64_fs_encoder.encode(result[0..], &rand_bytes);
1202 return result;
1203 }
1204
1205 fn registerGarbage(comp: *Compilation, comptime T: type, node: *std.atomic.Stack(*T).Node) void {
1206 // TODO put the garbage somewhere
1207 }
1208
1209 /// Returns a value which has been ref()'d once
1210 fn analyzeConstValue(
1211 comp: *Compilation,
1212 tree_scope: *Scope.AstTree,
1213 scope: *Scope,
1214 node: *ast.Node,
1215 expected_type: *Type,
1216 ) !*Value {
1217 var frame = try comp.gpa().create(@Frame(genAndAnalyzeCode));
1218 defer comp.gpa().destroy(frame);
1219 frame.* = async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type);
1220 const analyzed_code = try await frame;
1221 defer analyzed_code.destroy(comp.gpa());
1222
1223 return analyzed_code.getCompTimeResult(comp);
1224 }
1225
1226 fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
1227 const meta_type = &Type.MetaType.get(comp).base;
1228 defer meta_type.base.deref(comp);
1229
1230 const result_val = try comp.analyzeConstValue(tree_scope, scope, node, meta_type);
1231 errdefer result_val.base.deref(comp);
1232
1233 return result_val.cast(Type).?;
1234 }
1235
1236 /// This declaration has been blessed as going into the final code generation.
1237 pub async fn resolveDecl(comp: *Compilation, decl: *Decl) BuildError!void {
1238 if (decl.resolution.start()) |ptr| return ptr.*;
1239
1240 decl.resolution.data = try generateDecl(comp, decl);
1241 decl.resolution.resolve();
1242 return decl.resolution.data;
1243 }
1244};
1245
1246fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
1247 if (optional_token_index) |token_index| {
1248 const token = tree.tokens.at(token_index);
1249 assert(token.id == Token.Id.Keyword_pub);
1250 return Visib.Pub;
1251 } else {
1252 return Visib.Private;
1253 }
1254}
1255
1256/// The function that actually does the generation.
1257fn generateDecl(comp: *Compilation, decl: *Decl) !void {
1258 switch (decl.id) {
1259 .Var => @panic("TODO"),
1260 .Fn => {
1261 const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl);
1262 return generateDeclFn(comp, fn_decl);
1263 },
1264 .CompTime => @panic("TODO"),
1265 }
1266}
1267
1268fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1269 const tree_scope = fn_decl.base.tree_scope;
1270
1271 const body_node = fn_decl.fn_proto.body_node orelse return generateDeclFnProto(comp, fn_decl);
1272
1273 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
1274 defer fndef_scope.base.deref(comp);
1275
1276 const fn_type = try analyzeFnType(comp, tree_scope, fn_decl.base.parent_scope, fn_decl.fn_proto);
1277 defer fn_type.base.base.deref(comp);
1278
1279 var symbol_name = try std.ArrayListSentineled(u8, 0).init(comp.gpa(), fn_decl.base.name);
1280 var symbol_name_consumed = false;
1281 errdefer if (!symbol_name_consumed) symbol_name.deinit();
1282
1283 // The Decl.Fn owns the initial 1 reference count
1284 const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
1285 fn_decl.value = .{ .Fn = fn_val };
1286 symbol_name_consumed = true;
1287
1288 // Define local parameter variables
1289 for (fn_type.key.data.Normal.params) |param, i| {
1290 //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
1291 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
1292 const name_token = param_decl.name_token orelse {
1293 try comp.addCompileError(tree_scope, Span{
1294 .first = param_decl.firstToken(),
1295 .last = param_decl.type_node.firstToken(),
1296 }, "missing parameter name", .{});
1297 return error.SemanticAnalysisFailed;
1298 };
1299 const param_name = tree_scope.tree.tokenSlice(name_token);
1300
1301 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
1302 // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
1303 // }
1304
1305 // TODO check for shadowing
1306
1307 const var_scope = try Scope.Var.createParam(
1308 comp,
1309 fn_val.child_scope,
1310 param_name,
1311 &param_decl.base,
1312 i,
1313 param.typ,
1314 );
1315 fn_val.child_scope = &var_scope.base;
1316
1317 try fn_type.non_key.Normal.variable_list.append(var_scope);
1318 }
1319
1320 var frame = try comp.gpa().create(@Frame(Compilation.genAndAnalyzeCode));
1321 defer comp.gpa().destroy(frame);
1322 frame.* = async comp.genAndAnalyzeCode(
1323 tree_scope,
1324 fn_val.child_scope,
1325 body_node,
1326 fn_type.key.data.Normal.return_type,
1327 );
1328 const analyzed_code = try await frame;
1329 errdefer analyzed_code.destroy(comp.gpa());
1330
1331 assert(fn_val.block_scope != null);
1332
1333 // Kick off rendering to LLVM module, but it doesn't block the fn decl
1334 // analysis from being complete.
1335 try comp.prelink_group.call(codegen.renderToLlvm, .{ comp, fn_val, analyzed_code });
1336 try comp.prelink_group.call(addFnToLinkSet, .{ comp, fn_val });
1337}
1338
1339async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) Compilation.BuildError!void {
1340 fn_val.base.ref();
1341 defer fn_val.base.deref(comp);
1342
1343 fn_val.link_set_node.data = fn_val;
1344
1345 const held = comp.fn_link_set.acquire();
1346 defer held.release();
1347
1348 held.value.append(fn_val.link_set_node);
1349}
1350
1351fn getZigDir(allocator: *mem.Allocator) ![]u8 {
1352 return fs.getAppDataDir(allocator, "zig");
1353}
1354
1355fn analyzeFnType(
1356 comp: *Compilation,
1357 tree_scope: *Scope.AstTree,
1358 scope: *Scope,
1359 fn_proto: *ast.Node.FnProto,
1360) !*Type.Fn {
1361 const return_type_node = switch (fn_proto.return_type) {
1362 .Explicit => |n| n,
1363 .InferErrorSet => |n| n,
1364 };
1365 const return_type = try comp.analyzeTypeExpr(tree_scope, scope, return_type_node);
1366 return_type.base.deref(comp);
1367
1368 var params = ArrayList(Type.Fn.Param).init(comp.gpa());
1369 var params_consumed = false;
1370 defer if (!params_consumed) {
1371 for (params.span()) |param| {
1372 param.typ.base.deref(comp);
1373 }
1374 params.deinit();
1375 };
1376
1377 {
1378 var it = fn_proto.params.iterator(0);
1379 while (it.next()) |param_node_ptr| {
1380 const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
1381 const param_type = try comp.analyzeTypeExpr(tree_scope, scope, param_node.type_node);
1382 errdefer param_type.base.deref(comp);
1383 try params.append(Type.Fn.Param{
1384 .typ = param_type,
1385 .is_noalias = param_node.noalias_token != null,
1386 });
1387 }
1388 }
1389
1390 const key = Type.Fn.Key{
1391 .alignment = null,
1392 .data = Type.Fn.Key.Data{
1393 .Normal = Type.Fn.Key.Normal{
1394 .return_type = return_type,
1395 .params = params.toOwnedSlice(),
1396 .is_var_args = false, // TODO
1397 .cc = .Unspecified, // TODO
1398 },
1399 },
1400 };
1401 params_consumed = true;
1402 var key_consumed = false;
1403 defer if (!key_consumed) {
1404 for (key.data.Normal.params) |param| {
1405 param.typ.base.deref(comp);
1406 }
1407 comp.gpa().free(key.data.Normal.params);
1408 };
1409
1410 const fn_type = try Type.Fn.get(comp, key);
1411 key_consumed = true;
1412 errdefer fn_type.base.base.deref(comp);
1413
1414 return fn_type;
1415}
1416
1417fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1418 const fn_type = try analyzeFnType(
1419 comp,
1420 fn_decl.base.tree_scope,
1421 fn_decl.base.parent_scope,
1422 fn_decl.fn_proto,
1423 );
1424 defer fn_type.base.base.deref(comp);
1425
1426 var symbol_name = try std.ArrayListSentineled(u8, 0).init(comp.gpa(), fn_decl.base.name);
1427 var symbol_name_consumed = false;
1428 defer if (!symbol_name_consumed) symbol_name.deinit();
1429
1430 // The Decl.Fn owns the initial 1 reference count
1431 const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name);
1432 fn_decl.value = .{ .FnProto = fn_proto_val };
1433 symbol_name_consumed = true;
1434}
src-self-hosted/decl.zig deleted-102
......@@ -1,102 +0,0 @@
1const std = @import("std");
2const Allocator = mem.Allocator;
3const mem = std.mem;
4const ast = std.zig.ast;
5const Visib = @import("visib.zig").Visib;
6const event = std.event;
7const Value = @import("value.zig").Value;
8const Token = std.zig.Token;
9const errmsg = @import("errmsg.zig");
10const Scope = @import("scope.zig").Scope;
11const Compilation = @import("compilation.zig").Compilation;
12
13pub const Decl = struct {
14 id: Id,
15 name: []const u8,
16 visib: Visib,
17 resolution: event.Future(Compilation.BuildError!void),
18 parent_scope: *Scope,
19
20 // TODO when we destroy the decl, deref the tree scope
21 tree_scope: *Scope.AstTree,
22
23 pub const Table = std.StringHashMap(*Decl);
24
25 pub fn cast(base: *Decl, comptime T: type) ?*T {
26 if (base.id != @field(Id, @typeName(T))) return null;
27 return @fieldParentPtr(T, "base", base);
28 }
29
30 pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
31 switch (base.id) {
32 .Fn => {
33 const fn_decl = @fieldParentPtr(Fn, "base", base);
34 return fn_decl.isExported(tree);
35 },
36 else => return false,
37 }
38 }
39
40 pub fn getSpan(base: *const Decl) errmsg.Span {
41 switch (base.id) {
42 .Fn => {
43 const fn_decl = @fieldParentPtr(Fn, "base", base);
44 const fn_proto = fn_decl.fn_proto;
45 const start = fn_proto.fn_token;
46 const end = fn_proto.name_token orelse start;
47 return errmsg.Span{
48 .first = start,
49 .last = end + 1,
50 };
51 },
52 else => @panic("TODO"),
53 }
54 }
55
56 pub fn findRootScope(base: *const Decl) *Scope.Root {
57 return base.parent_scope.findRoot();
58 }
59
60 pub const Id = enum {
61 Var,
62 Fn,
63 CompTime,
64 };
65
66 pub const Var = struct {
67 base: Decl,
68 };
69
70 pub const Fn = struct {
71 base: Decl,
72 value: union(enum) {
73 Unresolved,
74 Fn: *Value.Fn,
75 FnProto: *Value.FnProto,
76 },
77 fn_proto: *ast.Node.FnProto,
78
79 pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
80 return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {
81 const token = tree.tokens.at(tok_index);
82 break :x switch (token.id) {
83 .Extern => tree.tokenSlicePtr(token),
84 else => null,
85 };
86 } else null;
87 }
88
89 pub fn isExported(self: Fn, tree: *ast.Tree) bool {
90 if (self.fn_proto.extern_export_inline_token) |tok_index| {
91 const token = tree.tokens.at(tok_index);
92 return token.id == .Keyword_export;
93 } else {
94 return false;
95 }
96 }
97 };
98
99 pub const CompTime = struct {
100 base: Decl,
101 };
102};
src-self-hosted/errmsg.zig deleted-284
......@@ -1,284 +0,0 @@
1const std = @import("std");
2const mem = std.mem;
3const fs = std.fs;
4const process = std.process;
5const Token = std.zig.Token;
6const ast = std.zig.ast;
7const TokenIndex = std.zig.ast.TokenIndex;
8const Compilation = @import("compilation.zig").Compilation;
9const Scope = @import("scope.zig").Scope;
10
11pub const Color = enum {
12 Auto,
13 Off,
14 On,
15};
16
17pub const Span = struct {
18 first: ast.TokenIndex,
19 last: ast.TokenIndex,
20
21 pub fn token(i: TokenIndex) Span {
22 return Span{
23 .first = i,
24 .last = i,
25 };
26 }
27
28 pub fn node(n: *ast.Node) Span {
29 return Span{
30 .first = n.firstToken(),
31 .last = n.lastToken(),
32 };
33 }
34};
35
36pub const Msg = struct {
37 text: []u8,
38 realpath: []u8,
39 data: Data,
40
41 const Data = union(enum) {
42 Cli: Cli,
43 PathAndTree: PathAndTree,
44 ScopeAndComp: ScopeAndComp,
45 };
46
47 const PathAndTree = struct {
48 span: Span,
49 tree: *ast.Tree,
50 allocator: *mem.Allocator,
51 };
52
53 const ScopeAndComp = struct {
54 span: Span,
55 tree_scope: *Scope.AstTree,
56 compilation: *Compilation,
57 };
58
59 const Cli = struct {
60 allocator: *mem.Allocator,
61 };
62
63 pub fn destroy(self: *Msg) void {
64 switch (self.data) {
65 .Cli => |cli| {
66 cli.allocator.free(self.text);
67 cli.allocator.free(self.realpath);
68 cli.allocator.destroy(self);
69 },
70 .PathAndTree => |path_and_tree| {
71 path_and_tree.allocator.free(self.text);
72 path_and_tree.allocator.free(self.realpath);
73 path_and_tree.allocator.destroy(self);
74 },
75 .ScopeAndComp => |scope_and_comp| {
76 scope_and_comp.tree_scope.base.deref(scope_and_comp.compilation);
77 scope_and_comp.compilation.gpa().free(self.text);
78 scope_and_comp.compilation.gpa().free(self.realpath);
79 scope_and_comp.compilation.gpa().destroy(self);
80 },
81 }
82 }
83
84 fn getAllocator(self: *const Msg) *mem.Allocator {
85 switch (self.data) {
86 .Cli => |cli| return cli.allocator,
87 .PathAndTree => |path_and_tree| {
88 return path_and_tree.allocator;
89 },
90 .ScopeAndComp => |scope_and_comp| {
91 return scope_and_comp.compilation.gpa();
92 },
93 }
94 }
95
96 pub fn getTree(self: *const Msg) *ast.Tree {
97 switch (self.data) {
98 .Cli => unreachable,
99 .PathAndTree => |path_and_tree| {
100 return path_and_tree.tree;
101 },
102 .ScopeAndComp => |scope_and_comp| {
103 return scope_and_comp.tree_scope.tree;
104 },
105 }
106 }
107
108 pub fn getSpan(self: *const Msg) Span {
109 return switch (self.data) {
110 .Cli => unreachable,
111 .PathAndTree => |path_and_tree| path_and_tree.span,
112 .ScopeAndComp => |scope_and_comp| scope_and_comp.span,
113 };
114 }
115
116 /// Takes ownership of text
117 /// References tree_scope, and derefs when the msg is freed
118 pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg {
119 const realpath = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
120 errdefer comp.gpa().free(realpath);
121
122 const msg = try comp.gpa().create(Msg);
123 msg.* = Msg{
124 .text = text,
125 .realpath = realpath,
126 .data = Data{
127 .ScopeAndComp = ScopeAndComp{
128 .tree_scope = tree_scope,
129 .compilation = comp,
130 .span = span,
131 },
132 },
133 };
134 tree_scope.base.ref();
135 return msg;
136 }
137
138 /// Caller owns returned Msg and must free with `allocator`
139 /// allocator will additionally be used for printing messages later.
140 pub fn createFromCli(comp: *Compilation, realpath: []const u8, text: []u8) !*Msg {
141 const realpath_copy = try mem.dupe(comp.gpa(), u8, realpath);
142 errdefer comp.gpa().free(realpath_copy);
143
144 const msg = try comp.gpa().create(Msg);
145 msg.* = Msg{
146 .text = text,
147 .realpath = realpath_copy,
148 .data = Data{
149 .Cli = Cli{ .allocator = comp.gpa() },
150 },
151 };
152 return msg;
153 }
154
155 pub fn createFromParseErrorAndScope(
156 comp: *Compilation,
157 tree_scope: *Scope.AstTree,
158 parse_error: *const ast.Error,
159 ) !*Msg {
160 const loc_token = parse_error.loc();
161 var text_buf = std.ArrayList(u8).init(comp.gpa());
162 defer text_buf.deinit();
163
164 const realpath_copy = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
165 errdefer comp.gpa().free(realpath_copy);
166
167 try parse_error.render(&tree_scope.tree.tokens, text_buf.outStream());
168
169 const msg = try comp.gpa().create(Msg);
170 msg.* = Msg{
171 .text = undefined,
172 .realpath = realpath_copy,
173 .data = Data{
174 .ScopeAndComp = ScopeAndComp{
175 .tree_scope = tree_scope,
176 .compilation = comp,
177 .span = Span{
178 .first = loc_token,
179 .last = loc_token,
180 },
181 },
182 },
183 };
184 tree_scope.base.ref();
185 msg.text = text_buf.toOwnedSlice();
186 return msg;
187 }
188
189 /// `realpath` must outlive the returned Msg
190 /// `tree` must outlive the returned Msg
191 /// Caller owns returned Msg and must free with `allocator`
192 /// allocator will additionally be used for printing messages later.
193 pub fn createFromParseError(
194 allocator: *mem.Allocator,
195 parse_error: *const ast.Error,
196 tree: *ast.Tree,
197 realpath: []const u8,
198 ) !*Msg {
199 const loc_token = parse_error.loc();
200 var text_buf = std.ArrayList(u8).init(allocator);
201 defer text_buf.deinit();
202
203 const realpath_copy = try mem.dupe(allocator, u8, realpath);
204 errdefer allocator.free(realpath_copy);
205
206 try parse_error.render(&tree.tokens, text_buf.outStream());
207
208 const msg = try allocator.create(Msg);
209 msg.* = Msg{
210 .text = undefined,
211 .realpath = realpath_copy,
212 .data = Data{
213 .PathAndTree = PathAndTree{
214 .allocator = allocator,
215 .tree = tree,
216 .span = Span{
217 .first = loc_token,
218 .last = loc_token,
219 },
220 },
221 },
222 };
223 msg.text = text_buf.toOwnedSlice();
224 errdefer allocator.destroy(msg);
225
226 return msg;
227 }
228
229 pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
230 switch (msg.data) {
231 .Cli => {
232 try stream.print("{}:-:-: error: {}\n", .{ msg.realpath, msg.text });
233 return;
234 },
235 else => {},
236 }
237
238 const allocator = msg.getAllocator();
239 const tree = msg.getTree();
240
241 const cwd = try process.getCwdAlloc(allocator);
242 defer allocator.free(cwd);
243
244 const relpath = try fs.path.relative(allocator, cwd, msg.realpath);
245 defer allocator.free(relpath);
246
247 const path = if (relpath.len < msg.realpath.len) relpath else msg.realpath;
248 const span = msg.getSpan();
249
250 const first_token = tree.tokens.at(span.first);
251 const last_token = tree.tokens.at(span.last);
252 const start_loc = tree.tokenLocationPtr(0, first_token);
253 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
254 if (!color_on) {
255 try stream.print("{}:{}:{}: error: {}\n", .{
256 path,
257 start_loc.line + 1,
258 start_loc.column + 1,
259 msg.text,
260 });
261 return;
262 }
263
264 try stream.print("{}:{}:{}: error: {}\n{}\n", .{
265 path,
266 start_loc.line + 1,
267 start_loc.column + 1,
268 msg.text,
269 tree.source[start_loc.line_start..start_loc.line_end],
270 });
271 try stream.writeByteNTimes(' ', start_loc.column);
272 try stream.writeByteNTimes('~', last_token.end - first_token.start);
273 try stream.writeAll("\n");
274 }
275
276 pub fn printToFile(msg: *const Msg, file: fs.File, color: Color) !void {
277 const color_on = switch (color) {
278 .Auto => file.isTty(),
279 .On => true,
280 .Off => false,
281 };
282 return msg.printToStream(file.outStream(), color_on);
283 }
284};
src-self-hosted/ir.zig+12-1206
......@@ -1,16 +1,9 @@
11const std = @import("std");
2const mem = std.mem;
3const Allocator = std.mem.Allocator;
42const Value = @import("value.zig").Value;
53const Type = @import("type.zig").Type;
6const assert = std.debug.assert;
7const BigIntConst = std.math.big.int.Const;
8const BigIntMutable = std.math.big.int.Mutable;
9const Target = std.Target;
4const Module = @import("Module.zig");
105
11pub const text = @import("ir/text.zig");
12
13/// These are in-memory, analyzed instructions. See `text.Inst` for the representation
6/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
147/// of instructions that correspond to the ZIR text format.
158/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
169/// so are the `Value` and `Type`. The value of a constant must be copied into
......@@ -25,6 +18,7 @@ pub const Inst = struct {
2518 assembly,
2619 bitcast,
2720 breakpoint,
21 call,
2822 cmp,
2923 condbr,
3024 constant,
......@@ -84,6 +78,15 @@ pub const Inst = struct {
8478 args: void,
8579 };
8680
81 pub const Call = struct {
82 pub const base_tag = Tag.call;
83 base: Inst,
84 args: struct {
85 func: *Inst,
86 args: []const *Inst,
87 },
88 };
89
8790 pub const Cmp = struct {
8891 pub const base_tag = Tag.cmp;
8992
......@@ -152,1200 +155,3 @@ pub const Inst = struct {
152155 args: void,
153156 };
154157};
155
156pub const TypedValue = struct {
157 ty: Type,
158 val: Value,
159};
160
161pub const Module = struct {
162 exports: []Export,
163 errors: []ErrorMsg,
164 arena: std.heap.ArenaAllocator,
165 fns: []Fn,
166 target: Target,
167 link_mode: std.builtin.LinkMode,
168 output_mode: std.builtin.OutputMode,
169 object_format: std.Target.ObjectFormat,
170 optimize_mode: std.builtin.Mode,
171
172 pub const Export = struct {
173 name: []const u8,
174 typed_value: TypedValue,
175 src: usize,
176 };
177
178 pub const Fn = struct {
179 analysis_status: enum { in_progress, failure, success },
180 body: Body,
181 fn_type: Type,
182 };
183
184 pub const Body = struct {
185 instructions: []*Inst,
186 };
187
188 pub fn deinit(self: *Module, allocator: *Allocator) void {
189 allocator.free(self.exports);
190 allocator.free(self.errors);
191 for (self.fns) |f| {
192 allocator.free(f.body.instructions);
193 }
194 allocator.free(self.fns);
195 self.arena.deinit();
196 self.* = undefined;
197 }
198};
199
200pub const ErrorMsg = struct {
201 byte_offset: usize,
202 msg: []const u8,
203};
204
205pub const AnalyzeOptions = struct {
206 target: Target,
207 output_mode: std.builtin.OutputMode,
208 link_mode: std.builtin.LinkMode,
209 object_format: ?std.Target.ObjectFormat = null,
210 optimize_mode: std.builtin.Mode,
211};
212
213pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeOptions) !Module {
214 var ctx = Analyze{
215 .allocator = allocator,
216 .arena = std.heap.ArenaAllocator.init(allocator),
217 .old_module = &old_module,
218 .errors = std.ArrayList(ErrorMsg).init(allocator),
219 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
220 .exports = std.ArrayList(Module.Export).init(allocator),
221 .fns = std.ArrayList(Module.Fn).init(allocator),
222 .target = options.target,
223 .optimize_mode = options.optimize_mode,
224 .link_mode = options.link_mode,
225 .output_mode = options.output_mode,
226 };
227 defer ctx.errors.deinit();
228 defer ctx.decl_table.deinit();
229 defer ctx.exports.deinit();
230 defer ctx.fns.deinit();
231 errdefer ctx.arena.deinit();
232
233 ctx.analyzeRoot() catch |err| switch (err) {
234 error.AnalysisFail => {
235 assert(ctx.errors.items.len != 0);
236 },
237 else => |e| return e,
238 };
239 return Module{
240 .exports = ctx.exports.toOwnedSlice(),
241 .errors = ctx.errors.toOwnedSlice(),
242 .fns = ctx.fns.toOwnedSlice(),
243 .arena = ctx.arena,
244 .target = ctx.target,
245 .link_mode = ctx.link_mode,
246 .output_mode = ctx.output_mode,
247 .object_format = options.object_format orelse ctx.target.getObjectFormat(),
248 .optimize_mode = ctx.optimize_mode,
249 };
250}
251
252const Analyze = struct {
253 allocator: *Allocator,
254 arena: std.heap.ArenaAllocator,
255 old_module: *const text.Module,
256 errors: std.ArrayList(ErrorMsg),
257 decl_table: std.AutoHashMap(*text.Inst, NewDecl),
258 exports: std.ArrayList(Module.Export),
259 fns: std.ArrayList(Module.Fn),
260 target: Target,
261 link_mode: std.builtin.LinkMode,
262 optimize_mode: std.builtin.Mode,
263 output_mode: std.builtin.OutputMode,
264
265 const NewDecl = struct {
266 /// null means a semantic analysis error happened
267 ptr: ?*Inst,
268 };
269
270 const NewInst = struct {
271 /// null means a semantic analysis error happened
272 ptr: ?*Inst,
273 };
274
275 const Fn = struct {
276 /// Index into Module fns array
277 fn_index: usize,
278 inner_block: Block,
279 inst_table: std.AutoHashMap(*text.Inst, NewInst),
280 };
281
282 const Block = struct {
283 func: *Fn,
284 instructions: std.ArrayList(*Inst),
285 };
286
287 const InnerError = error{ OutOfMemory, AnalysisFail };
288
289 fn analyzeRoot(self: *Analyze) !void {
290 for (self.old_module.decls) |decl| {
291 if (decl.cast(text.Inst.Export)) |export_inst| {
292 try analyzeExport(self, null, export_inst);
293 }
294 }
295 }
296
297 fn resolveInst(self: *Analyze, opt_block: ?*Block, old_inst: *text.Inst) InnerError!*Inst {
298 if (opt_block) |block| {
299 if (block.func.inst_table.get(old_inst)) |kv| {
300 return kv.value.ptr orelse return error.AnalysisFail;
301 }
302 }
303
304 if (self.decl_table.get(old_inst)) |kv| {
305 return kv.value.ptr orelse return error.AnalysisFail;
306 } else {
307 const new_inst = self.analyzeInst(null, old_inst) catch |err| switch (err) {
308 error.AnalysisFail => {
309 try self.decl_table.putNoClobber(old_inst, .{ .ptr = null });
310 return error.AnalysisFail;
311 },
312 else => |e| return e,
313 };
314 try self.decl_table.putNoClobber(old_inst, .{ .ptr = new_inst });
315 return new_inst;
316 }
317 }
318
319 fn requireRuntimeBlock(self: *Analyze, block: ?*Block, src: usize) !*Block {
320 return block orelse return self.fail(src, "instruction illegal outside function body", .{});
321 }
322
323 fn resolveInstConst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!TypedValue {
324 const new_inst = try self.resolveInst(block, old_inst);
325 const val = try self.resolveConstValue(new_inst);
326 return TypedValue{
327 .ty = new_inst.ty,
328 .val = val,
329 };
330 }
331
332 fn resolveConstValue(self: *Analyze, base: *Inst) !Value {
333 return (try self.resolveDefinedValue(base)) orelse
334 return self.fail(base.src, "unable to resolve comptime value", .{});
335 }
336
337 fn resolveDefinedValue(self: *Analyze, base: *Inst) !?Value {
338 if (base.value()) |val| {
339 if (val.isUndef()) {
340 return self.fail(base.src, "use of undefined value here causes undefined behavior", .{});
341 }
342 return val;
343 }
344 return null;
345 }
346
347 fn resolveConstString(self: *Analyze, block: ?*Block, old_inst: *text.Inst) ![]u8 {
348 const new_inst = try self.resolveInst(block, old_inst);
349 const wanted_type = Type.initTag(.const_slice_u8);
350 const coerced_inst = try self.coerce(block, wanted_type, new_inst);
351 const val = try self.resolveConstValue(coerced_inst);
352 return val.toAllocatedBytes(&self.arena.allocator);
353 }
354
355 fn resolveType(self: *Analyze, block: ?*Block, old_inst: *text.Inst) !Type {
356 const new_inst = try self.resolveInst(block, old_inst);
357 const wanted_type = Type.initTag(.@"type");
358 const coerced_inst = try self.coerce(block, wanted_type, new_inst);
359 const val = try self.resolveConstValue(coerced_inst);
360 return val.toType();
361 }
362
363 fn analyzeExport(self: *Analyze, block: ?*Block, export_inst: *text.Inst.Export) !void {
364 const symbol_name = try self.resolveConstString(block, export_inst.positionals.symbol_name);
365 const typed_value = try self.resolveInstConst(block, export_inst.positionals.value);
366
367 switch (typed_value.ty.zigTypeTag()) {
368 .Fn => {},
369 else => return self.fail(
370 export_inst.positionals.value.src,
371 "unable to export type '{}'",
372 .{typed_value.ty},
373 ),
374 }
375 try self.exports.append(.{
376 .name = symbol_name,
377 .typed_value = typed_value,
378 .src = export_inst.base.src,
379 });
380 }
381
382 /// TODO should not need the cast on the last parameter at the callsites
383 fn addNewInstArgs(
384 self: *Analyze,
385 block: *Block,
386 src: usize,
387 ty: Type,
388 comptime T: type,
389 args: Inst.Args(T),
390 ) !*Inst {
391 const inst = try self.addNewInst(block, src, ty, T);
392 inst.args = args;
393 return &inst.base;
394 }
395
396 fn addNewInst(self: *Analyze, block: *Block, src: usize, ty: Type, comptime T: type) !*T {
397 const inst = try self.arena.allocator.create(T);
398 inst.* = .{
399 .base = .{
400 .tag = T.base_tag,
401 .ty = ty,
402 .src = src,
403 },
404 .args = undefined,
405 };
406 try block.instructions.append(&inst.base);
407 return inst;
408 }
409
410 fn constInst(self: *Analyze, src: usize, typed_value: TypedValue) !*Inst {
411 const const_inst = try self.arena.allocator.create(Inst.Constant);
412 const_inst.* = .{
413 .base = .{
414 .tag = Inst.Constant.base_tag,
415 .ty = typed_value.ty,
416 .src = src,
417 },
418 .val = typed_value.val,
419 };
420 return &const_inst.base;
421 }
422
423 fn constStr(self: *Analyze, src: usize, str: []const u8) !*Inst {
424 const array_payload = try self.arena.allocator.create(Type.Payload.Array_u8_Sentinel0);
425 array_payload.* = .{ .len = str.len };
426
427 const ty_payload = try self.arena.allocator.create(Type.Payload.SingleConstPointer);
428 ty_payload.* = .{ .pointee_type = Type.initPayload(&array_payload.base) };
429
430 const bytes_payload = try self.arena.allocator.create(Value.Payload.Bytes);
431 bytes_payload.* = .{ .data = str };
432
433 return self.constInst(src, .{
434 .ty = Type.initPayload(&ty_payload.base),
435 .val = Value.initPayload(&bytes_payload.base),
436 });
437 }
438
439 fn constType(self: *Analyze, src: usize, ty: Type) !*Inst {
440 return self.constInst(src, .{
441 .ty = Type.initTag(.type),
442 .val = try ty.toValue(&self.arena.allocator),
443 });
444 }
445
446 fn constVoid(self: *Analyze, src: usize) !*Inst {
447 return self.constInst(src, .{
448 .ty = Type.initTag(.void),
449 .val = Value.initTag(.the_one_possible_value),
450 });
451 }
452
453 fn constUndef(self: *Analyze, src: usize, ty: Type) !*Inst {
454 return self.constInst(src, .{
455 .ty = ty,
456 .val = Value.initTag(.undef),
457 });
458 }
459
460 fn constBool(self: *Analyze, src: usize, v: bool) !*Inst {
461 return self.constInst(src, .{
462 .ty = Type.initTag(.bool),
463 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
464 });
465 }
466
467 fn constIntUnsigned(self: *Analyze, src: usize, ty: Type, int: u64) !*Inst {
468 const int_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
469 int_payload.* = .{ .int = int };
470
471 return self.constInst(src, .{
472 .ty = ty,
473 .val = Value.initPayload(&int_payload.base),
474 });
475 }
476
477 fn constIntSigned(self: *Analyze, src: usize, ty: Type, int: i64) !*Inst {
478 const int_payload = try self.arena.allocator.create(Value.Payload.Int_i64);
479 int_payload.* = .{ .int = int };
480
481 return self.constInst(src, .{
482 .ty = ty,
483 .val = Value.initPayload(&int_payload.base),
484 });
485 }
486
487 fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
488 const val_payload = if (big_int.positive) blk: {
489 if (big_int.to(u64)) |x| {
490 return self.constIntUnsigned(src, ty, x);
491 } else |err| switch (err) {
492 error.NegativeIntoUnsigned => unreachable,
493 error.TargetTooSmall => {}, // handled below
494 }
495 const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBigPositive);
496 big_int_payload.* = .{ .limbs = big_int.limbs };
497 break :blk &big_int_payload.base;
498 } else blk: {
499 if (big_int.to(i64)) |x| {
500 return self.constIntSigned(src, ty, x);
501 } else |err| switch (err) {
502 error.NegativeIntoUnsigned => unreachable,
503 error.TargetTooSmall => {}, // handled below
504 }
505 const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBigNegative);
506 big_int_payload.* = .{ .limbs = big_int.limbs };
507 break :blk &big_int_payload.base;
508 };
509
510 return self.constInst(src, .{
511 .ty = ty,
512 .val = Value.initPayload(val_payload),
513 });
514 }
515
516 fn analyzeInst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!*Inst {
517 switch (old_inst.tag) {
518 .breakpoint => return self.analyzeInstBreakpoint(block, old_inst.cast(text.Inst.Breakpoint).?),
519 .str => {
520 // We can use this reference because Inst.Const's Value is arena-allocated.
521 // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends.
522 const bytes = old_inst.cast(text.Inst.Str).?.positionals.bytes;
523 return self.constStr(old_inst.src, bytes);
524 },
525 .int => {
526 const big_int = old_inst.cast(text.Inst.Int).?.positionals.int;
527 return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int);
528 },
529 .ptrtoint => return self.analyzeInstPtrToInt(block, old_inst.cast(text.Inst.PtrToInt).?),
530 .fieldptr => return self.analyzeInstFieldPtr(block, old_inst.cast(text.Inst.FieldPtr).?),
531 .deref => return self.analyzeInstDeref(block, old_inst.cast(text.Inst.Deref).?),
532 .as => return self.analyzeInstAs(block, old_inst.cast(text.Inst.As).?),
533 .@"asm" => return self.analyzeInstAsm(block, old_inst.cast(text.Inst.Asm).?),
534 .@"unreachable" => return self.analyzeInstUnreachable(block, old_inst.cast(text.Inst.Unreachable).?),
535 .@"return" => return self.analyzeInstRet(block, old_inst.cast(text.Inst.Return).?),
536 .@"fn" => return self.analyzeInstFn(block, old_inst.cast(text.Inst.Fn).?),
537 .@"export" => {
538 try self.analyzeExport(block, old_inst.cast(text.Inst.Export).?);
539 return self.constVoid(old_inst.src);
540 },
541 .primitive => return self.analyzeInstPrimitive(old_inst.cast(text.Inst.Primitive).?),
542 .fntype => return self.analyzeInstFnType(block, old_inst.cast(text.Inst.FnType).?),
543 .intcast => return self.analyzeInstIntCast(block, old_inst.cast(text.Inst.IntCast).?),
544 .bitcast => return self.analyzeInstBitCast(block, old_inst.cast(text.Inst.BitCast).?),
545 .elemptr => return self.analyzeInstElemPtr(block, old_inst.cast(text.Inst.ElemPtr).?),
546 .add => return self.analyzeInstAdd(block, old_inst.cast(text.Inst.Add).?),
547 .cmp => return self.analyzeInstCmp(block, old_inst.cast(text.Inst.Cmp).?),
548 .condbr => return self.analyzeInstCondBr(block, old_inst.cast(text.Inst.CondBr).?),
549 .isnull => return self.analyzeInstIsNull(block, old_inst.cast(text.Inst.IsNull).?),
550 .isnonnull => return self.analyzeInstIsNonNull(block, old_inst.cast(text.Inst.IsNonNull).?),
551 }
552 }
553
554 fn analyzeInstBreakpoint(self: *Analyze, block: ?*Block, inst: *text.Inst.Breakpoint) InnerError!*Inst {
555 const b = try self.requireRuntimeBlock(block, inst.base.src);
556 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});
557 }
558
559 fn analyzeInstFn(self: *Analyze, block: ?*Block, fn_inst: *text.Inst.Fn) InnerError!*Inst {
560 const fn_type = try self.resolveType(block, fn_inst.positionals.fn_type);
561
562 var new_func: Fn = .{
563 .fn_index = self.fns.items.len,
564 .inner_block = .{
565 .func = undefined,
566 .instructions = std.ArrayList(*Inst).init(self.allocator),
567 },
568 .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
569 };
570 new_func.inner_block.func = &new_func;
571 defer new_func.inner_block.instructions.deinit();
572 defer new_func.inst_table.deinit();
573 // Don't hang on to a reference to this when analyzing body instructions, since the memory
574 // could become invalid.
575 (try self.fns.addOne()).* = .{
576 .analysis_status = .in_progress,
577 .fn_type = fn_type,
578 .body = undefined,
579 };
580
581 try self.analyzeBody(&new_func.inner_block, fn_inst.positionals.body);
582
583 const f = &self.fns.items[new_func.fn_index];
584 f.analysis_status = .success;
585 f.body = .{ .instructions = new_func.inner_block.instructions.toOwnedSlice() };
586
587 const fn_payload = try self.arena.allocator.create(Value.Payload.Function);
588 fn_payload.* = .{ .index = new_func.fn_index };
589
590 return self.constInst(fn_inst.base.src, .{
591 .ty = fn_type,
592 .val = Value.initPayload(&fn_payload.base),
593 });
594 }
595
596 fn analyzeInstFnType(self: *Analyze, block: ?*Block, fntype: *text.Inst.FnType) InnerError!*Inst {
597 const return_type = try self.resolveType(block, fntype.positionals.return_type);
598
599 if (return_type.zigTypeTag() == .NoReturn and
600 fntype.positionals.param_types.len == 0 and
601 fntype.kw_args.cc == .Naked)
602 {
603 return self.constType(fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
604 }
605
606 if (return_type.zigTypeTag() == .Void and
607 fntype.positionals.param_types.len == 0 and
608 fntype.kw_args.cc == .C)
609 {
610 return self.constType(fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
611 }
612
613 return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{});
614 }
615
616 fn analyzeInstPrimitive(self: *Analyze, primitive: *text.Inst.Primitive) InnerError!*Inst {
617 return self.constType(primitive.base.src, primitive.positionals.tag.toType());
618 }
619
620 fn analyzeInstAs(self: *Analyze, block: ?*Block, as: *text.Inst.As) InnerError!*Inst {
621 const dest_type = try self.resolveType(block, as.positionals.dest_type);
622 const new_inst = try self.resolveInst(block, as.positionals.value);
623 return self.coerce(block, dest_type, new_inst);
624 }
625
626 fn analyzeInstPtrToInt(self: *Analyze, block: ?*Block, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
627 const ptr = try self.resolveInst(block, ptrtoint.positionals.ptr);
628 if (ptr.ty.zigTypeTag() != .Pointer) {
629 return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
630 }
631 // TODO handle known-pointer-address
632 const b = try self.requireRuntimeBlock(block, ptrtoint.base.src);
633 const ty = Type.initTag(.usize);
634 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });
635 }
636
637 fn analyzeInstFieldPtr(self: *Analyze, block: ?*Block, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst {
638 const object_ptr = try self.resolveInst(block, fieldptr.positionals.object_ptr);
639 const field_name = try self.resolveConstString(block, fieldptr.positionals.field_name);
640
641 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
642 .Pointer => object_ptr.ty.elemType(),
643 else => return self.fail(fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
644 };
645 switch (elem_ty.zigTypeTag()) {
646 .Array => {
647 if (mem.eql(u8, field_name, "len")) {
648 const len_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
649 len_payload.* = .{ .int = elem_ty.arrayLen() };
650
651 const ref_payload = try self.arena.allocator.create(Value.Payload.RefVal);
652 ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
653
654 return self.constInst(fieldptr.base.src, .{
655 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
656 .val = Value.initPayload(&ref_payload.base),
657 });
658 } else {
659 return self.fail(
660 fieldptr.positionals.field_name.src,
661 "no member named '{}' in '{}'",
662 .{ field_name, elem_ty },
663 );
664 }
665 },
666 else => return self.fail(fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}),
667 }
668 }
669
670 fn analyzeInstIntCast(self: *Analyze, block: ?*Block, intcast: *text.Inst.IntCast) InnerError!*Inst {
671 const dest_type = try self.resolveType(block, intcast.positionals.dest_type);
672 const new_inst = try self.resolveInst(block, intcast.positionals.value);
673
674 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
675 .ComptimeInt => true,
676 .Int => false,
677 else => return self.fail(
678 intcast.positionals.dest_type.src,
679 "expected integer type, found '{}'",
680 .{
681 dest_type,
682 },
683 ),
684 };
685
686 switch (new_inst.ty.zigTypeTag()) {
687 .ComptimeInt, .Int => {},
688 else => return self.fail(
689 intcast.positionals.value.src,
690 "expected integer type, found '{}'",
691 .{new_inst.ty},
692 ),
693 }
694
695 if (dest_is_comptime_int or new_inst.value() != null) {
696 return self.coerce(block, dest_type, new_inst);
697 }
698
699 return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});
700 }
701
702 fn analyzeInstBitCast(self: *Analyze, block: ?*Block, inst: *text.Inst.BitCast) InnerError!*Inst {
703 const dest_type = try self.resolveType(block, inst.positionals.dest_type);
704 const operand = try self.resolveInst(block, inst.positionals.operand);
705 return self.bitcast(block, dest_type, operand);
706 }
707
708 fn analyzeInstElemPtr(self: *Analyze, block: ?*Block, inst: *text.Inst.ElemPtr) InnerError!*Inst {
709 const array_ptr = try self.resolveInst(block, inst.positionals.array_ptr);
710 const uncasted_index = try self.resolveInst(block, inst.positionals.index);
711 const elem_index = try self.coerce(block, Type.initTag(.usize), uncasted_index);
712
713 if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {
714 if (array_ptr.value()) |array_ptr_val| {
715 if (elem_index.value()) |index_val| {
716 // Both array pointer and index are compile-time known.
717 const index_u64 = index_val.toUnsignedInt();
718 // @intCast here because it would have been impossible to construct a value that
719 // required a larger index.
720 const elem_val = try array_ptr_val.elemValueAt(&self.arena.allocator, @intCast(usize, index_u64));
721
722 const ref_payload = try self.arena.allocator.create(Value.Payload.RefVal);
723 ref_payload.* = .{ .val = elem_val };
724
725 const type_payload = try self.arena.allocator.create(Type.Payload.SingleConstPointer);
726 type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() };
727
728 return self.constInst(inst.base.src, .{
729 .ty = Type.initPayload(&type_payload.base),
730 .val = Value.initPayload(&ref_payload.base),
731 });
732 }
733 }
734 }
735
736 return self.fail(inst.base.src, "TODO implement more analyze elemptr", .{});
737 }
738
739 fn analyzeInstAdd(self: *Analyze, block: ?*Block, inst: *text.Inst.Add) InnerError!*Inst {
740 const lhs = try self.resolveInst(block, inst.positionals.lhs);
741 const rhs = try self.resolveInst(block, inst.positionals.rhs);
742
743 if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) {
744 if (lhs.value()) |lhs_val| {
745 if (rhs.value()) |rhs_val| {
746 // TODO is this a performance issue? maybe we should try the operation without
747 // resorting to BigInt first.
748 var lhs_space: Value.BigIntSpace = undefined;
749 var rhs_space: Value.BigIntSpace = undefined;
750 const lhs_bigint = lhs_val.toBigInt(&lhs_space);
751 const rhs_bigint = rhs_val.toBigInt(&rhs_space);
752 const limbs = try self.arena.allocator.alloc(
753 std.math.big.Limb,
754 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
755 );
756 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
757 result_bigint.add(lhs_bigint, rhs_bigint);
758 const result_limbs = result_bigint.limbs[0..result_bigint.len];
759
760 if (!lhs.ty.eql(rhs.ty)) {
761 return self.fail(inst.base.src, "TODO implement peer type resolution", .{});
762 }
763
764 const val_payload = if (result_bigint.positive) blk: {
765 const val_payload = try self.arena.allocator.create(Value.Payload.IntBigPositive);
766 val_payload.* = .{ .limbs = result_limbs };
767 break :blk &val_payload.base;
768 } else blk: {
769 const val_payload = try self.arena.allocator.create(Value.Payload.IntBigNegative);
770 val_payload.* = .{ .limbs = result_limbs };
771 break :blk &val_payload.base;
772 };
773
774 return self.constInst(inst.base.src, .{
775 .ty = lhs.ty,
776 .val = Value.initPayload(val_payload),
777 });
778 }
779 }
780 }
781
782 return self.fail(inst.base.src, "TODO implement more analyze add", .{});
783 }
784
785 fn analyzeInstDeref(self: *Analyze, block: ?*Block, deref: *text.Inst.Deref) InnerError!*Inst {
786 const ptr = try self.resolveInst(block, deref.positionals.ptr);
787 const elem_ty = switch (ptr.ty.zigTypeTag()) {
788 .Pointer => ptr.ty.elemType(),
789 else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}),
790 };
791 if (ptr.value()) |val| {
792 return self.constInst(deref.base.src, .{
793 .ty = elem_ty,
794 .val = val.pointerDeref(),
795 });
796 }
797
798 return self.fail(deref.base.src, "TODO implement runtime deref", .{});
799 }
800
801 fn analyzeInstAsm(self: *Analyze, block: ?*Block, assembly: *text.Inst.Asm) InnerError!*Inst {
802 const return_type = try self.resolveType(block, assembly.positionals.return_type);
803 const asm_source = try self.resolveConstString(block, assembly.positionals.asm_source);
804 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(block, o) else null;
805
806 const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len);
807 const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len);
808 const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len);
809
810 for (inputs) |*elem, i| {
811 elem.* = try self.resolveConstString(block, assembly.kw_args.inputs[i]);
812 }
813 for (clobbers) |*elem, i| {
814 elem.* = try self.resolveConstString(block, assembly.kw_args.clobbers[i]);
815 }
816 for (args) |*elem, i| {
817 const arg = try self.resolveInst(block, assembly.kw_args.args[i]);
818 elem.* = try self.coerce(block, Type.initTag(.usize), arg);
819 }
820
821 const b = try self.requireRuntimeBlock(block, assembly.base.src);
822 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
823 .asm_source = asm_source,
824 .is_volatile = assembly.kw_args.@"volatile",
825 .output = output,
826 .inputs = inputs,
827 .clobbers = clobbers,
828 .args = args,
829 });
830 }
831
832 fn analyzeInstCmp(self: *Analyze, block: ?*Block, inst: *text.Inst.Cmp) InnerError!*Inst {
833 const lhs = try self.resolveInst(block, inst.positionals.lhs);
834 const rhs = try self.resolveInst(block, inst.positionals.rhs);
835 const op = inst.positionals.op;
836
837 const is_equality_cmp = switch (op) {
838 .eq, .neq => true,
839 else => false,
840 };
841 const lhs_ty_tag = lhs.ty.zigTypeTag();
842 const rhs_ty_tag = rhs.ty.zigTypeTag();
843 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
844 // null == null, null != null
845 return self.constBool(inst.base.src, op == .eq);
846 } else if (is_equality_cmp and
847 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
848 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
849 {
850 // comparing null with optionals
851 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
852 if (opt_operand.value()) |opt_val| {
853 const is_null = opt_val.isNull();
854 return self.constBool(inst.base.src, if (op == .eq) is_null else !is_null);
855 }
856 const b = try self.requireRuntimeBlock(block, inst.base.src);
857 switch (op) {
858 .eq => return self.addNewInstArgs(
859 b,
860 inst.base.src,
861 Type.initTag(.bool),
862 Inst.IsNull,
863 Inst.Args(Inst.IsNull){ .operand = opt_operand },
864 ),
865 .neq => return self.addNewInstArgs(
866 b,
867 inst.base.src,
868 Type.initTag(.bool),
869 Inst.IsNonNull,
870 Inst.Args(Inst.IsNonNull){ .operand = opt_operand },
871 ),
872 else => unreachable,
873 }
874 } else if (is_equality_cmp and
875 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
876 {
877 return self.fail(inst.base.src, "TODO implement C pointer cmp", .{});
878 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
879 const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
880 return self.fail(inst.base.src, "comparison of '{}' with null", .{non_null_type});
881 } else if (is_equality_cmp and
882 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
883 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
884 {
885 return self.fail(inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
886 } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
887 if (!is_equality_cmp) {
888 return self.fail(inst.base.src, "{} operator not allowed for errors", .{@tagName(op)});
889 }
890 return self.fail(inst.base.src, "TODO implement equality comparison between errors", .{});
891 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
892 // This operation allows any combination of integer and float types, regardless of the
893 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
894 // numeric types.
895 return self.cmpNumeric(block, inst.base.src, lhs, rhs, op);
896 }
897 return self.fail(inst.base.src, "TODO implement more cmp analysis", .{});
898 }
899
900 fn analyzeInstIsNull(self: *Analyze, block: ?*Block, inst: *text.Inst.IsNull) InnerError!*Inst {
901 const operand = try self.resolveInst(block, inst.positionals.operand);
902 return self.analyzeIsNull(block, inst.base.src, operand, true);
903 }
904
905 fn analyzeInstIsNonNull(self: *Analyze, block: ?*Block, inst: *text.Inst.IsNonNull) InnerError!*Inst {
906 const operand = try self.resolveInst(block, inst.positionals.operand);
907 return self.analyzeIsNull(block, inst.base.src, operand, false);
908 }
909
910 fn analyzeInstCondBr(self: *Analyze, block: ?*Block, inst: *text.Inst.CondBr) InnerError!*Inst {
911 const uncasted_cond = try self.resolveInst(block, inst.positionals.condition);
912 const cond = try self.coerce(block, Type.initTag(.bool), uncasted_cond);
913
914 if (try self.resolveDefinedValue(cond)) |cond_val| {
915 const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body;
916 try self.analyzeBody(block, body.*);
917 return self.constVoid(inst.base.src);
918 }
919
920 const parent_block = try self.requireRuntimeBlock(block, inst.base.src);
921
922 var true_block: Block = .{
923 .func = parent_block.func,
924 .instructions = std.ArrayList(*Inst).init(self.allocator),
925 };
926 defer true_block.instructions.deinit();
927 try self.analyzeBody(&true_block, inst.positionals.true_body);
928
929 var false_block: Block = .{
930 .func = parent_block.func,
931 .instructions = std.ArrayList(*Inst).init(self.allocator),
932 };
933 defer false_block.instructions.deinit();
934 try self.analyzeBody(&false_block, inst.positionals.false_body);
935
936 // Copy the instruction pointers to the arena memory
937 const true_instructions = try self.arena.allocator.alloc(*Inst, true_block.instructions.items.len);
938 const false_instructions = try self.arena.allocator.alloc(*Inst, false_block.instructions.items.len);
939
940 mem.copy(*Inst, true_instructions, true_block.instructions.items);
941 mem.copy(*Inst, false_instructions, false_block.instructions.items);
942
943 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.void), Inst.CondBr, Inst.Args(Inst.CondBr){
944 .condition = cond,
945 .true_body = .{ .instructions = true_instructions },
946 .false_body = .{ .instructions = false_instructions },
947 });
948 }
949
950 fn wantSafety(self: *Analyze, block: ?*Block) bool {
951 return switch (self.optimize_mode) {
952 .Debug => true,
953 .ReleaseSafe => true,
954 .ReleaseFast => false,
955 .ReleaseSmall => false,
956 };
957 }
958
959 fn analyzeInstUnreachable(self: *Analyze, block: ?*Block, unreach: *text.Inst.Unreachable) InnerError!*Inst {
960 const b = try self.requireRuntimeBlock(block, unreach.base.src);
961 if (self.wantSafety(block)) {
962 // TODO Once we have a panic function to call, call it here instead of this.
963 _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {});
964 }
965 return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
966 }
967
968 fn analyzeInstRet(self: *Analyze, block: ?*Block, inst: *text.Inst.Return) InnerError!*Inst {
969 const b = try self.requireRuntimeBlock(block, inst.base.src);
970 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {});
971 }
972
973 fn analyzeBody(self: *Analyze, block: ?*Block, body: text.Module.Body) !void {
974 for (body.instructions) |src_inst| {
975 const new_inst = self.analyzeInst(block, src_inst) catch |err| {
976 if (block) |b| {
977 self.fns.items[b.func.fn_index].analysis_status = .failure;
978 try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = null });
979 }
980 return err;
981 };
982 if (block) |b| try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
983 }
984 }
985
986 fn analyzeIsNull(
987 self: *Analyze,
988 block: ?*Block,
989 src: usize,
990 operand: *Inst,
991 invert_logic: bool,
992 ) InnerError!*Inst {
993 return self.fail(src, "TODO implement analysis of isnull and isnotnull", .{});
994 }
995
996 /// Asserts that lhs and rhs types are both numeric.
997 fn cmpNumeric(
998 self: *Analyze,
999 block: ?*Block,
1000 src: usize,
1001 lhs: *Inst,
1002 rhs: *Inst,
1003 op: std.math.CompareOperator,
1004 ) !*Inst {
1005 assert(lhs.ty.isNumeric());
1006 assert(rhs.ty.isNumeric());
1007
1008 const lhs_ty_tag = lhs.ty.zigTypeTag();
1009 const rhs_ty_tag = rhs.ty.zigTypeTag();
1010
1011 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
1012 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
1013 return self.fail(src, "vector length mismatch: {} and {}", .{
1014 lhs.ty.arrayLen(),
1015 rhs.ty.arrayLen(),
1016 });
1017 }
1018 return self.fail(src, "TODO implement support for vectors in cmpNumeric", .{});
1019 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
1020 return self.fail(src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
1021 lhs.ty,
1022 rhs.ty,
1023 });
1024 }
1025
1026 if (lhs.value()) |lhs_val| {
1027 if (rhs.value()) |rhs_val| {
1028 return self.constBool(src, Value.compare(lhs_val, op, rhs_val));
1029 }
1030 }
1031
1032 // TODO handle comparisons against lazy zero values
1033 // Some values can be compared against zero without being runtime known or without forcing
1034 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
1035 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
1036 // of this function if we don't need to.
1037
1038 // It must be a runtime comparison.
1039 const b = try self.requireRuntimeBlock(block, src);
1040 // For floats, emit a float comparison instruction.
1041 const lhs_is_float = switch (lhs_ty_tag) {
1042 .Float, .ComptimeFloat => true,
1043 else => false,
1044 };
1045 const rhs_is_float = switch (rhs_ty_tag) {
1046 .Float, .ComptimeFloat => true,
1047 else => false,
1048 };
1049 if (lhs_is_float and rhs_is_float) {
1050 // Implicit cast the smaller one to the larger one.
1051 const dest_type = x: {
1052 if (lhs_ty_tag == .ComptimeFloat) {
1053 break :x rhs.ty;
1054 } else if (rhs_ty_tag == .ComptimeFloat) {
1055 break :x lhs.ty;
1056 }
1057 if (lhs.ty.floatBits(self.target) >= rhs.ty.floatBits(self.target)) {
1058 break :x lhs.ty;
1059 } else {
1060 break :x rhs.ty;
1061 }
1062 };
1063 const casted_lhs = try self.coerce(block, dest_type, lhs);
1064 const casted_rhs = try self.coerce(block, dest_type, rhs);
1065 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
1066 .lhs = casted_lhs,
1067 .rhs = casted_rhs,
1068 .op = op,
1069 });
1070 }
1071 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
1072 // For mixed signed and unsigned integers, implicit cast both operands to a signed
1073 // integer with + 1 bit.
1074 // For mixed floats and integers, extract the integer part from the float, cast that to
1075 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
1076 // add/subtract 1.
1077 const lhs_is_signed = if (lhs.value()) |lhs_val|
1078 lhs_val.compareWithZero(.lt)
1079 else
1080 (lhs.ty.isFloat() or lhs.ty.isSignedInt());
1081 const rhs_is_signed = if (rhs.value()) |rhs_val|
1082 rhs_val.compareWithZero(.lt)
1083 else
1084 (rhs.ty.isFloat() or rhs.ty.isSignedInt());
1085 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
1086
1087 var dest_float_type: ?Type = null;
1088
1089 var lhs_bits: usize = undefined;
1090 if (lhs.value()) |lhs_val| {
1091 if (lhs_val.isUndef())
1092 return self.constUndef(src, Type.initTag(.bool));
1093 const is_unsigned = if (lhs_is_float) x: {
1094 var bigint_space: Value.BigIntSpace = undefined;
1095 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
1096 defer bigint.deinit();
1097 const zcmp = lhs_val.orderAgainstZero();
1098 if (lhs_val.floatHasFraction()) {
1099 switch (op) {
1100 .eq => return self.constBool(src, false),
1101 .neq => return self.constBool(src, true),
1102 else => {},
1103 }
1104 if (zcmp == .lt) {
1105 try bigint.addScalar(bigint.toConst(), -1);
1106 } else {
1107 try bigint.addScalar(bigint.toConst(), 1);
1108 }
1109 }
1110 lhs_bits = bigint.toConst().bitCountTwosComp();
1111 break :x (zcmp != .lt);
1112 } else x: {
1113 lhs_bits = lhs_val.intBitCountTwosComp();
1114 break :x (lhs_val.orderAgainstZero() != .lt);
1115 };
1116 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
1117 } else if (lhs_is_float) {
1118 dest_float_type = lhs.ty;
1119 } else {
1120 const int_info = lhs.ty.intInfo(self.target);
1121 lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
1122 }
1123
1124 var rhs_bits: usize = undefined;
1125 if (rhs.value()) |rhs_val| {
1126 if (rhs_val.isUndef())
1127 return self.constUndef(src, Type.initTag(.bool));
1128 const is_unsigned = if (rhs_is_float) x: {
1129 var bigint_space: Value.BigIntSpace = undefined;
1130 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
1131 defer bigint.deinit();
1132 const zcmp = rhs_val.orderAgainstZero();
1133 if (rhs_val.floatHasFraction()) {
1134 switch (op) {
1135 .eq => return self.constBool(src, false),
1136 .neq => return self.constBool(src, true),
1137 else => {},
1138 }
1139 if (zcmp == .lt) {
1140 try bigint.addScalar(bigint.toConst(), -1);
1141 } else {
1142 try bigint.addScalar(bigint.toConst(), 1);
1143 }
1144 }
1145 rhs_bits = bigint.toConst().bitCountTwosComp();
1146 break :x (zcmp != .lt);
1147 } else x: {
1148 rhs_bits = rhs_val.intBitCountTwosComp();
1149 break :x (rhs_val.orderAgainstZero() != .lt);
1150 };
1151 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
1152 } else if (rhs_is_float) {
1153 dest_float_type = rhs.ty;
1154 } else {
1155 const int_info = rhs.ty.intInfo(self.target);
1156 rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
1157 }
1158
1159 const dest_type = if (dest_float_type) |ft| ft else blk: {
1160 const max_bits = std.math.max(lhs_bits, rhs_bits);
1161 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
1162 error.Overflow => return self.fail(src, "{} exceeds maximum integer bit count", .{max_bits}),
1163 };
1164 break :blk try self.makeIntType(dest_int_is_signed, casted_bits);
1165 };
1166 const casted_lhs = try self.coerce(block, dest_type, lhs);
1167 const casted_rhs = try self.coerce(block, dest_type, lhs);
1168
1169 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
1170 .lhs = casted_lhs,
1171 .rhs = casted_rhs,
1172 .op = op,
1173 });
1174 }
1175
1176 fn makeIntType(self: *Analyze, signed: bool, bits: u16) !Type {
1177 if (signed) {
1178 const int_payload = try self.arena.allocator.create(Type.Payload.IntSigned);
1179 int_payload.* = .{ .bits = bits };
1180 return Type.initPayload(&int_payload.base);
1181 } else {
1182 const int_payload = try self.arena.allocator.create(Type.Payload.IntUnsigned);
1183 int_payload.* = .{ .bits = bits };
1184 return Type.initPayload(&int_payload.base);
1185 }
1186 }
1187
1188 fn coerce(self: *Analyze, block: ?*Block, dest_type: Type, inst: *Inst) !*Inst {
1189 // If the types are the same, we can return the operand.
1190 if (dest_type.eql(inst.ty))
1191 return inst;
1192
1193 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
1194 if (in_memory_result == .ok) {
1195 return self.bitcast(block, dest_type, inst);
1196 }
1197
1198 // *[N]T to []T
1199 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
1200 (!inst.ty.pointerIsConst() or dest_type.pointerIsConst()))
1201 {
1202 const array_type = inst.ty.elemType();
1203 const dst_elem_type = dest_type.elemType();
1204 if (array_type.zigTypeTag() == .Array and
1205 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
1206 {
1207 return self.coerceArrayPtrToSlice(dest_type, inst);
1208 }
1209 }
1210
1211 // comptime_int to fixed-width integer
1212 if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) {
1213 // The representation is already correct; we only need to make sure it fits in the destination type.
1214 const val = inst.value().?; // comptime_int always has comptime known value
1215 if (!val.intFitsInType(dest_type, self.target)) {
1216 return self.fail(inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
1217 }
1218 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
1219 }
1220
1221 // integer widening
1222 if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
1223 const src_info = inst.ty.intInfo(self.target);
1224 const dst_info = dest_type.intInfo(self.target);
1225 if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) {
1226 if (inst.value()) |val| {
1227 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
1228 } else {
1229 return self.fail(inst.src, "TODO implement runtime integer widening", .{});
1230 }
1231 } else {
1232 return self.fail(inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type });
1233 }
1234 }
1235
1236 return self.fail(inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
1237 }
1238
1239 fn bitcast(self: *Analyze, block: ?*Block, dest_type: Type, inst: *Inst) !*Inst {
1240 if (inst.value()) |val| {
1241 // Keep the comptime Value representation; take the new type.
1242 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
1243 }
1244 // TODO validate the type size and other compile errors
1245 const b = try self.requireRuntimeBlock(block, inst.src);
1246 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
1247 }
1248
1249 fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
1250 if (inst.value()) |val| {
1251 // The comptime Value representation is compatible with both types.
1252 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
1253 }
1254 return self.fail(inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
1255 }
1256
1257 fn fail(self: *Analyze, src: usize, comptime format: []const u8, args: var) InnerError {
1258 @setCold(true);
1259 const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args);
1260 (try self.errors.addOne()).* = .{
1261 .byte_offset = src,
1262 .msg = msg,
1263 };
1264 return error.AnalysisFail;
1265 }
1266
1267 const InMemoryCoercionResult = enum {
1268 ok,
1269 no_match,
1270 };
1271
1272 fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
1273 if (dest_type.eql(src_type))
1274 return .ok;
1275
1276 // TODO: implement more of this function
1277
1278 return .no_match;
1279 }
1280};
1281
1282pub fn main() anyerror!void {
1283 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1284 defer arena.deinit();
1285 const allocator = if (std.builtin.link_libc) std.heap.c_allocator else &arena.allocator;
1286
1287 const args = try std.process.argsAlloc(allocator);
1288 defer std.process.argsFree(allocator, args);
1289
1290 const src_path = args[1];
1291 const debug_error_trace = true;
1292
1293 const source = try std.fs.cwd().readFileAllocOptions(allocator, src_path, std.math.maxInt(u32), 1, 0);
1294 defer allocator.free(source);
1295
1296 var zir_module = try text.parse(allocator, source);
1297 defer zir_module.deinit(allocator);
1298
1299 if (zir_module.errors.len != 0) {
1300 for (zir_module.errors) |err_msg| {
1301 const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
1302 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
1303 }
1304 if (debug_error_trace) return error.ParseFailure;
1305 std.process.exit(1);
1306 }
1307
1308 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
1309
1310 var analyzed_module = try analyze(allocator, zir_module, .{
1311 .target = native_info.target,
1312 .output_mode = .Obj,
1313 .link_mode = .Static,
1314 .optimize_mode = .Debug,
1315 });
1316 defer analyzed_module.deinit(allocator);
1317
1318 if (analyzed_module.errors.len != 0) {
1319 for (analyzed_module.errors) |err_msg| {
1320 const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
1321 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
1322 }
1323 if (debug_error_trace) return error.AnalysisFail;
1324 std.process.exit(1);
1325 }
1326
1327 const output_zir = true;
1328 if (output_zir) {
1329 var new_zir_module = try text.emit_zir(allocator, analyzed_module);
1330 defer new_zir_module.deinit(allocator);
1331
1332 var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream());
1333 try new_zir_module.writeToStream(allocator, bos.outStream());
1334 try bos.flush();
1335 }
1336
1337 const link = @import("link.zig");
1338 var result = try link.updateFilePath(allocator, analyzed_module, std.fs.cwd(), "zir.o");
1339 defer result.deinit(allocator);
1340 if (result.errors.len != 0) {
1341 for (result.errors) |err_msg| {
1342 const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
1343 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
1344 }
1345 if (debug_error_trace) return error.LinkFailure;
1346 std.process.exit(1);
1347 }
1348}
1349
1350// Performance optimization ideas:
1351// * when analyzing use a field in the Inst instead of HashMap to track corresponding instructions
src-self-hosted/ir/text.zig deleted-1282
......@@ -1,1282 +0,0 @@
1//! This file has to do with parsing and rendering the ZIR text format.
2
3const std = @import("std");
4const mem = std.mem;
5const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;
7const BigIntConst = std.math.big.int.Const;
8const BigIntMutable = std.math.big.int.Mutable;
9const Type = @import("../type.zig").Type;
10const Value = @import("../value.zig").Value;
11const ir = @import("../ir.zig");
12
13/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for
14/// in-memory, analyzed instructions with types and values.
15pub const Inst = struct {
16 tag: Tag,
17 /// Byte offset into the source.
18 src: usize,
19
20 /// These names are used directly as the instruction names in the text format.
21 pub const Tag = enum {
22 breakpoint,
23 str,
24 int,
25 ptrtoint,
26 fieldptr,
27 deref,
28 as,
29 @"asm",
30 @"unreachable",
31 @"return",
32 @"fn",
33 @"export",
34 primitive,
35 fntype,
36 intcast,
37 bitcast,
38 elemptr,
39 add,
40 cmp,
41 condbr,
42 isnull,
43 isnonnull,
44 };
45
46 pub fn TagToType(tag: Tag) type {
47 return switch (tag) {
48 .breakpoint => Breakpoint,
49 .str => Str,
50 .int => Int,
51 .ptrtoint => PtrToInt,
52 .fieldptr => FieldPtr,
53 .deref => Deref,
54 .as => As,
55 .@"asm" => Asm,
56 .@"unreachable" => Unreachable,
57 .@"return" => Return,
58 .@"fn" => Fn,
59 .@"export" => Export,
60 .primitive => Primitive,
61 .fntype => FnType,
62 .intcast => IntCast,
63 .bitcast => BitCast,
64 .elemptr => ElemPtr,
65 .add => Add,
66 .cmp => Cmp,
67 .condbr => CondBr,
68 .isnull => IsNull,
69 .isnonnull => IsNonNull,
70 };
71 }
72
73 pub fn cast(base: *Inst, comptime T: type) ?*T {
74 if (base.tag != T.base_tag)
75 return null;
76
77 return @fieldParentPtr(T, "base", base);
78 }
79
80 pub const Breakpoint = struct {
81 pub const base_tag = Tag.breakpoint;
82 base: Inst,
83
84 positionals: struct {},
85 kw_args: struct {},
86 };
87
88 pub const Str = struct {
89 pub const base_tag = Tag.str;
90 base: Inst,
91
92 positionals: struct {
93 bytes: []const u8,
94 },
95 kw_args: struct {},
96 };
97
98 pub const Int = struct {
99 pub const base_tag = Tag.int;
100 base: Inst,
101
102 positionals: struct {
103 int: BigIntConst,
104 },
105 kw_args: struct {},
106 };
107
108 pub const PtrToInt = struct {
109 pub const base_tag = Tag.ptrtoint;
110 base: Inst,
111
112 positionals: struct {
113 ptr: *Inst,
114 },
115 kw_args: struct {},
116 };
117
118 pub const FieldPtr = struct {
119 pub const base_tag = Tag.fieldptr;
120 base: Inst,
121
122 positionals: struct {
123 object_ptr: *Inst,
124 field_name: *Inst,
125 },
126 kw_args: struct {},
127 };
128
129 pub const Deref = struct {
130 pub const base_tag = Tag.deref;
131 base: Inst,
132
133 positionals: struct {
134 ptr: *Inst,
135 },
136 kw_args: struct {},
137 };
138
139 pub const As = struct {
140 pub const base_tag = Tag.as;
141 base: Inst,
142
143 positionals: struct {
144 dest_type: *Inst,
145 value: *Inst,
146 },
147 kw_args: struct {},
148 };
149
150 pub const Asm = struct {
151 pub const base_tag = Tag.@"asm";
152 base: Inst,
153
154 positionals: struct {
155 asm_source: *Inst,
156 return_type: *Inst,
157 },
158 kw_args: struct {
159 @"volatile": bool = false,
160 output: ?*Inst = null,
161 inputs: []*Inst = &[0]*Inst{},
162 clobbers: []*Inst = &[0]*Inst{},
163 args: []*Inst = &[0]*Inst{},
164 },
165 };
166
167 pub const Unreachable = struct {
168 pub const base_tag = Tag.@"unreachable";
169 base: Inst,
170
171 positionals: struct {},
172 kw_args: struct {},
173 };
174
175 pub const Return = struct {
176 pub const base_tag = Tag.@"return";
177 base: Inst,
178
179 positionals: struct {},
180 kw_args: struct {},
181 };
182
183 pub const Fn = struct {
184 pub const base_tag = Tag.@"fn";
185 base: Inst,
186
187 positionals: struct {
188 fn_type: *Inst,
189 body: Module.Body,
190 },
191 kw_args: struct {},
192 };
193
194 pub const Export = struct {
195 pub const base_tag = Tag.@"export";
196 base: Inst,
197
198 positionals: struct {
199 symbol_name: *Inst,
200 value: *Inst,
201 },
202 kw_args: struct {},
203 };
204
205 pub const Primitive = struct {
206 pub const base_tag = Tag.primitive;
207 base: Inst,
208
209 positionals: struct {
210 tag: BuiltinType,
211 },
212 kw_args: struct {},
213
214 pub const BuiltinType = enum {
215 @"isize",
216 @"usize",
217 @"c_short",
218 @"c_ushort",
219 @"c_int",
220 @"c_uint",
221 @"c_long",
222 @"c_ulong",
223 @"c_longlong",
224 @"c_ulonglong",
225 @"c_longdouble",
226 @"c_void",
227 @"f16",
228 @"f32",
229 @"f64",
230 @"f128",
231 @"bool",
232 @"void",
233 @"noreturn",
234 @"type",
235 @"anyerror",
236 @"comptime_int",
237 @"comptime_float",
238
239 pub fn toType(self: BuiltinType) Type {
240 return switch (self) {
241 .@"isize" => Type.initTag(.@"isize"),
242 .@"usize" => Type.initTag(.@"usize"),
243 .@"c_short" => Type.initTag(.@"c_short"),
244 .@"c_ushort" => Type.initTag(.@"c_ushort"),
245 .@"c_int" => Type.initTag(.@"c_int"),
246 .@"c_uint" => Type.initTag(.@"c_uint"),
247 .@"c_long" => Type.initTag(.@"c_long"),
248 .@"c_ulong" => Type.initTag(.@"c_ulong"),
249 .@"c_longlong" => Type.initTag(.@"c_longlong"),
250 .@"c_ulonglong" => Type.initTag(.@"c_ulonglong"),
251 .@"c_longdouble" => Type.initTag(.@"c_longdouble"),
252 .@"c_void" => Type.initTag(.@"c_void"),
253 .@"f16" => Type.initTag(.@"f16"),
254 .@"f32" => Type.initTag(.@"f32"),
255 .@"f64" => Type.initTag(.@"f64"),
256 .@"f128" => Type.initTag(.@"f128"),
257 .@"bool" => Type.initTag(.@"bool"),
258 .@"void" => Type.initTag(.@"void"),
259 .@"noreturn" => Type.initTag(.@"noreturn"),
260 .@"type" => Type.initTag(.@"type"),
261 .@"anyerror" => Type.initTag(.@"anyerror"),
262 .@"comptime_int" => Type.initTag(.@"comptime_int"),
263 .@"comptime_float" => Type.initTag(.@"comptime_float"),
264 };
265 }
266 };
267 };
268
269 pub const FnType = struct {
270 pub const base_tag = Tag.fntype;
271 base: Inst,
272
273 positionals: struct {
274 param_types: []*Inst,
275 return_type: *Inst,
276 },
277 kw_args: struct {
278 cc: std.builtin.CallingConvention = .Unspecified,
279 },
280 };
281
282 pub const IntCast = struct {
283 pub const base_tag = Tag.intcast;
284 base: Inst,
285
286 positionals: struct {
287 dest_type: *Inst,
288 value: *Inst,
289 },
290 kw_args: struct {},
291 };
292
293 pub const BitCast = struct {
294 pub const base_tag = Tag.bitcast;
295 base: Inst,
296
297 positionals: struct {
298 dest_type: *Inst,
299 operand: *Inst,
300 },
301 kw_args: struct {},
302 };
303
304 pub const ElemPtr = struct {
305 pub const base_tag = Tag.elemptr;
306 base: Inst,
307
308 positionals: struct {
309 array_ptr: *Inst,
310 index: *Inst,
311 },
312 kw_args: struct {},
313 };
314
315 pub const Add = struct {
316 pub const base_tag = Tag.add;
317 base: Inst,
318
319 positionals: struct {
320 lhs: *Inst,
321 rhs: *Inst,
322 },
323 kw_args: struct {},
324 };
325
326 pub const Cmp = struct {
327 pub const base_tag = Tag.cmp;
328 base: Inst,
329
330 positionals: struct {
331 lhs: *Inst,
332 op: std.math.CompareOperator,
333 rhs: *Inst,
334 },
335 kw_args: struct {},
336 };
337
338 pub const CondBr = struct {
339 pub const base_tag = Tag.condbr;
340 base: Inst,
341
342 positionals: struct {
343 condition: *Inst,
344 true_body: Module.Body,
345 false_body: Module.Body,
346 },
347 kw_args: struct {},
348 };
349
350 pub const IsNull = struct {
351 pub const base_tag = Tag.isnull;
352 base: Inst,
353
354 positionals: struct {
355 operand: *Inst,
356 },
357 kw_args: struct {},
358 };
359
360 pub const IsNonNull = struct {
361 pub const base_tag = Tag.isnonnull;
362 base: Inst,
363
364 positionals: struct {
365 operand: *Inst,
366 },
367 kw_args: struct {},
368 };
369};
370
371pub const ErrorMsg = struct {
372 byte_offset: usize,
373 msg: []const u8,
374};
375
376pub const Module = struct {
377 decls: []*Inst,
378 errors: []ErrorMsg,
379 arena: std.heap.ArenaAllocator,
380
381 pub const Body = struct {
382 instructions: []*Inst,
383 };
384
385 pub fn deinit(self: *Module, allocator: *Allocator) void {
386 allocator.free(self.decls);
387 allocator.free(self.errors);
388 self.arena.deinit();
389 self.* = undefined;
390 }
391
392 /// This is a debugging utility for rendering the tree to stderr.
393 pub fn dump(self: Module) void {
394 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
395 }
396
397 const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body });
398
399 /// The allocator is used for temporary storage, but this function always returns
400 /// with no resources allocated.
401 pub fn writeToStream(self: Module, allocator: *Allocator, stream: var) !void {
402 // First, build a map of *Inst to @ or % indexes
403 var inst_table = InstPtrTable.init(allocator);
404 defer inst_table.deinit();
405
406 try inst_table.ensureCapacity(self.decls.len);
407
408 for (self.decls) |decl, decl_i| {
409 try inst_table.putNoClobber(decl, .{ .index = decl_i, .fn_body = null });
410
411 if (decl.cast(Inst.Fn)) |fn_inst| {
412 for (fn_inst.positionals.body.instructions) |inst, inst_i| {
413 try inst_table.putNoClobber(inst, .{ .index = inst_i, .fn_body = &fn_inst.positionals.body });
414 }
415 }
416 }
417
418 for (self.decls) |decl, i| {
419 try stream.print("@{} ", .{i});
420 try self.writeInstToStream(stream, decl, &inst_table);
421 try stream.writeByte('\n');
422 }
423 }
424
425 fn writeInstToStream(
426 self: Module,
427 stream: var,
428 decl: *Inst,
429 inst_table: *const InstPtrTable,
430 ) @TypeOf(stream).Error!void {
431 // TODO I tried implementing this with an inline for loop and hit a compiler bug
432 switch (decl.tag) {
433 .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),
434 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
435 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
436 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
437 .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, decl, inst_table),
438 .deref => return self.writeInstToStreamGeneric(stream, .deref, decl, inst_table),
439 .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table),
440 .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
441 .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
442 .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table),
443 .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
444 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
445 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
446 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
447 .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
448 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
449 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table),
450 .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table),
451 .cmp => return self.writeInstToStreamGeneric(stream, .cmp, decl, inst_table),
452 .condbr => return self.writeInstToStreamGeneric(stream, .condbr, decl, inst_table),
453 .isnull => return self.writeInstToStreamGeneric(stream, .isnull, decl, inst_table),
454 .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, decl, inst_table),
455 }
456 }
457
458 fn writeInstToStreamGeneric(
459 self: Module,
460 stream: var,
461 comptime inst_tag: Inst.Tag,
462 base: *Inst,
463 inst_table: *const InstPtrTable,
464 ) !void {
465 const SpecificInst = Inst.TagToType(inst_tag);
466 const inst = @fieldParentPtr(SpecificInst, "base", base);
467 const Positionals = @TypeOf(inst.positionals);
468 try stream.writeAll("= " ++ @tagName(inst_tag) ++ "(");
469 const pos_fields = @typeInfo(Positionals).Struct.fields;
470 inline for (pos_fields) |arg_field, i| {
471 if (i != 0) {
472 try stream.writeAll(", ");
473 }
474 try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name), inst_table);
475 }
476
477 comptime var need_comma = pos_fields.len != 0;
478 const KW_Args = @TypeOf(inst.kw_args);
479 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| {
480 if (@typeInfo(arg_field.field_type) == .Optional) {
481 if (@field(inst.kw_args, arg_field.name)) |non_optional| {
482 if (need_comma) try stream.writeAll(", ");
483 try stream.print("{}=", .{arg_field.name});
484 try self.writeParamToStream(stream, non_optional, inst_table);
485 need_comma = true;
486 }
487 } else {
488 if (need_comma) try stream.writeAll(", ");
489 try stream.print("{}=", .{arg_field.name});
490 try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name), inst_table);
491 need_comma = true;
492 }
493 }
494
495 try stream.writeByte(')');
496 }
497
498 fn writeParamToStream(self: Module, stream: var, param: var, inst_table: *const InstPtrTable) !void {
499 if (@typeInfo(@TypeOf(param)) == .Enum) {
500 return stream.writeAll(@tagName(param));
501 }
502 switch (@TypeOf(param)) {
503 *Inst => return self.writeInstParamToStream(stream, param, inst_table),
504 []*Inst => {
505 try stream.writeByte('[');
506 for (param) |inst, i| {
507 if (i != 0) {
508 try stream.writeAll(", ");
509 }
510 try self.writeInstParamToStream(stream, inst, inst_table);
511 }
512 try stream.writeByte(']');
513 },
514 Module.Body => {
515 try stream.writeAll("{\n");
516 for (param.instructions) |inst, i| {
517 try stream.print(" %{} ", .{i});
518 try self.writeInstToStream(stream, inst, inst_table);
519 try stream.writeByte('\n');
520 }
521 try stream.writeByte('}');
522 },
523 bool => return stream.writeByte("01"[@boolToInt(param)]),
524 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
525 BigIntConst => return stream.print("{}", .{param}),
526 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
527 }
528 }
529
530 fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void {
531 const info = inst_table.getValue(inst).?;
532 const prefix = if (info.fn_body == null) "@" else "%";
533 try stream.print("{}{}", .{ prefix, info.index });
534 }
535};
536
537pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module {
538 var global_name_map = std.StringHashMap(usize).init(allocator);
539 defer global_name_map.deinit();
540
541 var parser: Parser = .{
542 .allocator = allocator,
543 .arena = std.heap.ArenaAllocator.init(allocator),
544 .i = 0,
545 .source = source,
546 .decls = std.ArrayList(*Inst).init(allocator),
547 .errors = std.ArrayList(ErrorMsg).init(allocator),
548 .global_name_map = &global_name_map,
549 };
550 errdefer parser.arena.deinit();
551
552 parser.parseRoot() catch |err| switch (err) {
553 error.ParseFailure => {
554 assert(parser.errors.items.len != 0);
555 },
556 else => |e| return e,
557 };
558 return Module{
559 .decls = parser.decls.toOwnedSlice(),
560 .errors = parser.errors.toOwnedSlice(),
561 .arena = parser.arena,
562 };
563}
564
565const Parser = struct {
566 allocator: *Allocator,
567 arena: std.heap.ArenaAllocator,
568 i: usize,
569 source: [:0]const u8,
570 errors: std.ArrayList(ErrorMsg),
571 decls: std.ArrayList(*Inst),
572 global_name_map: *std.StringHashMap(usize),
573
574 const Body = struct {
575 instructions: std.ArrayList(*Inst),
576 name_map: std.StringHashMap(usize),
577 };
578
579 fn parseBody(self: *Parser) !Module.Body {
580 var body_context = Body{
581 .instructions = std.ArrayList(*Inst).init(self.allocator),
582 .name_map = std.StringHashMap(usize).init(self.allocator),
583 };
584 defer body_context.instructions.deinit();
585 defer body_context.name_map.deinit();
586
587 try requireEatBytes(self, "{");
588 skipSpace(self);
589
590 while (true) : (self.i += 1) switch (self.source[self.i]) {
591 ';' => _ = try skipToAndOver(self, '\n'),
592 '%' => {
593 self.i += 1;
594 const ident = try skipToAndOver(self, ' ');
595 skipSpace(self);
596 try requireEatBytes(self, "=");
597 skipSpace(self);
598 const inst = try parseInstruction(self, &body_context);
599 const ident_index = body_context.instructions.items.len;
600 if (try body_context.name_map.put(ident, ident_index)) |_| {
601 return self.fail("redefinition of identifier '{}'", .{ident});
602 }
603 try body_context.instructions.append(inst);
604 continue;
605 },
606 ' ', '\n' => continue,
607 '}' => {
608 self.i += 1;
609 break;
610 },
611 else => |byte| return self.failByte(byte),
612 };
613
614 // Move the instructions to the arena
615 const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len);
616 mem.copy(*Inst, instrs, body_context.instructions.items);
617 return Module.Body{ .instructions = instrs };
618 }
619
620 fn parseStringLiteral(self: *Parser) ![]u8 {
621 const start = self.i;
622 try self.requireEatBytes("\"");
623
624 while (true) : (self.i += 1) switch (self.source[self.i]) {
625 '"' => {
626 self.i += 1;
627 const span = self.source[start..self.i];
628 var bad_index: usize = undefined;
629 const parsed = std.zig.parseStringLiteral(&self.arena.allocator, span, &bad_index) catch |err| switch (err) {
630 error.InvalidCharacter => {
631 self.i = start + bad_index;
632 const bad_byte = self.source[self.i];
633 return self.fail("invalid string literal character: '{c}'\n", .{bad_byte});
634 },
635 else => |e| return e,
636 };
637 return parsed;
638 },
639 '\\' => {
640 self.i += 1;
641 continue;
642 },
643 0 => return self.failByte(0),
644 else => continue,
645 };
646 }
647
648 fn parseIntegerLiteral(self: *Parser) !BigIntConst {
649 const start = self.i;
650 if (self.source[self.i] == '-') self.i += 1;
651 while (true) : (self.i += 1) switch (self.source[self.i]) {
652 '0'...'9' => continue,
653 else => break,
654 };
655 const number_text = self.source[start..self.i];
656 const base = 10;
657 // TODO reuse the same array list for this
658 const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
659 const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len);
660 defer self.allocator.free(limbs_buffer);
661 const limb_len = std.math.big.int.calcSetStringLimbCount(base, number_text.len);
662 const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len);
663 var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
664 result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) {
665 error.InvalidCharacter => {
666 self.i = start;
667 return self.fail("invalid digit in integer literal", .{});
668 },
669 };
670 return result.toConst();
671 }
672
673 fn parseRoot(self: *Parser) !void {
674 // The IR format is designed so that it can be tokenized and parsed at the same time.
675 while (true) {
676 switch (self.source[self.i]) {
677 ';' => _ = try skipToAndOver(self, '\n'),
678 '@' => {
679 self.i += 1;
680 const ident = try skipToAndOver(self, ' ');
681 skipSpace(self);
682 try requireEatBytes(self, "=");
683 skipSpace(self);
684 const inst = try parseInstruction(self, null);
685 const ident_index = self.decls.items.len;
686 if (try self.global_name_map.put(ident, ident_index)) |_| {
687 return self.fail("redefinition of identifier '{}'", .{ident});
688 }
689 try self.decls.append(inst);
690 },
691 ' ', '\n' => self.i += 1,
692 0 => break,
693 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
694 }
695 }
696 }
697
698 fn eatByte(self: *Parser, byte: u8) bool {
699 if (self.source[self.i] != byte) return false;
700 self.i += 1;
701 return true;
702 }
703
704 fn skipSpace(self: *Parser) void {
705 while (self.source[self.i] == ' ' or self.source[self.i] == '\n') {
706 self.i += 1;
707 }
708 }
709
710 fn requireEatBytes(self: *Parser, bytes: []const u8) !void {
711 const start = self.i;
712 for (bytes) |byte| {
713 if (self.source[self.i] != byte) {
714 self.i = start;
715 return self.fail("expected '{}'", .{bytes});
716 }
717 self.i += 1;
718 }
719 }
720
721 fn skipToAndOver(self: *Parser, byte: u8) ![]const u8 {
722 const start_i = self.i;
723 while (self.source[self.i] != 0) : (self.i += 1) {
724 if (self.source[self.i] == byte) {
725 const result = self.source[start_i..self.i];
726 self.i += 1;
727 return result;
728 }
729 }
730 return self.fail("unexpected EOF", .{});
731 }
732
733 /// ParseFailure is an internal error code; handled in `parse`.
734 const InnerError = error{ ParseFailure, OutOfMemory };
735
736 fn failByte(self: *Parser, byte: u8) InnerError {
737 if (byte == 0) {
738 return self.fail("unexpected EOF", .{});
739 } else {
740 return self.fail("unexpected byte: '{c}'", .{byte});
741 }
742 }
743
744 fn fail(self: *Parser, comptime format: []const u8, args: var) InnerError {
745 @setCold(true);
746 const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args);
747 (try self.errors.addOne()).* = .{
748 .byte_offset = self.i,
749 .msg = msg,
750 };
751 return error.ParseFailure;
752 }
753
754 fn parseInstruction(self: *Parser, body_ctx: ?*Body) InnerError!*Inst {
755 const fn_name = try skipToAndOver(self, '(');
756 inline for (@typeInfo(Inst.Tag).Enum.fields) |field| {
757 if (mem.eql(u8, field.name, fn_name)) {
758 const tag = @field(Inst.Tag, field.name);
759 return parseInstructionGeneric(self, field.name, Inst.TagToType(tag), body_ctx);
760 }
761 }
762 return self.fail("unknown instruction '{}'", .{fn_name});
763 }
764
765 fn parseInstructionGeneric(
766 self: *Parser,
767 comptime fn_name: []const u8,
768 comptime InstType: type,
769 body_ctx: ?*Body,
770 ) !*Inst {
771 const inst_specific = try self.arena.allocator.create(InstType);
772 inst_specific.base = .{
773 .src = self.i,
774 .tag = InstType.base_tag,
775 };
776
777 if (@hasField(InstType, "ty")) {
778 inst_specific.ty = opt_type orelse {
779 return self.fail("instruction '" ++ fn_name ++ "' requires type", .{});
780 };
781 }
782
783 const Positionals = @TypeOf(inst_specific.positionals);
784 inline for (@typeInfo(Positionals).Struct.fields) |arg_field| {
785 if (self.source[self.i] == ',') {
786 self.i += 1;
787 skipSpace(self);
788 } else if (self.source[self.i] == ')') {
789 return self.fail("expected positional parameter '{}'", .{arg_field.name});
790 }
791 @field(inst_specific.positionals, arg_field.name) = try parseParameterGeneric(
792 self,
793 arg_field.field_type,
794 body_ctx,
795 );
796 skipSpace(self);
797 }
798
799 const KW_Args = @TypeOf(inst_specific.kw_args);
800 inst_specific.kw_args = .{}; // assign defaults
801 skipSpace(self);
802 while (eatByte(self, ',')) {
803 skipSpace(self);
804 const name = try skipToAndOver(self, '=');
805 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| {
806 const field_name = arg_field.name;
807 if (mem.eql(u8, name, field_name)) {
808 const NonOptional = switch (@typeInfo(arg_field.field_type)) {
809 .Optional => |info| info.child,
810 else => arg_field.field_type,
811 };
812 @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(self, NonOptional, body_ctx);
813 break;
814 }
815 } else {
816 return self.fail("unrecognized keyword parameter: '{}'", .{name});
817 }
818 skipSpace(self);
819 }
820 try requireEatBytes(self, ")");
821
822 return &inst_specific.base;
823 }
824
825 fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T {
826 if (@typeInfo(T) == .Enum) {
827 const start = self.i;
828 while (true) : (self.i += 1) switch (self.source[self.i]) {
829 ' ', '\n', ',', ')' => {
830 const enum_name = self.source[start..self.i];
831 return std.meta.stringToEnum(T, enum_name) orelse {
832 return self.fail("tag '{}' not a member of enum '{}'", .{ enum_name, @typeName(T) });
833 };
834 },
835 0 => return self.failByte(0),
836 else => continue,
837 };
838 }
839 switch (T) {
840 Module.Body => return parseBody(self),
841 bool => {
842 const bool_value = switch (self.source[self.i]) {
843 '0' => false,
844 '1' => true,
845 else => |byte| return self.fail("expected '0' or '1' for boolean value, found {c}", .{byte}),
846 };
847 self.i += 1;
848 return bool_value;
849 },
850 []*Inst => {
851 try requireEatBytes(self, "[");
852 skipSpace(self);
853 if (eatByte(self, ']')) return &[0]*Inst{};
854
855 var instructions = std.ArrayList(*Inst).init(&self.arena.allocator);
856 while (true) {
857 skipSpace(self);
858 try instructions.append(try parseParameterInst(self, body_ctx));
859 skipSpace(self);
860 if (!eatByte(self, ',')) break;
861 }
862 try requireEatBytes(self, "]");
863 return instructions.toOwnedSlice();
864 },
865 *Inst => return parseParameterInst(self, body_ctx),
866 []u8, []const u8 => return self.parseStringLiteral(),
867 BigIntConst => return self.parseIntegerLiteral(),
868 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
869 }
870 return self.fail("TODO parse parameter {}", .{@typeName(T)});
871 }
872
873 fn parseParameterInst(self: *Parser, body_ctx: ?*Body) !*Inst {
874 const local_ref = switch (self.source[self.i]) {
875 '@' => false,
876 '%' => true,
877 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
878 };
879 const map = if (local_ref)
880 if (body_ctx) |bc|
881 &bc.name_map
882 else
883 return self.fail("referencing a % instruction in global scope", .{})
884 else
885 self.global_name_map;
886
887 self.i += 1;
888 const name_start = self.i;
889 while (true) : (self.i += 1) switch (self.source[self.i]) {
890 0, ' ', '\n', ',', ')', ']' => break,
891 else => continue,
892 };
893 const ident = self.source[name_start..self.i];
894 const kv = map.get(ident) orelse {
895 const bad_name = self.source[name_start - 1 .. self.i];
896 self.i = name_start - 1;
897 return self.fail("unrecognized identifier: {}", .{bad_name});
898 };
899 if (local_ref) {
900 return body_ctx.?.instructions.items[kv.value];
901 } else {
902 return self.decls.items[kv.value];
903 }
904 }
905};
906
907pub fn emit_zir(allocator: *Allocator, old_module: ir.Module) !Module {
908 var ctx: EmitZIR = .{
909 .allocator = allocator,
910 .decls = std.ArrayList(*Inst).init(allocator),
911 .decl_table = std.AutoHashMap(*ir.Inst, *Inst).init(allocator),
912 .arena = std.heap.ArenaAllocator.init(allocator),
913 .old_module = &old_module,
914 };
915 defer ctx.decls.deinit();
916 defer ctx.decl_table.deinit();
917 errdefer ctx.arena.deinit();
918
919 try ctx.emit();
920
921 return Module{
922 .decls = ctx.decls.toOwnedSlice(),
923 .arena = ctx.arena,
924 .errors = &[0]ErrorMsg{},
925 };
926}
927
928const EmitZIR = struct {
929 allocator: *Allocator,
930 arena: std.heap.ArenaAllocator,
931 old_module: *const ir.Module,
932 decls: std.ArrayList(*Inst),
933 decl_table: std.AutoHashMap(*ir.Inst, *Inst),
934
935 fn emit(self: *EmitZIR) !void {
936 for (self.old_module.exports) |module_export| {
937 const export_value = try self.emitTypedValue(module_export.src, module_export.typed_value);
938 const symbol_name = try self.emitStringLiteral(module_export.src, module_export.name);
939 const export_inst = try self.arena.allocator.create(Inst.Export);
940 export_inst.* = .{
941 .base = .{ .src = module_export.src, .tag = Inst.Export.base_tag },
942 .positionals = .{
943 .symbol_name = symbol_name,
944 .value = export_value,
945 },
946 .kw_args = .{},
947 };
948 try self.decls.append(&export_inst.base);
949 }
950 }
951
952 fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
953 if (inst.cast(ir.Inst.Constant)) |const_inst| {
954 if (self.decl_table.getValue(inst)) |decl| {
955 return decl;
956 }
957 const new_decl = try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
958 try self.decl_table.putNoClobber(inst, new_decl);
959 return new_decl;
960 } else {
961 return inst_table.getValue(inst).?;
962 }
963 }
964
965 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
966 const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
967 const int_inst = try self.arena.allocator.create(Inst.Int);
968 int_inst.* = .{
969 .base = .{ .src = src, .tag = Inst.Int.base_tag },
970 .positionals = .{
971 .int = val.toBigInt(big_int_space),
972 },
973 .kw_args = .{},
974 };
975 try self.decls.append(&int_inst.base);
976 return &int_inst.base;
977 }
978
979 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: ir.TypedValue) Allocator.Error!*Inst {
980 switch (typed_value.ty.zigTypeTag()) {
981 .Pointer => {
982 const ptr_elem_type = typed_value.ty.elemType();
983 switch (ptr_elem_type.zigTypeTag()) {
984 .Array => {
985 // TODO more checks to make sure this can be emitted as a string literal
986 //const array_elem_type = ptr_elem_type.elemType();
987 //if (array_elem_type.eql(Type.initTag(.u8)) and
988 // ptr_elem_type.hasSentinel(Value.initTag(.zero)))
989 //{
990 //}
991 const bytes = try typed_value.val.toAllocatedBytes(&self.arena.allocator);
992 return self.emitStringLiteral(src, bytes);
993 },
994 else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}),
995 }
996 },
997 .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val),
998 .Int => {
999 const as_inst = try self.arena.allocator.create(Inst.As);
1000 as_inst.* = .{
1001 .base = .{ .src = src, .tag = Inst.As.base_tag },
1002 .positionals = .{
1003 .dest_type = try self.emitType(src, typed_value.ty),
1004 .value = try self.emitComptimeIntVal(src, typed_value.val),
1005 },
1006 .kw_args = .{},
1007 };
1008 try self.decls.append(&as_inst.base);
1009
1010 return &as_inst.base;
1011 },
1012 .Type => {
1013 const ty = typed_value.val.toType();
1014 return self.emitType(src, ty);
1015 },
1016 .Fn => {
1017 const index = typed_value.val.cast(Value.Payload.Function).?.index;
1018 const module_fn = self.old_module.fns[index];
1019
1020 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
1021 defer inst_table.deinit();
1022
1023 var instructions = std.ArrayList(*Inst).init(self.allocator);
1024 defer instructions.deinit();
1025
1026 try self.emitBody(module_fn.body, &inst_table, &instructions);
1027
1028 const fn_type = try self.emitType(src, module_fn.fn_type);
1029
1030 const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
1031 mem.copy(*Inst, arena_instrs, instructions.items);
1032
1033 const fn_inst = try self.arena.allocator.create(Inst.Fn);
1034 fn_inst.* = .{
1035 .base = .{ .src = src, .tag = Inst.Fn.base_tag },
1036 .positionals = .{
1037 .fn_type = fn_type,
1038 .body = .{ .instructions = arena_instrs },
1039 },
1040 .kw_args = .{},
1041 };
1042 try self.decls.append(&fn_inst.base);
1043 return &fn_inst.base;
1044 },
1045 else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),
1046 }
1047 }
1048
1049 fn emitTrivial(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {
1050 const new_inst = try self.arena.allocator.create(T);
1051 new_inst.* = .{
1052 .base = .{ .src = src, .tag = T.base_tag },
1053 .positionals = .{},
1054 .kw_args = .{},
1055 };
1056 return &new_inst.base;
1057 }
1058
1059 fn emitBody(
1060 self: *EmitZIR,
1061 body: ir.Module.Body,
1062 inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
1063 instructions: *std.ArrayList(*Inst),
1064 ) Allocator.Error!void {
1065 for (body.instructions) |inst| {
1066 const new_inst = switch (inst.tag) {
1067 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
1068 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
1069 .ret => try self.emitTrivial(inst.src, Inst.Return),
1070 .constant => unreachable, // excluded from function bodies
1071 .assembly => blk: {
1072 const old_inst = inst.cast(ir.Inst.Assembly).?;
1073 const new_inst = try self.arena.allocator.create(Inst.Asm);
1074
1075 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
1076 for (inputs) |*elem, i| {
1077 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
1078 }
1079
1080 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
1081 for (clobbers) |*elem, i| {
1082 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
1083 }
1084
1085 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1086 for (args) |*elem, i| {
1087 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
1088 }
1089
1090 new_inst.* = .{
1091 .base = .{ .src = inst.src, .tag = Inst.Asm.base_tag },
1092 .positionals = .{
1093 .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
1094 .return_type = try self.emitType(inst.src, inst.ty),
1095 },
1096 .kw_args = .{
1097 .@"volatile" = old_inst.args.is_volatile,
1098 .output = if (old_inst.args.output) |o|
1099 try self.emitStringLiteral(inst.src, o)
1100 else
1101 null,
1102 .inputs = inputs,
1103 .clobbers = clobbers,
1104 .args = args,
1105 },
1106 };
1107 break :blk &new_inst.base;
1108 },
1109 .ptrtoint => blk: {
1110 const old_inst = inst.cast(ir.Inst.PtrToInt).?;
1111 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
1112 new_inst.* = .{
1113 .base = .{ .src = inst.src, .tag = Inst.PtrToInt.base_tag },
1114 .positionals = .{
1115 .ptr = try self.resolveInst(inst_table, old_inst.args.ptr),
1116 },
1117 .kw_args = .{},
1118 };
1119 break :blk &new_inst.base;
1120 },
1121 .bitcast => blk: {
1122 const old_inst = inst.cast(ir.Inst.BitCast).?;
1123 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1124 new_inst.* = .{
1125 .base = .{ .src = inst.src, .tag = Inst.BitCast.base_tag },
1126 .positionals = .{
1127 .dest_type = try self.emitType(inst.src, inst.ty),
1128 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1129 },
1130 .kw_args = .{},
1131 };
1132 break :blk &new_inst.base;
1133 },
1134 .cmp => blk: {
1135 const old_inst = inst.cast(ir.Inst.Cmp).?;
1136 const new_inst = try self.arena.allocator.create(Inst.Cmp);
1137 new_inst.* = .{
1138 .base = .{ .src = inst.src, .tag = Inst.Cmp.base_tag },
1139 .positionals = .{
1140 .lhs = try self.resolveInst(inst_table, old_inst.args.lhs),
1141 .rhs = try self.resolveInst(inst_table, old_inst.args.rhs),
1142 .op = old_inst.args.op,
1143 },
1144 .kw_args = .{},
1145 };
1146 break :blk &new_inst.base;
1147 },
1148 .condbr => blk: {
1149 const old_inst = inst.cast(ir.Inst.CondBr).?;
1150
1151 var true_body = std.ArrayList(*Inst).init(self.allocator);
1152 var false_body = std.ArrayList(*Inst).init(self.allocator);
1153
1154 defer true_body.deinit();
1155 defer false_body.deinit();
1156
1157 try self.emitBody(old_inst.args.true_body, inst_table, &true_body);
1158 try self.emitBody(old_inst.args.false_body, inst_table, &false_body);
1159
1160 const new_inst = try self.arena.allocator.create(Inst.CondBr);
1161 new_inst.* = .{
1162 .base = .{ .src = inst.src, .tag = Inst.CondBr.base_tag },
1163 .positionals = .{
1164 .condition = try self.resolveInst(inst_table, old_inst.args.condition),
1165 .true_body = .{ .instructions = true_body.toOwnedSlice() },
1166 .false_body = .{ .instructions = false_body.toOwnedSlice() },
1167 },
1168 .kw_args = .{},
1169 };
1170 break :blk &new_inst.base;
1171 },
1172 .isnull => blk: {
1173 const old_inst = inst.cast(ir.Inst.IsNull).?;
1174 const new_inst = try self.arena.allocator.create(Inst.IsNull);
1175 new_inst.* = .{
1176 .base = .{ .src = inst.src, .tag = Inst.IsNull.base_tag },
1177 .positionals = .{
1178 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1179 },
1180 .kw_args = .{},
1181 };
1182 break :blk &new_inst.base;
1183 },
1184 .isnonnull => blk: {
1185 const old_inst = inst.cast(ir.Inst.IsNonNull).?;
1186 const new_inst = try self.arena.allocator.create(Inst.IsNonNull);
1187 new_inst.* = .{
1188 .base = .{ .src = inst.src, .tag = Inst.IsNonNull.base_tag },
1189 .positionals = .{
1190 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1191 },
1192 .kw_args = .{},
1193 };
1194 break :blk &new_inst.base;
1195 },
1196 };
1197 try instructions.append(new_inst);
1198 try inst_table.putNoClobber(inst, new_inst);
1199 }
1200 }
1201
1202 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
1203 switch (ty.tag()) {
1204 .isize => return self.emitPrimitiveType(src, .isize),
1205 .usize => return self.emitPrimitiveType(src, .usize),
1206 .c_short => return self.emitPrimitiveType(src, .c_short),
1207 .c_ushort => return self.emitPrimitiveType(src, .c_ushort),
1208 .c_int => return self.emitPrimitiveType(src, .c_int),
1209 .c_uint => return self.emitPrimitiveType(src, .c_uint),
1210 .c_long => return self.emitPrimitiveType(src, .c_long),
1211 .c_ulong => return self.emitPrimitiveType(src, .c_ulong),
1212 .c_longlong => return self.emitPrimitiveType(src, .c_longlong),
1213 .c_ulonglong => return self.emitPrimitiveType(src, .c_ulonglong),
1214 .c_longdouble => return self.emitPrimitiveType(src, .c_longdouble),
1215 .c_void => return self.emitPrimitiveType(src, .c_void),
1216 .f16 => return self.emitPrimitiveType(src, .f16),
1217 .f32 => return self.emitPrimitiveType(src, .f32),
1218 .f64 => return self.emitPrimitiveType(src, .f64),
1219 .f128 => return self.emitPrimitiveType(src, .f128),
1220 .anyerror => return self.emitPrimitiveType(src, .anyerror),
1221 else => switch (ty.zigTypeTag()) {
1222 .Bool => return self.emitPrimitiveType(src, .bool),
1223 .Void => return self.emitPrimitiveType(src, .void),
1224 .NoReturn => return self.emitPrimitiveType(src, .noreturn),
1225 .Type => return self.emitPrimitiveType(src, .type),
1226 .ComptimeInt => return self.emitPrimitiveType(src, .comptime_int),
1227 .ComptimeFloat => return self.emitPrimitiveType(src, .comptime_float),
1228 .Fn => {
1229 const param_types = try self.allocator.alloc(Type, ty.fnParamLen());
1230 defer self.allocator.free(param_types);
1231
1232 ty.fnParamTypes(param_types);
1233 const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len);
1234 for (param_types) |param_type, i| {
1235 emitted_params[i] = try self.emitType(src, param_type);
1236 }
1237
1238 const fntype_inst = try self.arena.allocator.create(Inst.FnType);
1239 fntype_inst.* = .{
1240 .base = .{ .src = src, .tag = Inst.FnType.base_tag },
1241 .positionals = .{
1242 .param_types = emitted_params,
1243 .return_type = try self.emitType(src, ty.fnReturnType()),
1244 },
1245 .kw_args = .{
1246 .cc = ty.fnCallingConvention(),
1247 },
1248 };
1249 try self.decls.append(&fntype_inst.base);
1250 return &fntype_inst.base;
1251 },
1252 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
1253 },
1254 }
1255 }
1256
1257 fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst {
1258 const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
1259 primitive_inst.* = .{
1260 .base = .{ .src = src, .tag = Inst.Primitive.base_tag },
1261 .positionals = .{
1262 .tag = tag,
1263 },
1264 .kw_args = .{},
1265 };
1266 try self.decls.append(&primitive_inst.base);
1267 return &primitive_inst.base;
1268 }
1269
1270 fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {
1271 const str_inst = try self.arena.allocator.create(Inst.Str);
1272 str_inst.* = .{
1273 .base = .{ .src = src, .tag = Inst.Str.base_tag },
1274 .positionals = .{
1275 .bytes = str,
1276 },
1277 .kw_args = .{},
1278 };
1279 try self.decls.append(&str_inst.base);
1280 return &str_inst.base;
1281 }
1282};
src-self-hosted/libc_installation.zig-1
......@@ -1,6 +1,5 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const util = @import("util.zig");
43const Target = std.Target;
54const fs = std.fs;
65const Allocator = std.mem.Allocator;
src-self-hosted/link.zig+749-291
......@@ -3,56 +3,74 @@ const mem = std.mem;
33const assert = std.debug.assert;
44const Allocator = std.mem.Allocator;
55const ir = @import("ir.zig");
6const Module = @import("Module.zig");
67const fs = std.fs;
78const elf = std.elf;
89const codegen = @import("codegen.zig");
910
1011const default_entry_addr = 0x8000000;
1112
12pub const ErrorMsg = struct {
13 byte_offset: usize,
14 msg: []const u8,
15};
16
17pub const Result = struct {
18 errors: []ErrorMsg,
19
20 pub fn deinit(self: *Result, allocator: *mem.Allocator) void {
21 for (self.errors) |err| {
22 allocator.free(err.msg);
23 }
24 allocator.free(self.errors);
25 self.* = undefined;
26 }
13pub const Options = struct {
14 target: std.Target,
15 output_mode: std.builtin.OutputMode,
16 link_mode: std.builtin.LinkMode,
17 object_format: std.builtin.ObjectFormat,
18 /// Used for calculating how much space to reserve for symbols in case the binary file
19 /// does not already have a symbol table.
20 symbol_count_hint: u64 = 32,
21 /// Used for calculating how much space to reserve for executable program code in case
22 /// the binary file deos not already have such a section.
23 program_code_size_hint: u64 = 256 * 1024,
2724};
2825
2926/// Attempts incremental linking, if the file already exists.
3027/// If incremental linking fails, falls back to truncating the file and rewriting it.
3128/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
3229/// This operation is not atomic.
33pub fn updateFilePath(
30pub fn openBinFilePath(
3431 allocator: *Allocator,
35 module: ir.Module,
3632 dir: fs.Dir,
3733 sub_path: []const u8,
38) !Result {
39 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(module) });
40 defer file.close();
41
42 return updateFile(allocator, module, file);
34 options: Options,
35) !ElfFile {
36 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) });
37 errdefer file.close();
38
39 var bin_file = try openBinFile(allocator, file, options);
40 bin_file.owns_file_handle = true;
41 return bin_file;
4342}
4443
4544/// Atomically overwrites the old file, if present.
4645pub fn writeFilePath(
4746 allocator: *Allocator,
48 module: ir.Module,
4947 dir: fs.Dir,
5048 sub_path: []const u8,
51) !Result {
52 const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(module) });
49 module: Module,
50 errors: *std.ArrayList(Module.ErrorMsg),
51) !void {
52 const options: Options = .{
53 .target = module.target,
54 .output_mode = module.output_mode,
55 .link_mode = module.link_mode,
56 .object_format = module.object_format,
57 .symbol_count_hint = module.decls.items.len,
58 };
59 const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(options) });
5360 defer af.deinit();
5461
55 const result = try writeFile(allocator, module, af.file);
62 const elf_file = try createElfFile(allocator, af.file, options);
63 for (module.decls.items) |decl| {
64 try elf_file.updateDecl(module, decl, errors);
65 }
66 try elf_file.flush();
67 if (elf_file.error_flags.no_entry_point_found) {
68 try errors.ensureCapacity(errors.items.len + 1);
69 errors.appendAssumeCapacity(.{
70 .byte_offset = 0,
71 .msg = try std.fmt.allocPrint(errors.allocator, "no entry point found", .{}),
72 });
73 }
5674 try af.finish();
5775 return result;
5876}
......@@ -62,58 +80,126 @@ pub fn writeFilePath(
6280/// Returns an error if `file` is not already open with +read +write +seek abilities.
6381/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
6482/// This operation is not atomic.
65pub fn updateFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
66 return updateFileInner(allocator, module, file) catch |err| switch (err) {
83pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
84 return openBinFileInner(allocator, file, options) catch |err| switch (err) {
6785 error.IncrFailed => {
68 return writeFile(allocator, module, file);
86 return createElfFile(allocator, file, options);
6987 },
7088 else => |e| return e,
7189 };
7290}
7391
74const Update = struct {
75 file: fs.File,
76 module: *const ir.Module,
92pub const ElfFile = struct {
93 allocator: *Allocator,
94 file: ?fs.File,
95 owns_file_handle: bool,
96 options: Options,
97 ptr_width: enum { p32, p64 },
7798
7899 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
79100 /// Same order as in the file.
80 sections: std.ArrayList(elf.Elf64_Shdr),
81 shdr_table_offset: ?u64,
101 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
102 shdr_table_offset: ?u64 = null,
82103
83104 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
84105 /// Same order as in the file.
85 program_headers: std.ArrayList(elf.Elf64_Phdr),
86 phdr_table_offset: ?u64,
106 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
107 phdr_table_offset: ?u64 = null,
87108 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
88 phdr_load_re_index: ?u16,
89 entry_addr: ?u64,
109 phdr_load_re_index: ?u16 = null,
110 /// The index into the program headers of the global offset table.
111 /// It needs PT_LOAD and Read flags.
112 phdr_got_index: ?u16 = null,
113 entry_addr: ?u64 = null,
114
115 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
116 shstrtab_index: ?u16 = null,
117
118 text_section_index: ?u16 = null,
119 symtab_section_index: ?u16 = null,
120 got_section_index: ?u16 = null,
121
122 /// The same order as in the file. ELF requires global symbols to all be after the
123 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
124 /// write them at the end. These are only the local symbols. The length of this array
125 /// is the value used for sh_info in the .symtab section.
126 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
127 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
128
129 /// Same order as in the file. The value is the absolute vaddr value.
130 /// If the vaddr of the executable program header changes, the entire
131 /// offset table needs to be rewritten.
132 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
133
134 phdr_table_dirty: bool = false,
135 shdr_table_dirty: bool = false,
136 shstrtab_dirty: bool = false,
137 offset_table_count_dirty: bool = false,
138
139 error_flags: ErrorFlags = ErrorFlags{},
140
141 pub const ErrorFlags = struct {
142 no_entry_point_found: bool = false,
143 };
90144
91 shstrtab: std.ArrayList(u8),
92 shstrtab_index: ?u16,
145 pub const Decl = struct {
146 /// Each decl always gets a local symbol with the fully qualified name.
147 /// The vaddr and size are found here directly.
148 /// The file offset is found by computing the vaddr offset from the section vaddr
149 /// the symbol references, and adding that to the file offset of the section.
150 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
151 /// offset table entry.
152 local_sym_index: u32,
153 /// This field is undefined for symbols with size = 0.
154 offset_table_index: u32,
155
156 pub const empty = Decl{
157 .local_sym_index = 0,
158 .offset_table_index = undefined,
159 };
160 };
93161
94 text_section_index: ?u16,
95 symtab_section_index: ?u16,
162 pub const Export = struct {
163 sym_index: ?u32 = null,
164 };
96165
97 /// The same order as in the file
98 symbols: std.ArrayList(elf.Elf64_Sym),
166 pub fn deinit(self: *ElfFile) void {
167 self.sections.deinit(self.allocator);
168 self.program_headers.deinit(self.allocator);
169 self.shstrtab.deinit(self.allocator);
170 self.local_symbols.deinit(self.allocator);
171 self.global_symbols.deinit(self.allocator);
172 self.offset_table.deinit(self.allocator);
173 if (self.owns_file_handle) {
174 if (self.file) |f| f.close();
175 }
176 }
99177
100 errors: std.ArrayList(ErrorMsg),
178 pub fn makeExecutable(self: *ElfFile) !void {
179 assert(self.owns_file_handle);
180 if (self.file) |f| {
181 f.close();
182 self.file = null;
183 }
184 }
101185
102 fn deinit(self: *Update) void {
103 self.sections.deinit();
104 self.program_headers.deinit();
105 self.shstrtab.deinit();
106 self.symbols.deinit();
107 self.errors.deinit();
186 pub fn makeWritable(self: *ElfFile, dir: fs.Dir, sub_path: []const u8) !void {
187 assert(self.owns_file_handle);
188 if (self.file != null) return;
189 self.file = try dir.createFile(sub_path, .{
190 .truncate = false,
191 .read = true,
192 .mode = determineMode(self.options),
193 });
108194 }
109195
110 // `expand_num / expand_den` is the factor of padding when allocation
196 // `alloc_num / alloc_den` is the factor of padding when allocation
111197 const alloc_num = 4;
112198 const alloc_den = 3;
113199
114200 /// Returns end pos of collision, if any.
115 fn detectAllocCollision(self: *Update, start: u64, size: u64) ?u64 {
116 const small_ptr = self.module.target.cpu.arch.ptrBitWidth() == 32;
201 fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 {
202 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
117203 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
118204 if (start < ehdr_size)
119205 return ehdr_size;
......@@ -157,7 +243,7 @@ const Update = struct {
157243 return null;
158244 }
159245
160 fn allocatedSize(self: *Update, start: u64) u64 {
246 fn allocatedSize(self: *ElfFile, start: u64) u64 {
161247 var min_pos: u64 = std.math.maxInt(u64);
162248 if (self.shdr_table_offset) |off| {
163249 if (off > start and off < min_pos) min_pos = off;
......@@ -176,7 +262,7 @@ const Update = struct {
176262 return min_pos - start;
177263 }
178264
179 fn findFreeSpace(self: *Update, object_size: u64, min_alignment: u16) u64 {
265 fn findFreeSpace(self: *ElfFile, object_size: u64, min_alignment: u16) u64 {
180266 var start: u64 = 0;
181267 while (self.detectAllocCollision(start, object_size)) |item_end| {
182268 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
......@@ -184,73 +270,86 @@ const Update = struct {
184270 return start;
185271 }
186272
187 fn makeString(self: *Update, bytes: []const u8) !u32 {
273 fn makeString(self: *ElfFile, bytes: []const u8) !u32 {
274 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
188275 const result = self.shstrtab.items.len;
189 try self.shstrtab.appendSlice(bytes);
190 try self.shstrtab.append(0);
276 self.shstrtab.appendSliceAssumeCapacity(bytes);
277 self.shstrtab.appendAssumeCapacity(0);
191278 return @intCast(u32, result);
192279 }
193280
194 fn perform(self: *Update) !void {
195 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
196 32 => .p32,
197 64 => .p64,
198 else => return error.UnsupportedArchitecture,
199 };
200 const small_ptr = switch (ptr_width) {
281 fn getString(self: *ElfFile, str_off: u32) []const u8 {
282 assert(str_off < self.shstrtab.items.len);
283 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
284 }
285
286 fn updateString(self: *ElfFile, old_str_off: u32, new_name: []const u8) !u32 {
287 const existing_name = self.getString(old_str_off);
288 if (mem.eql(u8, existing_name, new_name)) {
289 return old_str_off;
290 }
291 return self.makeString(new_name);
292 }
293
294 pub fn populateMissingMetadata(self: *ElfFile) !void {
295 const small_ptr = switch (self.ptr_width) {
201296 .p32 => true,
202297 .p64 => false,
203298 };
204 // This means the entire read-only executable program code needs to be rewritten.
205 var phdr_load_re_dirty = false;
206 var phdr_table_dirty = false;
207 var shdr_table_dirty = false;
208 var shstrtab_dirty = false;
209 var symtab_dirty = false;
210
299 const ptr_size: u8 = switch (self.ptr_width) {
300 .p32 => 4,
301 .p64 => 8,
302 };
211303 if (self.phdr_load_re_index == null) {
212304 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
213 const file_size = 256 * 1024;
305 const file_size = self.options.program_code_size_hint;
214306 const p_align = 0x1000;
215307 const off = self.findFreeSpace(file_size, p_align);
216308 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
217 try self.program_headers.append(.{
309 try self.program_headers.append(self.allocator, .{
218310 .p_type = elf.PT_LOAD,
219311 .p_offset = off,
220312 .p_filesz = file_size,
221313 .p_vaddr = default_entry_addr,
222314 .p_paddr = default_entry_addr,
223 .p_memsz = 0,
315 .p_memsz = file_size,
224316 .p_align = p_align,
225317 .p_flags = elf.PF_X | elf.PF_R,
226318 });
227319 self.entry_addr = null;
228 phdr_load_re_dirty = true;
229 phdr_table_dirty = true;
230 }
231 if (self.sections.items.len == 0) {
232 // There must always be a null section in index 0
233 try self.sections.append(.{
234 .sh_name = 0,
235 .sh_type = elf.SHT_NULL,
236 .sh_flags = 0,
237 .sh_addr = 0,
238 .sh_offset = 0,
239 .sh_size = 0,
240 .sh_link = 0,
241 .sh_info = 0,
242 .sh_addralign = 0,
243 .sh_entsize = 0,
320 self.phdr_table_dirty = true;
321 }
322 if (self.phdr_got_index == null) {
323 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
324 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
325 // We really only need ptr alignment but since we are using PROGBITS, linux requires
326 // page align.
327 const p_align = 0x1000;
328 const off = self.findFreeSpace(file_size, p_align);
329 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
330 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
331 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
332 // else in virtual memory.
333 const default_got_addr = 0x4000000;
334 try self.program_headers.append(self.allocator, .{
335 .p_type = elf.PT_LOAD,
336 .p_offset = off,
337 .p_filesz = file_size,
338 .p_vaddr = default_got_addr,
339 .p_paddr = default_got_addr,
340 .p_memsz = file_size,
341 .p_align = p_align,
342 .p_flags = elf.PF_R,
244343 });
245 shdr_table_dirty = true;
344 self.phdr_table_dirty = true;
246345 }
247346 if (self.shstrtab_index == null) {
248347 self.shstrtab_index = @intCast(u16, self.sections.items.len);
249348 assert(self.shstrtab.items.len == 0);
250 try self.shstrtab.append(0); // need a 0 at position 0
349 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
251350 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
252351 //std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
253 try self.sections.append(.{
352 try self.sections.append(self.allocator, .{
254353 .sh_name = try self.makeString(".shstrtab"),
255354 .sh_type = elf.SHT_STRTAB,
256355 .sh_flags = 0,
......@@ -262,14 +361,14 @@ const Update = struct {
262361 .sh_addralign = 1,
263362 .sh_entsize = 0,
264363 });
265 shstrtab_dirty = true;
266 shdr_table_dirty = true;
364 self.shstrtab_dirty = true;
365 self.shdr_table_dirty = true;
267366 }
268367 if (self.text_section_index == null) {
269368 self.text_section_index = @intCast(u16, self.sections.items.len);
270369 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
271370
272 try self.sections.append(.{
371 try self.sections.append(self.allocator, .{
273372 .sh_name = try self.makeString(".text"),
274373 .sh_type = elf.SHT_PROGBITS,
275374 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
......@@ -281,17 +380,35 @@ const Update = struct {
281380 .sh_addralign = phdr.p_align,
282381 .sh_entsize = 0,
283382 });
284 shdr_table_dirty = true;
383 self.shdr_table_dirty = true;
384 }
385 if (self.got_section_index == null) {
386 self.got_section_index = @intCast(u16, self.sections.items.len);
387 const phdr = &self.program_headers.items[self.phdr_got_index.?];
388
389 try self.sections.append(self.allocator, .{
390 .sh_name = try self.makeString(".got"),
391 .sh_type = elf.SHT_PROGBITS,
392 .sh_flags = elf.SHF_ALLOC,
393 .sh_addr = phdr.p_vaddr,
394 .sh_offset = phdr.p_offset,
395 .sh_size = phdr.p_filesz,
396 .sh_link = 0,
397 .sh_info = 0,
398 .sh_addralign = phdr.p_align,
399 .sh_entsize = 0,
400 });
401 self.shdr_table_dirty = true;
285402 }
286403 if (self.symtab_section_index == null) {
287404 self.symtab_section_index = @intCast(u16, self.sections.items.len);
288405 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
289406 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
290 const file_size = self.module.exports.len * each_size;
407 const file_size = self.options.symbol_count_hint * each_size;
291408 const off = self.findFreeSpace(file_size, min_align);
292409 //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
293410
294 try self.sections.append(.{
411 try self.sections.append(self.allocator, .{
295412 .sh_name = try self.makeString(".symtab"),
296413 .sh_type = elf.SHT_SYMTAB,
297414 .sh_flags = 0,
......@@ -300,42 +417,56 @@ const Update = struct {
300417 .sh_size = file_size,
301418 // The section header index of the associated string table.
302419 .sh_link = self.shstrtab_index.?,
303 .sh_info = @intCast(u32, self.module.exports.len),
420 .sh_info = @intCast(u32, self.local_symbols.items.len),
304421 .sh_addralign = min_align,
305422 .sh_entsize = each_size,
306423 });
307 symtab_dirty = true;
308 shdr_table_dirty = true;
424 self.shdr_table_dirty = true;
425 try self.writeSymbol(0);
309426 }
310 const shsize: u64 = switch (ptr_width) {
427 const shsize: u64 = switch (self.ptr_width) {
311428 .p32 => @sizeOf(elf.Elf32_Shdr),
312429 .p64 => @sizeOf(elf.Elf64_Shdr),
313430 };
314 const shalign: u16 = switch (ptr_width) {
431 const shalign: u16 = switch (self.ptr_width) {
315432 .p32 => @alignOf(elf.Elf32_Shdr),
316433 .p64 => @alignOf(elf.Elf64_Shdr),
317434 };
318435 if (self.shdr_table_offset == null) {
319436 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
320 shdr_table_dirty = true;
437 self.shdr_table_dirty = true;
321438 }
322 const phsize: u64 = switch (ptr_width) {
439 const phsize: u64 = switch (self.ptr_width) {
323440 .p32 => @sizeOf(elf.Elf32_Phdr),
324441 .p64 => @sizeOf(elf.Elf64_Phdr),
325442 };
326 const phalign: u16 = switch (ptr_width) {
443 const phalign: u16 = switch (self.ptr_width) {
327444 .p32 => @alignOf(elf.Elf32_Phdr),
328445 .p64 => @alignOf(elf.Elf64_Phdr),
329446 };
330447 if (self.phdr_table_offset == null) {
331448 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
332 phdr_table_dirty = true;
449 self.phdr_table_dirty = true;
333450 }
334 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
451 }
335452
336 try self.writeCodeAndSymbols(phdr_table_dirty, shdr_table_dirty);
453 /// Commit pending changes and write headers.
454 pub fn flush(self: *ElfFile) !void {
455 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
337456
338 if (phdr_table_dirty) {
457 // Unfortunately these have to be buffered and done at the end because ELF does not allow
458 // mixing local and global symbols within a symbol table.
459 try self.writeAllGlobalSymbols();
460
461 if (self.phdr_table_dirty) {
462 const phsize: u64 = switch (self.ptr_width) {
463 .p32 => @sizeOf(elf.Elf32_Phdr),
464 .p64 => @sizeOf(elf.Elf64_Phdr),
465 };
466 const phalign: u16 = switch (self.ptr_width) {
467 .p32 => @alignOf(elf.Elf32_Phdr),
468 .p64 => @alignOf(elf.Elf64_Phdr),
469 };
339470 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
340471 const needed_size = self.program_headers.items.len * phsize;
341472
......@@ -344,11 +475,10 @@ const Update = struct {
344475 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
345476 }
346477
347 const allocator = self.program_headers.allocator;
348 switch (ptr_width) {
478 switch (self.ptr_width) {
349479 .p32 => {
350 const buf = try allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
351 defer allocator.free(buf);
480 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
481 defer self.allocator.free(buf);
352482
353483 for (buf) |*phdr, i| {
354484 phdr.* = progHeaderTo32(self.program_headers.items[i]);
......@@ -356,11 +486,11 @@ const Update = struct {
356486 bswapAllFields(elf.Elf32_Phdr, phdr);
357487 }
358488 }
359 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
489 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
360490 },
361491 .p64 => {
362 const buf = try allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
363 defer allocator.free(buf);
492 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
493 defer self.allocator.free(buf);
364494
365495 for (buf) |*phdr, i| {
366496 phdr.* = self.program_headers.items[i];
......@@ -368,14 +498,15 @@ const Update = struct {
368498 bswapAllFields(elf.Elf64_Phdr, phdr);
369499 }
370500 }
371 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
501 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
372502 },
373503 }
504 self.phdr_table_dirty = false;
374505 }
375506
376507 {
377508 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
378 if (shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
509 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
379510 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
380511 const needed_size = self.shstrtab.items.len;
381512
......@@ -386,27 +517,35 @@ const Update = struct {
386517 shstrtab_sect.sh_size = needed_size;
387518 //std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
388519
389 try self.file.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
390 if (!shdr_table_dirty) {
520 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
521 if (!self.shdr_table_dirty) {
391522 // Then it won't get written with the others and we need to do it.
392523 try self.writeSectHeader(self.shstrtab_index.?);
393524 }
525 self.shstrtab_dirty = false;
394526 }
395527 }
396 if (shdr_table_dirty) {
528 if (self.shdr_table_dirty) {
529 const shsize: u64 = switch (self.ptr_width) {
530 .p32 => @sizeOf(elf.Elf32_Shdr),
531 .p64 => @sizeOf(elf.Elf64_Shdr),
532 };
533 const shalign: u16 = switch (self.ptr_width) {
534 .p32 => @alignOf(elf.Elf32_Shdr),
535 .p64 => @alignOf(elf.Elf64_Shdr),
536 };
397537 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
398 const needed_size = self.sections.items.len * phsize;
538 const needed_size = self.sections.items.len * shsize;
399539
400540 if (needed_size > allocated_size) {
401541 self.shdr_table_offset = null; // free the space
402 self.shdr_table_offset = self.findFreeSpace(needed_size, phalign);
542 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
403543 }
404544
405 const allocator = self.sections.allocator;
406 switch (ptr_width) {
545 switch (self.ptr_width) {
407546 .p32 => {
408 const buf = try allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
409 defer allocator.free(buf);
547 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
548 defer self.allocator.free(buf);
410549
411550 for (buf) |*shdr, i| {
412551 shdr.* = sectHeaderTo32(self.sections.items[i]);
......@@ -414,11 +553,11 @@ const Update = struct {
414553 bswapAllFields(elf.Elf32_Shdr, shdr);
415554 }
416555 }
417 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
556 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
418557 },
419558 .p64 => {
420 const buf = try allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
421 defer allocator.free(buf);
559 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
560 defer self.allocator.free(buf);
422561
423562 for (buf) |*shdr, i| {
424563 shdr.* = self.sections.items[i];
......@@ -427,42 +566,42 @@ const Update = struct {
427566 bswapAllFields(elf.Elf64_Shdr, shdr);
428567 }
429568 }
430 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
569 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
431570 },
432571 }
572 self.shdr_table_dirty = false;
433573 }
434 if (self.entry_addr == null and self.module.output_mode == .Exe) {
435 const msg = try std.fmt.allocPrint(self.errors.allocator, "no entry point found", .{});
436 errdefer self.errors.allocator.free(msg);
437 try self.errors.append(.{
438 .byte_offset = 0,
439 .msg = msg,
440 });
574 if (self.entry_addr == null and self.options.output_mode == .Exe) {
575 self.error_flags.no_entry_point_found = true;
441576 } else {
577 self.error_flags.no_entry_point_found = false;
442578 try self.writeElfHeader();
443579 }
444580 // TODO find end pos and truncate
581
582 // The point of flush() is to commit changes, so nothing should be dirty after this.
583 assert(!self.phdr_table_dirty);
584 assert(!self.shdr_table_dirty);
585 assert(!self.shstrtab_dirty);
586 assert(!self.offset_table_count_dirty);
587 const syms_sect = &self.sections.items[self.symtab_section_index.?];
588 assert(syms_sect.sh_info == self.local_symbols.items.len);
445589 }
446590
447 fn writeElfHeader(self: *Update) !void {
591 fn writeElfHeader(self: *ElfFile) !void {
448592 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
449593
450594 var index: usize = 0;
451595 hdr_buf[0..4].* = "\x7fELF".*;
452596 index += 4;
453597
454 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
455 32 => .p32,
456 64 => .p64,
457 else => return error.UnsupportedArchitecture,
458 };
459 hdr_buf[index] = switch (ptr_width) {
598 hdr_buf[index] = switch (self.ptr_width) {
460599 .p32 => elf.ELFCLASS32,
461600 .p64 => elf.ELFCLASS64,
462601 };
463602 index += 1;
464603
465 const endian = self.module.target.cpu.arch.endian();
604 const endian = self.options.target.cpu.arch.endian();
466605 hdr_buf[index] = switch (endian) {
467606 .Little => elf.ELFDATA2LSB,
468607 .Big => elf.ELFDATA2MSB,
......@@ -480,10 +619,10 @@ const Update = struct {
480619
481620 assert(index == 16);
482621
483 const elf_type = switch (self.module.output_mode) {
622 const elf_type = switch (self.options.output_mode) {
484623 .Exe => elf.ET.EXEC,
485624 .Obj => elf.ET.REL,
486 .Lib => switch (self.module.link_mode) {
625 .Lib => switch (self.options.link_mode) {
487626 .Static => elf.ET.REL,
488627 .Dynamic => elf.ET.DYN,
489628 },
......@@ -491,7 +630,7 @@ const Update = struct {
491630 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
492631 index += 2;
493632
494 const machine = self.module.target.cpu.arch.toElfMachine();
633 const machine = self.options.target.cpu.arch.toElfMachine();
495634 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
496635 index += 2;
497636
......@@ -501,7 +640,7 @@ const Update = struct {
501640
502641 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
503642
504 switch (ptr_width) {
643 switch (self.ptr_width) {
505644 .p32 => {
506645 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
507646 index += 4;
......@@ -533,14 +672,14 @@ const Update = struct {
533672 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
534673 index += 4;
535674
536 const e_ehsize: u16 = switch (ptr_width) {
675 const e_ehsize: u16 = switch (self.ptr_width) {
537676 .p32 => @sizeOf(elf.Elf32_Ehdr),
538677 .p64 => @sizeOf(elf.Elf64_Ehdr),
539678 };
540679 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
541680 index += 2;
542681
543 const e_phentsize: u16 = switch (ptr_width) {
682 const e_phentsize: u16 = switch (self.ptr_width) {
544683 .p32 => @sizeOf(elf.Elf32_Phdr),
545684 .p64 => @sizeOf(elf.Elf64_Phdr),
546685 };
......@@ -551,7 +690,7 @@ const Update = struct {
551690 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
552691 index += 2;
553692
554 const e_shentsize: u16 = switch (ptr_width) {
693 const e_shentsize: u16 = switch (self.ptr_width) {
555694 .p32 => @sizeOf(elf.Elf32_Shdr),
556695 .p64 => @sizeOf(elf.Elf64_Shdr),
557696 };
......@@ -567,186 +706,463 @@ const Update = struct {
567706
568707 assert(index == e_ehsize);
569708
570 try self.file.pwriteAll(hdr_buf[0..index], 0);
709 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
571710 }
572711
573 fn writeCodeAndSymbols(self: *Update, phdr_table_dirty: bool, shdr_table_dirty: bool) !void {
574 // index 0 is always a null symbol
575 try self.symbols.resize(1);
576 self.symbols.items[0] = .{
712 const AllocatedBlock = struct {
713 vaddr: u64,
714 file_offset: u64,
715 size_capacity: u64,
716 };
717
718 fn allocateTextBlock(self: *ElfFile, new_block_size: u64, alignment: u64) !AllocatedBlock {
719 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
720 const shdr = &self.sections.items[self.text_section_index.?];
721
722 // TODO Also detect virtual address collisions.
723 const text_capacity = self.allocatedSize(shdr.sh_offset);
724 // TODO instead of looping here, maintain a free list and a pointer to the end.
725 var last_start: u64 = phdr.p_vaddr;
726 var last_size: u64 = 0;
727 for (self.local_symbols.items) |sym| {
728 if (sym.st_value + sym.st_size > last_start + last_size) {
729 last_start = sym.st_value;
730 last_size = sym.st_size;
731 }
732 }
733 const end_vaddr = last_start + (last_size * alloc_num / alloc_den);
734 const aligned_start_vaddr = mem.alignForwardGeneric(u64, end_vaddr, alignment);
735 const needed_size = (aligned_start_vaddr + new_block_size) - phdr.p_vaddr;
736 if (needed_size > text_capacity) {
737 // Must move the entire text section.
738 const new_offset = self.findFreeSpace(needed_size, 0x1000);
739 const text_size = (last_start + last_size) - phdr.p_vaddr;
740 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
741 if (amt != text_size) return error.InputOutput;
742 shdr.sh_offset = new_offset;
743 }
744 // Now that we know the code size, we need to update the program header for executable code
745 shdr.sh_size = needed_size;
746 phdr.p_memsz = needed_size;
747 phdr.p_filesz = needed_size;
748
749 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
750 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
751
752 return AllocatedBlock{
753 .vaddr = aligned_start_vaddr,
754 .file_offset = shdr.sh_offset + (aligned_start_vaddr - phdr.p_vaddr),
755 .size_capacity = text_capacity - needed_size,
756 };
757 }
758
759 fn findAllocatedTextBlock(self: *ElfFile, sym: elf.Elf64_Sym) AllocatedBlock {
760 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
761 const shdr = &self.sections.items[self.text_section_index.?];
762
763 // Find the next sym after this one.
764 // TODO look into using a hash map to speed up perf.
765 const text_capacity = self.allocatedSize(shdr.sh_offset);
766 var next_vaddr_start = phdr.p_vaddr + text_capacity;
767 for (self.local_symbols.items) |elem| {
768 if (elem.st_value < sym.st_value) continue;
769 if (elem.st_value < next_vaddr_start) next_vaddr_start = elem.st_value;
770 }
771 return .{
772 .vaddr = sym.st_value,
773 .file_offset = shdr.sh_offset + (sym.st_value - phdr.p_vaddr),
774 .size_capacity = next_vaddr_start - sym.st_value,
775 };
776 }
777
778 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {
779 if (decl.link.local_sym_index != 0) return;
780
781 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
782 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
783 const local_sym_index = self.local_symbols.items.len;
784 const offset_table_index = self.offset_table.items.len;
785 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
786
787 self.local_symbols.appendAssumeCapacity(.{
577788 .st_name = 0,
578789 .st_info = 0,
579790 .st_other = 0,
580791 .st_shndx = 0,
581 .st_value = 0,
792 .st_value = phdr.p_vaddr,
582793 .st_size = 0,
794 });
795 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);
796 self.offset_table.appendAssumeCapacity(0);
797 errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
798
799 self.offset_table_count_dirty = true;
800
801 decl.link = .{
802 .local_sym_index = @intCast(u32, local_sym_index),
803 .offset_table_index = @intCast(u32, offset_table_index),
583804 };
805 }
584806
585 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
586 var vaddr: u64 = phdr.p_vaddr;
587 var file_off: u64 = phdr.p_offset;
588
589 var code = std.ArrayList(u8).init(self.sections.allocator);
590 defer code.deinit();
591
592 for (self.module.exports) |exp| {
593 code.shrink(0);
594 var symbol = try codegen.generateSymbol(exp.typed_value, self.module.*, &code);
595 defer symbol.deinit(code.allocator);
596 if (symbol.errors.len != 0) {
597 for (symbol.errors) |err| {
598 const msg = try mem.dupe(self.errors.allocator, u8, err.msg);
599 errdefer self.errors.allocator.free(msg);
600 try self.errors.append(.{
601 .byte_offset = err.byte_offset,
602 .msg = msg,
603 });
604 }
605 continue;
606 }
607 try self.file.pwriteAll(code.items, file_off);
807 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {
808 var code_buffer = std.ArrayList(u8).init(self.allocator);
809 defer code_buffer.deinit();
810
811 const typed_value = decl.typed_value.most_recent.typed_value;
812 const code = switch (try codegen.generateSymbol(self, decl.src, typed_value, &code_buffer)) {
813 .externally_managed => |x| x,
814 .appended => code_buffer.items,
815 .fail => |em| {
816 decl.analysis = .codegen_failure;
817 _ = try module.failed_decls.put(decl, em);
818 return;
819 },
820 };
608821
609 if (mem.eql(u8, exp.name, "_start")) {
610 self.entry_addr = vaddr;
611 }
612 (try self.symbols.addOne()).* = .{
613 .st_name = try self.makeString(exp.name),
614 .st_info = (elf.STB_LOCAL << 4) | elf.STT_FUNC,
615 .st_other = 0,
616 .st_shndx = self.text_section_index.?,
617 .st_value = vaddr,
618 .st_size = code.items.len,
822 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
823
824 const file_offset = blk: {
825 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
826 .Fn => elf.STT_FUNC,
827 else => elf.STT_OBJECT,
619828 };
620 vaddr += code.items.len;
621 }
622829
623 {
624 // Now that we know the code size, we need to update the program header for executable code
625 phdr.p_memsz = vaddr - phdr.p_vaddr;
626 phdr.p_filesz = phdr.p_memsz;
830 if (decl.link.local_sym_index != 0) {
831 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
832 const existing_block = self.findAllocatedTextBlock(local_sym.*);
833 const need_realloc = local_sym.st_size == 0 or
834 code.len > existing_block.size_capacity or
835 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
836 // TODO check for collision with another symbol
837 const file_offset = if (need_realloc) fo: {
838 const new_block = try self.allocateTextBlock(code.len, required_alignment);
839 local_sym.st_value = new_block.vaddr;
840 self.offset_table.items[decl.link.offset_table_index] = new_block.vaddr;
841
842 //std.debug.warn("{}: writing got index {}=0x{x}\n", .{
843 // decl.name,
844 // decl.link.offset_table_index,
845 // self.offset_table.items[decl.link.offset_table_index],
846 //});
847 try self.writeOffsetTableEntry(decl.link.offset_table_index);
848
849 break :fo new_block.file_offset;
850 } else existing_block.file_offset;
851 local_sym.st_size = code.len;
852 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
853 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
854 local_sym.st_other = 0;
855 local_sym.st_shndx = self.text_section_index.?;
856 // TODO this write could be avoided if no fields of the symbol were changed.
857 try self.writeSymbol(decl.link.local_sym_index);
858
859 //std.debug.warn("updating {} at vaddr 0x{x}\n", .{ decl.name, local_sym.st_value });
860 break :blk file_offset;
861 } else {
862 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
863 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
864 const decl_name = mem.spanZ(decl.name);
865 const name_str_index = try self.makeString(decl_name);
866 const new_block = try self.allocateTextBlock(code.len, required_alignment);
867 const local_sym_index = self.local_symbols.items.len;
868 const offset_table_index = self.offset_table.items.len;
869
870 //std.debug.warn("add symbol for {} at vaddr 0x{x}, size {}\n", .{ decl.name, new_block.vaddr, code.len });
871 self.local_symbols.appendAssumeCapacity(.{
872 .st_name = name_str_index,
873 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
874 .st_other = 0,
875 .st_shndx = self.text_section_index.?,
876 .st_value = new_block.vaddr,
877 .st_size = code.len,
878 });
879 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);
880 self.offset_table.appendAssumeCapacity(new_block.vaddr);
881 errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
882
883 self.offset_table_count_dirty = true;
884
885 try self.writeSymbol(local_sym_index);
886 try self.writeOffsetTableEntry(offset_table_index);
887
888 decl.link = .{
889 .local_sym_index = @intCast(u32, local_sym_index),
890 .offset_table_index = @intCast(u32, offset_table_index),
891 };
892
893 //std.debug.warn("writing new {} at vaddr 0x{x}\n", .{ decl.name, new_block.vaddr });
894 break :blk new_block.file_offset;
895 }
896 };
897
898 try self.file.?.pwriteAll(code, file_offset);
627899
628 const shdr = &self.sections.items[self.text_section_index.?];
629 shdr.sh_size = phdr.p_filesz;
900 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
901 const decl_exports = module.decl_exports.getValue(decl) orelse &[0]*Module.Export{};
902 return self.updateDeclExports(module, decl, decl_exports);
903 }
630904
631 if (!phdr_table_dirty) {
632 // Then it won't get written with the others and we need to do it.
633 try self.writeProgHeader(self.phdr_load_re_index.?);
905 /// Must be called only after a successful call to `updateDecl`.
906 pub fn updateDeclExports(
907 self: *ElfFile,
908 module: *Module,
909 decl: *const Module.Decl,
910 exports: []const *Module.Export,
911 ) !void {
912 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
913 const typed_value = decl.typed_value.most_recent.typed_value;
914 if (decl.link.local_sym_index == 0) return;
915 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
916
917 for (exports) |exp| {
918 if (exp.options.section) |section_name| {
919 if (!mem.eql(u8, section_name, ".text")) {
920 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);
921 module.failed_exports.putAssumeCapacityNoClobber(
922 exp,
923 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
924 );
925 continue;
926 }
634927 }
635 if (!shdr_table_dirty) {
636 // Then it won't get written with the others and we need to do it.
637 try self.writeSectHeader(self.text_section_index.?);
928 const stb_bits: u8 = switch (exp.options.linkage) {
929 .Internal => elf.STB_LOCAL,
930 .Strong => blk: {
931 if (mem.eql(u8, exp.options.name, "_start")) {
932 self.entry_addr = decl_sym.st_value;
933 }
934 break :blk elf.STB_GLOBAL;
935 },
936 .Weak => elf.STB_WEAK,
937 .LinkOnce => {
938 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);
939 module.failed_exports.putAssumeCapacityNoClobber(
940 exp,
941 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
942 );
943 continue;
944 },
945 };
946 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
947 if (exp.link.sym_index) |i| {
948 const sym = &self.global_symbols.items[i];
949 sym.* = .{
950 .st_name = try self.updateString(sym.st_name, exp.options.name),
951 .st_info = (stb_bits << 4) | stt_bits,
952 .st_other = 0,
953 .st_shndx = self.text_section_index.?,
954 .st_value = decl_sym.st_value,
955 .st_size = decl_sym.st_size,
956 };
957 } else {
958 const name = try self.makeString(exp.options.name);
959 const i = self.global_symbols.items.len;
960 self.global_symbols.appendAssumeCapacity(.{
961 .st_name = name,
962 .st_info = (stb_bits << 4) | stt_bits,
963 .st_other = 0,
964 .st_shndx = self.text_section_index.?,
965 .st_value = decl_sym.st_value,
966 .st_size = decl_sym.st_size,
967 });
968 errdefer self.global_symbols.shrink(self.allocator, self.global_symbols.items.len - 1);
969
970 exp.link.sym_index = @intCast(u32, i);
638971 }
639972 }
640
641 return self.writeSymbols();
642973 }
643974
644 fn writeProgHeader(self: *Update, index: usize) !void {
645 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
975 fn writeProgHeader(self: *ElfFile, index: usize) !void {
976 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
646977 const offset = self.program_headers.items[index].p_offset;
647 switch (self.module.target.cpu.arch.ptrBitWidth()) {
978 switch (self.options.target.cpu.arch.ptrBitWidth()) {
648979 32 => {
649980 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
650981 if (foreign_endian) {
651982 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
652983 }
653 return self.file.pwriteAll(mem.sliceAsBytes(&phdr), offset);
984 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
654985 },
655986 64 => {
656987 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
657988 if (foreign_endian) {
658989 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
659990 }
660 return self.file.pwriteAll(mem.sliceAsBytes(&phdr), offset);
991 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
661992 },
662993 else => return error.UnsupportedArchitecture,
663994 }
664995 }
665996
666 fn writeSectHeader(self: *Update, index: usize) !void {
667 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
997 fn writeSectHeader(self: *ElfFile, index: usize) !void {
998 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
668999 const offset = self.sections.items[index].sh_offset;
669 switch (self.module.target.cpu.arch.ptrBitWidth()) {
1000 switch (self.options.target.cpu.arch.ptrBitWidth()) {
6701001 32 => {
6711002 var shdr: [1]elf.Elf32_Shdr = undefined;
6721003 shdr[0] = sectHeaderTo32(self.sections.items[index]);
6731004 if (foreign_endian) {
6741005 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
6751006 }
676 return self.file.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1007 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
6771008 },
6781009 64 => {
6791010 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
6801011 if (foreign_endian) {
6811012 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
6821013 }
683 return self.file.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1014 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
6841015 },
6851016 else => return error.UnsupportedArchitecture,
6861017 }
6871018 }
6881019
689 fn writeSymbols(self: *Update) !void {
690 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
691 32 => .p32,
692 64 => .p64,
693 else => return error.UnsupportedArchitecture,
1020 fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {
1021 const shdr = &self.sections.items[self.got_section_index.?];
1022 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1023 const entry_size: u16 = switch (self.ptr_width) {
1024 .p32 => 4,
1025 .p64 => 8,
6941026 };
695 const small_ptr = ptr_width == .p32;
696 const syms_sect = &self.sections.items[self.symtab_section_index.?];
697 const sym_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
698 const sym_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
1027 if (self.offset_table_count_dirty) {
1028 // TODO Also detect virtual address collisions.
1029 const allocated_size = self.allocatedSize(shdr.sh_offset);
1030 const needed_size = self.local_symbols.items.len * entry_size;
1031 if (needed_size > allocated_size) {
1032 // Must move the entire got section.
1033 const new_offset = self.findFreeSpace(needed_size, entry_size);
1034 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1035 if (amt != shdr.sh_size) return error.InputOutput;
1036 shdr.sh_offset = new_offset;
1037 }
1038 shdr.sh_size = needed_size;
1039 phdr.p_memsz = needed_size;
1040 phdr.p_filesz = needed_size;
1041
1042 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1043 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1044
1045 self.offset_table_count_dirty = false;
1046 }
1047 const endian = self.options.target.cpu.arch.endian();
1048 const off = shdr.sh_offset + @as(u64, entry_size) * index;
1049 switch (self.ptr_width) {
1050 .p32 => {
1051 var buf: [4]u8 = undefined;
1052 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
1053 try self.file.?.pwriteAll(&buf, off);
1054 },
1055 .p64 => {
1056 var buf: [8]u8 = undefined;
1057 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
1058 try self.file.?.pwriteAll(&buf, off);
1059 },
1060 }
1061 }
6991062
700 const allocated_size = self.allocatedSize(syms_sect.sh_offset);
701 const needed_size = self.symbols.items.len * sym_size;
702 if (needed_size > allocated_size) {
703 syms_sect.sh_size = 0; // free the space
704 syms_sect.sh_offset = self.findFreeSpace(needed_size, sym_align);
705 //std.debug.warn("moved symtab to 0x{x} to 0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });
1063 fn writeSymbol(self: *ElfFile, index: usize) !void {
1064 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1065 // Make sure we are not pointlessly writing symbol data that will have to get relocated
1066 // due to running out of space.
1067 if (self.local_symbols.items.len != syms_sect.sh_info) {
1068 const sym_size: u64 = switch (self.ptr_width) {
1069 .p32 => @sizeOf(elf.Elf32_Sym),
1070 .p64 => @sizeOf(elf.Elf64_Sym),
1071 };
1072 const sym_align: u16 = switch (self.ptr_width) {
1073 .p32 => @alignOf(elf.Elf32_Sym),
1074 .p64 => @alignOf(elf.Elf64_Sym),
1075 };
1076 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
1077 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
1078 // Move all the symbols to a new file location.
1079 const new_offset = self.findFreeSpace(needed_size, sym_align);
1080 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
1081 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);
1082 if (amt != existing_size) return error.InputOutput;
1083 syms_sect.sh_offset = new_offset;
1084 }
1085 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1086 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1087 self.shdr_table_dirty = true; // TODO look into only writing one section
7061088 }
1089 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1090 switch (self.ptr_width) {
1091 .p32 => {
1092 var sym = [1]elf.Elf32_Sym{
1093 .{
1094 .st_name = self.local_symbols.items[index].st_name,
1095 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
1096 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
1097 .st_info = self.local_symbols.items[index].st_info,
1098 .st_other = self.local_symbols.items[index].st_other,
1099 .st_shndx = self.local_symbols.items[index].st_shndx,
1100 },
1101 };
1102 if (foreign_endian) {
1103 bswapAllFields(elf.Elf32_Sym, &sym[0]);
1104 }
1105 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
1106 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1107 },
1108 .p64 => {
1109 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
1110 if (foreign_endian) {
1111 bswapAllFields(elf.Elf64_Sym, &sym[0]);
1112 }
1113 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
1114 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1115 },
1116 }
1117 }
1118
1119 fn writeAllGlobalSymbols(self: *ElfFile) !void {
1120 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1121 const sym_size: u64 = switch (self.ptr_width) {
1122 .p32 => @sizeOf(elf.Elf32_Sym),
1123 .p64 => @sizeOf(elf.Elf64_Sym),
1124 };
7071125 //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });
708 syms_sect.sh_size = needed_size;
709 syms_sect.sh_info = @intCast(u32, self.symbols.items.len);
710 const allocator = self.symbols.allocator;
711 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
712 switch (ptr_width) {
1126 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1127 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
1128 switch (self.ptr_width) {
7131129 .p32 => {
714 const buf = try allocator.alloc(elf.Elf32_Sym, self.symbols.items.len);
715 defer allocator.free(buf);
1130 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
1131 defer self.allocator.free(buf);
7161132
7171133 for (buf) |*sym, i| {
7181134 sym.* = .{
719 .st_name = self.symbols.items[i].st_name,
720 .st_value = @intCast(u32, self.symbols.items[i].st_value),
721 .st_size = @intCast(u32, self.symbols.items[i].st_size),
722 .st_info = self.symbols.items[i].st_info,
723 .st_other = self.symbols.items[i].st_other,
724 .st_shndx = self.symbols.items[i].st_shndx,
1135 .st_name = self.global_symbols.items[i].st_name,
1136 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
1137 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
1138 .st_info = self.global_symbols.items[i].st_info,
1139 .st_other = self.global_symbols.items[i].st_other,
1140 .st_shndx = self.global_symbols.items[i].st_shndx,
7251141 };
7261142 if (foreign_endian) {
7271143 bswapAllFields(elf.Elf32_Sym, sym);
7281144 }
7291145 }
730 try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset);
1146 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
7311147 },
7321148 .p64 => {
733 const buf = try allocator.alloc(elf.Elf64_Sym, self.symbols.items.len);
734 defer allocator.free(buf);
1149 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
1150 defer self.allocator.free(buf);
7351151
7361152 for (buf) |*sym, i| {
7371153 sym.* = .{
738 .st_name = self.symbols.items[i].st_name,
739 .st_value = self.symbols.items[i].st_value,
740 .st_size = self.symbols.items[i].st_size,
741 .st_info = self.symbols.items[i].st_info,
742 .st_other = self.symbols.items[i].st_other,
743 .st_shndx = self.symbols.items[i].st_shndx,
1154 .st_name = self.global_symbols.items[i].st_name,
1155 .st_value = self.global_symbols.items[i].st_value,
1156 .st_size = self.global_symbols.items[i].st_size,
1157 .st_info = self.global_symbols.items[i].st_info,
1158 .st_other = self.global_symbols.items[i].st_other,
1159 .st_shndx = self.global_symbols.items[i].st_shndx,
7441160 };
7451161 if (foreign_endian) {
7461162 bswapAllFields(elf.Elf64_Sym, sym);
7471163 }
7481164 }
749 try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset);
1165 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
7501166 },
7511167 }
7521168 }
......@@ -754,13 +1170,13 @@ const Update = struct {
7541170
7551171/// Truncates the existing file contents and overwrites the contents.
7561172/// Returns an error if `file` is not already open with +read +write +seek abilities.
757pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
758 switch (module.output_mode) {
1173pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
1174 switch (options.output_mode) {
7591175 .Exe => {},
7601176 .Obj => {},
7611177 .Lib => return error.TODOImplementWritingLibFiles,
7621178 }
763 switch (module.object_format) {
1179 switch (options.object_format) {
7641180 .unknown => unreachable, // TODO remove this tag from the enum
7651181 .coff => return error.TODOImplementWritingCOFF,
7661182 .elf => {},
......@@ -768,38 +1184,80 @@ pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Resul
7681184 .wasm => return error.TODOImplementWritingWasmObjects,
7691185 }
7701186
771 var update = Update{
1187 var self: ElfFile = .{
1188 .allocator = allocator,
7721189 .file = file,
773 .module = &module,
774 .sections = std.ArrayList(elf.Elf64_Shdr).init(allocator),
775 .shdr_table_offset = null,
776 .program_headers = std.ArrayList(elf.Elf64_Phdr).init(allocator),
777 .phdr_table_offset = null,
778 .phdr_load_re_index = null,
779 .entry_addr = null,
780 .shstrtab = std.ArrayList(u8).init(allocator),
781 .shstrtab_index = null,
782 .text_section_index = null,
783 .symtab_section_index = null,
784
785 .symbols = std.ArrayList(elf.Elf64_Sym).init(allocator),
786
787 .errors = std.ArrayList(ErrorMsg).init(allocator),
788 };
789 defer update.deinit();
790
791 try update.perform();
792 return Result{
793 .errors = update.errors.toOwnedSlice(),
1190 .options = options,
1191 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
1192 32 => .p32,
1193 64 => .p64,
1194 else => return error.UnsupportedELFArchitecture,
1195 },
1196 .shdr_table_dirty = true,
1197 .owns_file_handle = false,
7941198 };
1199 errdefer self.deinit();
1200
1201 // Index 0 is always a null symbol.
1202 try self.local_symbols.append(allocator, .{
1203 .st_name = 0,
1204 .st_info = 0,
1205 .st_other = 0,
1206 .st_shndx = 0,
1207 .st_value = 0,
1208 .st_size = 0,
1209 });
1210
1211 // There must always be a null section in index 0
1212 try self.sections.append(allocator, .{
1213 .sh_name = 0,
1214 .sh_type = elf.SHT_NULL,
1215 .sh_flags = 0,
1216 .sh_addr = 0,
1217 .sh_offset = 0,
1218 .sh_size = 0,
1219 .sh_link = 0,
1220 .sh_info = 0,
1221 .sh_addralign = 0,
1222 .sh_entsize = 0,
1223 });
1224
1225 try self.populateMissingMetadata();
1226
1227 return self;
7951228}
7961229
7971230/// Returns error.IncrFailed if incremental update could not be performed.
798fn updateFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
799 //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
1231fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
1232 switch (options.output_mode) {
1233 .Exe => {},
1234 .Obj => {},
1235 .Lib => return error.IncrFailed,
1236 }
1237 switch (options.object_format) {
1238 .unknown => unreachable, // TODO remove this tag from the enum
1239 .coff => return error.IncrFailed,
1240 .elf => {},
1241 .macho => return error.IncrFailed,
1242 .wasm => return error.IncrFailed,
1243 }
1244 var self: ElfFile = .{
1245 .allocator = allocator,
1246 .file = file,
1247 .owns_file_handle = false,
1248 .options = options,
1249 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
1250 32 => .p32,
1251 64 => .p64,
1252 else => return error.UnsupportedELFArchitecture,
1253 },
1254 };
1255 errdefer self.deinit();
8001256
801 // TODO implement incremental linking
1257 // TODO implement reading the elf file
8021258 return error.IncrFailed;
1259 //try self.populateMissingMetadata();
1260 //return self;
8031261}
8041262
8051263/// Saturating multiplication
......@@ -840,14 +1298,14 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
8401298 };
8411299}
8421300
843fn determineMode(module: ir.Module) fs.File.Mode {
1301fn determineMode(options: Options) fs.File.Mode {
8441302 // On common systems with a 0o022 umask, 0o777 will still result in a file created
8451303 // with 0o755 permissions, but it works appropriately if the system is configured
8461304 // more leniently. As another data point, C's fopen seems to open files with the
8471305 // 666 mode.
8481306 const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777;
849 switch (module.output_mode) {
850 .Lib => return switch (module.link_mode) {
1307 switch (options.output_mode) {
1308 .Lib => return switch (options.link_mode) {
8511309 .Dynamic => executable_mode,
8521310 .Static => fs.File.default_mode,
8531311 },
src-self-hosted/main.zig+413-525
......@@ -1,29 +1,29 @@
11const std = @import("std");
2const builtin = @import("builtin");
3
4const event = std.event;
5const os = std.os;
62const io = std.io;
73const fs = std.fs;
84const mem = std.mem;
95const process = std.process;
106const Allocator = mem.Allocator;
117const ArrayList = std.ArrayList;
8const ast = std.zig.ast;
9const Module = @import("Module.zig");
10const link = @import("link.zig");
11const Package = @import("Package.zig");
12const zir = @import("zir.zig");
1213
13const c = @import("c.zig");
14const introspect = @import("introspect.zig");
15const ZigCompiler = @import("compilation.zig").ZigCompiler;
16const Compilation = @import("compilation.zig").Compilation;
17const Target = std.Target;
18const errmsg = @import("errmsg.zig");
19const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
20
21pub const io_mode = .evented;
14// TODO Improve async I/O enough that we feel comfortable doing this.
15//pub const io_mode = .evented;
2216
2317pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
2418
19pub const Color = enum {
20 Auto,
21 Off,
22 On,
23};
24
2525const usage =
26 \\usage: zig [command] [options]
26 \\Usage: zig [command] [options]
2727 \\
2828 \\Commands:
2929 \\
......@@ -31,7 +31,6 @@ const usage =
3131 \\ build-lib [source] Create library from source or object files
3232 \\ build-obj [source] Create object from source or assembly
3333 \\ fmt [source] Parse file and render in canonical zig format
34 \\ libc [paths_file] Display native libc paths file or validate one
3534 \\ targets List available compilation targets
3635 \\ version Print version number and exit
3736 \\ zen Print zen of zig and exit
......@@ -39,175 +38,152 @@ const usage =
3938 \\
4039;
4140
42const Command = struct {
43 name: []const u8,
44 exec: fn (*Allocator, []const []const u8) callconv(.Async) anyerror!void,
45};
46
4741pub fn main() !void {
48 const allocator = std.heap.c_allocator;
49
50 const stderr = io.getStdErr().outStream();
42 // TODO general purpose allocator in the zig std lib
43 const gpa = if (std.builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator;
44 var arena_instance = std.heap.ArenaAllocator.init(gpa);
45 defer arena_instance.deinit();
46 const arena = &arena_instance.allocator;
5147
52 const args = try process.argsAlloc(allocator);
53 defer process.argsFree(allocator, args);
48 const args = try process.argsAlloc(arena);
5449
5550 if (args.len <= 1) {
56 try stderr.writeAll("expected command argument\n\n");
57 try stderr.writeAll(usage);
51 std.debug.warn("expected command argument\n\n{}", .{usage});
5852 process.exit(1);
5953 }
6054
6155 const cmd = args[1];
6256 const cmd_args = args[2..];
6357 if (mem.eql(u8, cmd, "build-exe")) {
64 return buildOutputType(allocator, cmd_args, .Exe);
58 return buildOutputType(gpa, arena, cmd_args, .Exe);
6559 } else if (mem.eql(u8, cmd, "build-lib")) {
66 return buildOutputType(allocator, cmd_args, .Lib);
60 return buildOutputType(gpa, arena, cmd_args, .Lib);
6761 } else if (mem.eql(u8, cmd, "build-obj")) {
68 return buildOutputType(allocator, cmd_args, .Obj);
62 return buildOutputType(gpa, arena, cmd_args, .Obj);
6963 } else if (mem.eql(u8, cmd, "fmt")) {
70 return cmdFmt(allocator, cmd_args);
71 } else if (mem.eql(u8, cmd, "libc")) {
72 return cmdLibC(allocator, cmd_args);
64 return cmdFmt(gpa, cmd_args);
7365 } else if (mem.eql(u8, cmd, "targets")) {
74 const info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
66 const info = try std.zig.system.NativeTargetInfo.detect(arena, .{});
7567 const stdout = io.getStdOut().outStream();
76 return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, info.target);
68 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
7769 } else if (mem.eql(u8, cmd, "version")) {
78 return cmdVersion(allocator, cmd_args);
70 // Need to set up the build script to give the version as a comptime value.
71 std.debug.warn("TODO version command not implemented yet\n", .{});
72 return error.Unimplemented;
7973 } else if (mem.eql(u8, cmd, "zen")) {
80 return cmdZen(allocator, cmd_args);
74 try io.getStdOut().writeAll(info_zen);
8175 } else if (mem.eql(u8, cmd, "help")) {
82 return cmdHelp(allocator, cmd_args);
83 } else if (mem.eql(u8, cmd, "internal")) {
84 return cmdInternal(allocator, cmd_args);
76 try io.getStdOut().writeAll(usage);
8577 } else {
86 try stderr.print("unknown command: {}\n\n", .{args[1]});
87 try stderr.writeAll(usage);
78 std.debug.warn("unknown command: {}\n\n{}", .{ args[1], usage });
8879 process.exit(1);
8980 }
9081}
9182
9283const usage_build_generic =
93 \\usage: zig build-exe <options> [file]
94 \\ zig build-lib <options> [file]
95 \\ zig build-obj <options> [file]
84 \\Usage: zig build-exe <options> [files]
85 \\ zig build-lib <options> [files]
86 \\ zig build-obj <options> [files]
87 \\
88 \\Supported file types:
89 \\ (planned) .zig Zig source code
90 \\ .zir Zig Intermediate Representation code
91 \\ (planned) .o ELF object file
92 \\ (planned) .o MACH-O (macOS) object file
93 \\ (planned) .obj COFF (Windows) object file
94 \\ (planned) .lib COFF (Windows) static library
95 \\ (planned) .a ELF static library
96 \\ (planned) .so ELF shared object (dynamic link)
97 \\ (planned) .dll Windows Dynamic Link Library
98 \\ (planned) .dylib MACH-O (macOS) dynamic library
99 \\ (planned) .s Target-specific assembly source code
100 \\ (planned) .S Assembly with C preprocessor (requires LLVM extensions)
101 \\ (planned) .c C source code (requires LLVM extensions)
102 \\ (planned) .cpp C++ source code (requires LLVM extensions)
103 \\ Other C++ extensions: .C .cc .cxx
96104 \\
97105 \\General Options:
98 \\ --help Print this help and exit
99 \\ --color [auto|off|on] Enable or disable colored error messages
106 \\ -h, --help Print this help and exit
107 \\ --watch Enable compiler REPL
108 \\ --color [auto|off|on] Enable or disable colored error messages
109 \\ -femit-bin[=path] (default) output machine code
110 \\ -fno-emit-bin Do not output machine code
100111 \\
101112 \\Compile Options:
102 \\ --libc [file] Provide a file which specifies libc paths
103 \\ --assembly [source] Add assembly file to build
104 \\ --emit [filetype] Emit a specific file format as compilation output
105 \\ --enable-timing-info Print timing diagnostics
106 \\ --name [name] Override output name
107 \\ --output [file] Override destination path
108 \\ --output-h [file] Override generated header file path
109 \\ --pkg-begin [name] [path] Make package available to import and push current pkg
110 \\ --pkg-end Pop current pkg
111 \\ --mode [mode] Set the build mode
112 \\ debug (default) optimizations off, safety on
113 \\ release-fast optimizations on, safety off
114 \\ release-safe optimizations on, safety on
115 \\ release-small optimize for small binary, safety off
116 \\ --static Output will be statically linked
117 \\ --strip Exclude debug symbols
118 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command
119 \\ --eh-frame-hdr enable C++ exception handling by passing --eh-frame-hdr to linker
120 \\ --verbose-tokenize Turn on compiler debug output for tokenization
121 \\ --verbose-ast-tree Turn on compiler debug output for parsing into an AST (tree view)
122 \\ --verbose-ast-fmt Turn on compiler debug output for parsing into an AST (render source)
123 \\ --verbose-link Turn on compiler debug output for linking
124 \\ --verbose-ir Turn on compiler debug output for Zig IR
125 \\ --verbose-llvm-ir Turn on compiler debug output for LLVM IR
126 \\ --verbose-cimport Turn on compiler debug output for C imports
127 \\ -dirafter [dir] Same as -isystem but do it last
128 \\ -isystem [dir] Add additional search path for other .h files
129 \\ -mllvm [arg] Additional arguments to forward to LLVM's option processing
113 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command
114 \\ -mcpu [cpu] Specify target CPU and feature set
115 \\ --name [name] Override output name
116 \\ --mode [mode] Set the build mode
117 \\ Debug (default) optimizations off, safety on
118 \\ ReleaseFast optimizations on, safety off
119 \\ ReleaseSafe optimizations on, safety on
120 \\ ReleaseSmall optimize for small binary, safety off
121 \\ --dynamic Force output to be dynamically linked
122 \\ --strip Exclude debug symbols
130123 \\
131124 \\Link Options:
132 \\ --ar-path [path] Set the path to ar
133 \\ --each-lib-rpath Add rpath for each used dynamic library
134 \\ --library [lib] Link against lib
135 \\ --forbid-library [lib] Make it an error to link against lib
136 \\ --library-path [dir] Add a directory to the library search path
137 \\ --linker-script [path] Use a custom linker script
138 \\ --object [obj] Add object file to build
139 \\ -rdynamic Add all symbols to the dynamic symbol table
140 \\ -rpath [path] Add directory to the runtime library search path
141 \\ -framework [name] (darwin) link against framework
142 \\ -mios-version-min [ver] (darwin) set iOS deployment target
143 \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
144 \\ --ver-major [ver] Dynamic library semver major version
145 \\ --ver-minor [ver] Dynamic library semver minor version
146 \\ --ver-patch [ver] Dynamic library semver patch version
125 \\ -l[lib], --library [lib] Link against system library
126 \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
127 \\ --version [ver] Dynamic library semver
147128 \\
129 \\Debug Options (Zig Compiler Development):
130 \\ -ftime-report Print timing diagnostics
131 \\ --debug-tokenize verbose tokenization
132 \\ --debug-ast-tree verbose parsing into an AST (tree view)
133 \\ --debug-ast-fmt verbose parsing into an AST (render source)
134 \\ --debug-ir verbose Zig IR
135 \\ --debug-link verbose linking
136 \\ --debug-codegen verbose machine code generation
148137 \\
149138;
150139
151fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Compilation.Kind) !void {
152 const stderr = io.getStdErr().outStream();
140const Emit = union(enum) {
141 no,
142 yes_default_path,
143 yes: []const u8,
144};
153145
154 var color: errmsg.Color = .Auto;
146fn buildOutputType(
147 gpa: *Allocator,
148 arena: *Allocator,
149 args: []const []const u8,
150 output_mode: std.builtin.OutputMode,
151) !void {
152 var color: Color = .Auto;
155153 var build_mode: std.builtin.Mode = .Debug;
156 var emit_bin = true;
157 var emit_asm = false;
158 var emit_llvm_ir = false;
159 var emit_h = false;
160154 var provided_name: ?[]const u8 = null;
161 var is_dynamic = false;
155 var link_mode: ?std.builtin.LinkMode = null;
162156 var root_src_file: ?[]const u8 = null;
163 var libc_arg: ?[]const u8 = null;
164157 var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
165 var linker_script: ?[]const u8 = null;
166158 var strip = false;
167 var verbose_tokenize = false;
168 var verbose_ast_tree = false;
169 var verbose_ast_fmt = false;
170 var verbose_link = false;
171 var verbose_ir = false;
172 var verbose_llvm_ir = false;
173 var verbose_cimport = false;
174 var linker_rdynamic = false;
175 var link_eh_frame_hdr = false;
176 var macosx_version_min: ?[]const u8 = null;
177 var ios_version_min: ?[]const u8 = null;
178
179 var assembly_files = ArrayList([]const u8).init(allocator);
180 defer assembly_files.deinit();
181
182 var link_objects = ArrayList([]const u8).init(allocator);
183 defer link_objects.deinit();
184
185 var clang_argv_buf = ArrayList([]const u8).init(allocator);
186 defer clang_argv_buf.deinit();
187
188 var mllvm_flags = ArrayList([]const u8).init(allocator);
189 defer mllvm_flags.deinit();
190
191 var cur_pkg = try CliPkg.init(allocator, "", "", null);
192 defer cur_pkg.deinit();
193
194 var system_libs = ArrayList([]const u8).init(allocator);
159 var watch = false;
160 var debug_tokenize = false;
161 var debug_ast_tree = false;
162 var debug_ast_fmt = false;
163 var debug_link = false;
164 var debug_ir = false;
165 var debug_codegen = false;
166 var time_report = false;
167 var emit_bin: Emit = .yes_default_path;
168 var emit_zir: Emit = .no;
169 var target_arch_os_abi: []const u8 = "native";
170 var target_mcpu: ?[]const u8 = null;
171 var target_dynamic_linker: ?[]const u8 = null;
172
173 var system_libs = std.ArrayList([]const u8).init(gpa);
195174 defer system_libs.deinit();
196175
197 var c_src_files = ArrayList([]const u8).init(allocator);
198 defer c_src_files.deinit();
199
200176 {
201177 var i: usize = 0;
202178 while (i < args.len) : (i += 1) {
203179 const arg = args[i];
204180 if (mem.startsWith(u8, arg, "-")) {
205 if (mem.eql(u8, arg, "--help")) {
181 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
206182 try io.getStdOut().writeAll(usage_build_generic);
207183 process.exit(0);
208184 } else if (mem.eql(u8, arg, "--color")) {
209185 if (i + 1 >= args.len) {
210 try stderr.writeAll("expected [auto|on|off] after --color\n");
186 std.debug.warn("expected [auto|on|off] after --color\n", .{});
211187 process.exit(1);
212188 }
213189 i += 1;
......@@ -219,12 +195,12 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
219195 } else if (mem.eql(u8, next_arg, "off")) {
220196 color = .Off;
221197 } else {
222 try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
198 std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
223199 process.exit(1);
224200 }
225201 } else if (mem.eql(u8, arg, "--mode")) {
226202 if (i + 1 >= args.len) {
227 try stderr.writeAll("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n");
203 std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
228204 process.exit(1);
229205 }
230206 i += 1;
......@@ -238,289 +214,302 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
238214 } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
239215 build_mode = .ReleaseSmall;
240216 } else {
241 try stderr.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
217 std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
242218 process.exit(1);
243219 }
244220 } else if (mem.eql(u8, arg, "--name")) {
245221 if (i + 1 >= args.len) {
246 try stderr.writeAll("expected parameter after --name\n");
222 std.debug.warn("expected parameter after --name\n", .{});
247223 process.exit(1);
248224 }
249225 i += 1;
250226 provided_name = args[i];
251 } else if (mem.eql(u8, arg, "--ver-major")) {
252 if (i + 1 >= args.len) {
253 try stderr.writeAll("expected parameter after --ver-major\n");
254 process.exit(1);
255 }
256 i += 1;
257 version.major = try std.fmt.parseInt(u32, args[i], 10);
258 } else if (mem.eql(u8, arg, "--ver-minor")) {
259 if (i + 1 >= args.len) {
260 try stderr.writeAll("expected parameter after --ver-minor\n");
261 process.exit(1);
262 }
263 i += 1;
264 version.minor = try std.fmt.parseInt(u32, args[i], 10);
265 } else if (mem.eql(u8, arg, "--ver-patch")) {
227 } else if (mem.eql(u8, arg, "--library")) {
266228 if (i + 1 >= args.len) {
267 try stderr.writeAll("expected parameter after --ver-patch\n");
229 std.debug.warn("expected parameter after --library\n", .{});
268230 process.exit(1);
269231 }
270232 i += 1;
271 version.patch = try std.fmt.parseInt(u32, args[i], 10);
272 } else if (mem.eql(u8, arg, "--linker-script")) {
233 try system_libs.append(args[i]);
234 } else if (mem.eql(u8, arg, "--version")) {
273235 if (i + 1 >= args.len) {
274 try stderr.writeAll("expected parameter after --linker-script\n");
236 std.debug.warn("expected parameter after --version\n", .{});
275237 process.exit(1);
276238 }
277239 i += 1;
278 linker_script = args[i];
279 } else if (mem.eql(u8, arg, "--libc")) {
280 if (i + 1 >= args.len) {
281 try stderr.writeAll("expected parameter after --libc\n");
240 version = std.builtin.Version.parse(args[i]) catch |err| {
241 std.debug.warn("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
282242 process.exit(1);
283 }
284 i += 1;
285 libc_arg = args[i];
286 } else if (mem.eql(u8, arg, "-mllvm")) {
243 };
244 } else if (mem.eql(u8, arg, "-target")) {
287245 if (i + 1 >= args.len) {
288 try stderr.writeAll("expected parameter after -mllvm\n");
246 std.debug.warn("expected parameter after -target\n", .{});
289247 process.exit(1);
290248 }
291249 i += 1;
292 try clang_argv_buf.append("-mllvm");
293 try clang_argv_buf.append(args[i]);
294
295 try mllvm_flags.append(args[i]);
296 } else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
250 target_arch_os_abi = args[i];
251 } else if (mem.eql(u8, arg, "-mcpu")) {
297252 if (i + 1 >= args.len) {
298 try stderr.writeAll("expected parameter after -mmacosx-version-min\n");
253 std.debug.warn("expected parameter after -mcpu\n", .{});
299254 process.exit(1);
300255 }
301256 i += 1;
302 macosx_version_min = args[i];
303 } else if (mem.eql(u8, arg, "-mios-version-min")) {
257 target_mcpu = args[i];
258 } else if (mem.startsWith(u8, arg, "-mcpu=")) {
259 target_mcpu = arg["-mcpu=".len..];
260 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
304261 if (i + 1 >= args.len) {
305 try stderr.writeAll("expected parameter after -mios-version-min\n");
262 std.debug.warn("expected parameter after --dynamic-linker\n", .{});
306263 process.exit(1);
307264 }
308265 i += 1;
309 ios_version_min = args[i];
266 target_dynamic_linker = args[i];
267 } else if (mem.eql(u8, arg, "--watch")) {
268 watch = true;
269 } else if (mem.eql(u8, arg, "-ftime-report")) {
270 time_report = true;
310271 } else if (mem.eql(u8, arg, "-femit-bin")) {
311 emit_bin = true;
272 emit_bin = .yes_default_path;
273 } else if (mem.startsWith(u8, arg, "-femit-bin=")) {
274 emit_bin = .{ .yes = arg["-femit-bin=".len..] };
312275 } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
313 emit_bin = false;
314 } else if (mem.eql(u8, arg, "-femit-asm")) {
315 emit_asm = true;
316 } else if (mem.eql(u8, arg, "-fno-emit-asm")) {
317 emit_asm = false;
318 } else if (mem.eql(u8, arg, "-femit-llvm-ir")) {
319 emit_llvm_ir = true;
320 } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
321 emit_llvm_ir = false;
276 emit_bin = .no;
277 } else if (mem.eql(u8, arg, "-femit-zir")) {
278 emit_zir = .yes_default_path;
279 } else if (mem.startsWith(u8, arg, "-femit-zir=")) {
280 emit_zir = .{ .yes = arg["-femit-zir=".len..] };
281 } else if (mem.eql(u8, arg, "-fno-emit-zir")) {
282 emit_zir = .no;
322283 } else if (mem.eql(u8, arg, "-dynamic")) {
323 is_dynamic = true;
284 link_mode = .Dynamic;
285 } else if (mem.eql(u8, arg, "-static")) {
286 link_mode = .Static;
324287 } else if (mem.eql(u8, arg, "--strip")) {
325288 strip = true;
326 } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
327 verbose_tokenize = true;
328 } else if (mem.eql(u8, arg, "--verbose-ast-tree")) {
329 verbose_ast_tree = true;
330 } else if (mem.eql(u8, arg, "--verbose-ast-fmt")) {
331 verbose_ast_fmt = true;
332 } else if (mem.eql(u8, arg, "--verbose-link")) {
333 verbose_link = true;
334 } else if (mem.eql(u8, arg, "--verbose-ir")) {
335 verbose_ir = true;
336 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
337 verbose_llvm_ir = true;
338 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
339 link_eh_frame_hdr = true;
340 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
341 verbose_cimport = true;
342 } else if (mem.eql(u8, arg, "-rdynamic")) {
343 linker_rdynamic = true;
344 } else if (mem.eql(u8, arg, "--pkg-begin")) {
345 if (i + 2 >= args.len) {
346 try stderr.writeAll("expected [name] [path] after --pkg-begin\n");
347 process.exit(1);
348 }
349 i += 1;
350 const new_pkg_name = args[i];
351 i += 1;
352 const new_pkg_path = args[i];
353
354 var new_cur_pkg = try CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg);
355 try cur_pkg.children.append(new_cur_pkg);
356 cur_pkg = new_cur_pkg;
357 } else if (mem.eql(u8, arg, "--pkg-end")) {
358 if (cur_pkg.parent) |parent| {
359 cur_pkg = parent;
360 } else {
361 try stderr.writeAll("encountered --pkg-end with no matching --pkg-begin\n");
362 process.exit(1);
363 }
289 } else if (mem.eql(u8, arg, "--debug-tokenize")) {
290 debug_tokenize = true;
291 } else if (mem.eql(u8, arg, "--debug-ast-tree")) {
292 debug_ast_tree = true;
293 } else if (mem.eql(u8, arg, "--debug-ast-fmt")) {
294 debug_ast_fmt = true;
295 } else if (mem.eql(u8, arg, "--debug-link")) {
296 debug_link = true;
297 } else if (mem.eql(u8, arg, "--debug-ir")) {
298 debug_ir = true;
299 } else if (mem.eql(u8, arg, "--debug-codegen")) {
300 debug_codegen = true;
364301 } else if (mem.startsWith(u8, arg, "-l")) {
365302 try system_libs.append(arg[2..]);
366303 } else {
367 try stderr.print("unrecognized parameter: '{}'", .{arg});
304 std.debug.warn("unrecognized parameter: '{}'", .{arg});
368305 process.exit(1);
369306 }
370 } else if (mem.endsWith(u8, arg, ".s")) {
371 try assembly_files.append(arg);
307 } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {
308 std.debug.warn("assembly files not supported yet", .{});
309 process.exit(1);
372310 } else if (mem.endsWith(u8, arg, ".o") or
373311 mem.endsWith(u8, arg, ".obj") or
374312 mem.endsWith(u8, arg, ".a") or
375313 mem.endsWith(u8, arg, ".lib"))
376314 {
377 try link_objects.append(arg);
315 std.debug.warn("object files and static libraries not supported yet", .{});
316 process.exit(1);
378317 } else if (mem.endsWith(u8, arg, ".c") or
379318 mem.endsWith(u8, arg, ".cpp"))
380319 {
381 try c_src_files.append(arg);
382 } else if (mem.endsWith(u8, arg, ".zig")) {
320 std.debug.warn("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});
321 process.exit(1);
322 } else if (mem.endsWith(u8, arg, ".so") or
323 mem.endsWith(u8, arg, ".dylib") or
324 mem.endsWith(u8, arg, ".dll"))
325 {
326 std.debug.warn("linking against dynamic libraries not yet supported", .{});
327 process.exit(1);
328 } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {
383329 if (root_src_file) |other| {
384 try stderr.print("found another zig file '{}' after root source file '{}'", .{
385 arg,
386 other,
387 });
330 std.debug.warn("found another zig file '{}' after root source file '{}'", .{ arg, other });
388331 process.exit(1);
389332 } else {
390333 root_src_file = arg;
391334 }
392335 } else {
393 try stderr.print("unrecognized file extension of parameter '{}'", .{arg});
336 std.debug.warn("unrecognized file extension of parameter '{}'", .{arg});
394337 }
395338 }
396339 }
397340
398 if (cur_pkg.parent != null) {
399 try stderr.print("unmatched --pkg-begin\n", .{});
400 process.exit(1);
401 }
402
403341 const root_name = if (provided_name) |n| n else blk: {
404342 if (root_src_file) |file| {
405343 const basename = fs.path.basename(file);
406344 var it = mem.split(basename, ".");
407345 break :blk it.next() orelse basename;
408346 } else {
409 try stderr.writeAll("--name [name] not provided and unable to infer\n");
347 std.debug.warn("--name [name] not provided and unable to infer\n", .{});
410348 process.exit(1);
411349 }
412350 };
413351
414 if (root_src_file == null and link_objects.len == 0 and assembly_files.len == 0) {
415 try stderr.writeAll("Expected source file argument or at least one --object or --assembly argument\n");
352 if (system_libs.items.len != 0) {
353 std.debug.warn("linking against system libraries not yet supported", .{});
416354 process.exit(1);
417355 }
418356
419 if (out_type == Compilation.Kind.Obj and link_objects.len != 0) {
420 try stderr.writeAll("When building an object file, --object arguments are invalid\n");
357 var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{};
358 const cross_target = std.zig.CrossTarget.parse(.{
359 .arch_os_abi = target_arch_os_abi,
360 .cpu_features = target_mcpu,
361 .dynamic_linker = target_dynamic_linker,
362 .diagnostics = &diags,
363 }) catch |err| switch (err) {
364 error.UnknownCpuModel => {
365 std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
366 diags.cpu_name.?,
367 @tagName(diags.arch.?),
368 });
369 for (diags.arch.?.allCpuModels()) |cpu| {
370 std.debug.warn(" {}\n", .{cpu.name});
371 }
372 process.exit(1);
373 },
374 error.UnknownCpuFeature => {
375 std.debug.warn(
376 \\Unknown CPU feature: '{}'
377 \\Available CPU features for architecture '{}':
378 \\
379 , .{
380 diags.unknown_feature_name,
381 @tagName(diags.arch.?),
382 });
383 for (diags.arch.?.allFeaturesList()) |feature| {
384 std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });
385 }
386 process.exit(1);
387 },
388 else => |e| return e,
389 };
390
391 const object_format: ?std.builtin.ObjectFormat = null;
392 var target_info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
393 if (target_info.cpu_detection_unimplemented) {
394 // TODO We want to just use detected_info.target but implementing
395 // CPU model & feature detection is todo so here we rely on LLVM.
396 std.debug.warn("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
421397 process.exit(1);
422398 }
423399
424 try ZigCompiler.setLlvmArgv(allocator, mllvm_flags.span());
400 const src_path = root_src_file orelse {
401 std.debug.warn("expected at least one file argument", .{});
402 process.exit(1);
403 };
425404
426 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch process.exit(1);
427 defer allocator.free(zig_lib_dir);
405 const bin_path = switch (emit_bin) {
406 .no => {
407 std.debug.warn("-fno-emit-bin not supported yet", .{});
408 process.exit(1);
409 },
410 .yes_default_path => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }),
411 .yes => |p| p,
412 };
428413
429 var override_libc: LibCInstallation = undefined;
414 const zir_out_path: ?[]const u8 = switch (emit_zir) {
415 .no => null,
416 .yes_default_path => blk: {
417 if (root_src_file) |rsf| {
418 if (mem.endsWith(u8, rsf, ".zir")) {
419 break :blk try std.fmt.allocPrint(arena, "{}.out.zir", .{root_name});
420 }
421 }
422 break :blk try std.fmt.allocPrint(arena, "{}.zir", .{root_name});
423 },
424 .yes => |p| p,
425 };
430426
431 var zig_compiler = try ZigCompiler.init(allocator);
432 defer zig_compiler.deinit();
427 const root_pkg = try Package.create(gpa, fs.cwd(), ".", src_path);
428 defer root_pkg.destroy();
429
430 var module = try Module.init(gpa, .{
431 .target = target_info.target,
432 .output_mode = output_mode,
433 .root_pkg = root_pkg,
434 .bin_file_dir = fs.cwd(),
435 .bin_file_path = bin_path,
436 .link_mode = link_mode,
437 .object_format = object_format,
438 .optimize_mode = build_mode,
439 });
440 defer module.deinit();
433441
434 var comp = try Compilation.create(
435 &zig_compiler,
436 root_name,
437 root_src_file,
438 .{},
439 out_type,
440 build_mode,
441 !is_dynamic,
442 zig_lib_dir,
443 );
444 defer comp.destroy();
442 const stdin = std.io.getStdIn().inStream();
443 const stderr = std.io.getStdErr().outStream();
444 var repl_buf: [1024]u8 = undefined;
445445
446 if (libc_arg) |libc_path| {
447 parseLibcPaths(allocator, &override_libc, libc_path);
448 comp.override_libc = &override_libc;
449 }
446 try updateModule(gpa, &module, zir_out_path);
450447
451 for (system_libs.span()) |lib| {
452 _ = try comp.addLinkLib(lib, true);
448 while (watch) {
449 try stderr.print("🦎 ", .{});
450 if (output_mode == .Exe) {
451 try module.makeBinFileExecutable();
452 }
453 if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {
454 try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});
455 continue;
456 }) |line| {
457 if (mem.eql(u8, line, "update")) {
458 if (output_mode == .Exe) {
459 try module.makeBinFileWritable();
460 }
461 try updateModule(gpa, &module, zir_out_path);
462 } else if (mem.eql(u8, line, "exit")) {
463 break;
464 } else if (mem.eql(u8, line, "help")) {
465 try stderr.writeAll(repl_help);
466 } else {
467 try stderr.print("unknown command: {}\n", .{line});
468 }
469 } else {
470 break;
471 }
453472 }
473}
454474
455 comp.version = version;
456 comp.is_test = false;
457 comp.linker_script = linker_script;
458 comp.clang_argv = clang_argv_buf.span();
459 comp.strip = strip;
460
461 comp.verbose_tokenize = verbose_tokenize;
462 comp.verbose_ast_tree = verbose_ast_tree;
463 comp.verbose_ast_fmt = verbose_ast_fmt;
464 comp.verbose_link = verbose_link;
465 comp.verbose_ir = verbose_ir;
466 comp.verbose_llvm_ir = verbose_llvm_ir;
467 comp.verbose_cimport = verbose_cimport;
468
469 comp.link_eh_frame_hdr = link_eh_frame_hdr;
470
471 comp.err_color = color;
475fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void {
476 try module.update();
472477
473 comp.linker_rdynamic = linker_rdynamic;
478 var errors = try module.getAllErrorsAlloc();
479 defer errors.deinit(module.allocator);
474480
475 if (macosx_version_min != null and ios_version_min != null) {
476 try stderr.writeAll("-mmacosx-version-min and -mios-version-min options not allowed together\n");
477 process.exit(1);
481 if (errors.list.len != 0) {
482 for (errors.list) |full_err_msg| {
483 std.debug.warn("{}:{}:{}: error: {}\n", .{
484 full_err_msg.src_path,
485 full_err_msg.line + 1,
486 full_err_msg.column + 1,
487 full_err_msg.msg,
488 });
489 }
478490 }
479491
480 if (macosx_version_min) |ver| {
481 comp.darwin_version_min = Compilation.DarwinVersionMin{ .MacOS = ver };
482 }
483 if (ios_version_min) |ver| {
484 comp.darwin_version_min = Compilation.DarwinVersionMin{ .Ios = ver };
485 }
492 if (zir_out_path) |zop| {
493 var new_zir_module = try zir.emit(gpa, module.*);
494 defer new_zir_module.deinit(gpa);
486495
487 comp.emit_bin = emit_bin;
488 comp.emit_asm = emit_asm;
489 comp.emit_llvm_ir = emit_llvm_ir;
490 comp.emit_h = emit_h;
491 comp.assembly_files = assembly_files.span();
492 comp.link_objects = link_objects.span();
496 const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{});
497 defer baf.destroy();
493498
494 comp.start();
495 processBuildEvents(comp, color);
496}
499 try new_zir_module.writeToStream(gpa, baf.stream());
497500
498fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
499 const stderr_file = io.getStdErr();
500 const stderr = stderr_file.outStream();
501 var count: usize = 0;
502 while (!comp.cancelled) {
503 const build_event = comp.events.get();
504 count += 1;
505
506 switch (build_event) {
507 .Ok => {
508 stderr.print("Build {} succeeded\n", .{count}) catch process.exit(1);
509 },
510 .Error => |err| {
511 stderr.print("Build {} failed: {}\n", .{ count, @errorName(err) }) catch process.exit(1);
512 },
513 .Fail => |msgs| {
514 stderr.print("Build {} compile errors:\n", .{count}) catch process.exit(1);
515 for (msgs) |msg| {
516 defer msg.destroy();
517 msg.printToFile(stderr_file, color) catch process.exit(1);
518 }
519 },
520 }
501 try baf.finish();
521502 }
522503}
523504
505const repl_help =
506 \\Commands:
507 \\ update Detect changes to source files and update output files.
508 \\ help Print this text
509 \\ exit Quit this repl
510 \\
511;
512
524513pub const usage_fmt =
525514 \\usage: zig fmt [file]...
526515 \\
......@@ -539,58 +528,20 @@ pub const usage_fmt =
539528;
540529
541530const Fmt = struct {
542 seen: event.Locked(SeenMap),
531 seen: SeenMap,
543532 any_error: bool,
544 color: errmsg.Color,
545 allocator: *Allocator,
533 color: Color,
534 gpa: *Allocator,
546535
547 const SeenMap = std.StringHashMap(void);
536 const SeenMap = std.BufSet;
548537};
549538
550fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
551 const stderr = io.getStdErr().outStream();
552 libc.* = LibCInstallation.parse(allocator, libc_paths_file, stderr) catch |err| {
553 stderr.print("Unable to parse libc path file '{}': {}.\n" ++
554 "Try running `zig libc` to see an example for the native target.\n", .{
555 libc_paths_file,
556 @errorName(err),
557 }) catch {};
558 process.exit(1);
559 };
560}
561
562fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
563 const stderr = io.getStdErr().outStream();
564 switch (args.len) {
565 0 => {},
566 1 => {
567 var libc_installation: LibCInstallation = undefined;
568 parseLibcPaths(allocator, &libc_installation, args[0]);
569 return;
570 },
571 else => {
572 try stderr.print("unexpected extra parameter: {}\n", .{args[1]});
573 process.exit(1);
574 },
575 }
576
577 var zig_compiler = try ZigCompiler.init(allocator);
578 defer zig_compiler.deinit();
579
580 const libc = zig_compiler.getNativeLibC() catch |err| {
581 stderr.print("unable to find libc: {}\n", .{@errorName(err)}) catch {};
582 process.exit(1);
583 };
584 libc.render(io.getStdOut().outStream()) catch process.exit(1);
585}
586
587fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
539pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
588540 const stderr_file = io.getStdErr();
589 const stderr = stderr_file.outStream();
590 var color: errmsg.Color = .Auto;
541 var color: Color = .Auto;
591542 var stdin_flag: bool = false;
592543 var check_flag: bool = false;
593 var input_files = ArrayList([]const u8).init(allocator);
544 var input_files = ArrayList([]const u8).init(gpa);
594545
595546 {
596547 var i: usize = 0;
......@@ -603,7 +554,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
603554 process.exit(0);
604555 } else if (mem.eql(u8, arg, "--color")) {
605556 if (i + 1 >= args.len) {
606 try stderr.writeAll("expected [auto|on|off] after --color\n");
557 std.debug.warn("expected [auto|on|off] after --color\n", .{});
607558 process.exit(1);
608559 }
609560 i += 1;
......@@ -615,7 +566,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
615566 } else if (mem.eql(u8, next_arg, "off")) {
616567 color = .Off;
617568 } else {
618 try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
569 std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
619570 process.exit(1);
620571 }
621572 } else if (mem.eql(u8, arg, "--stdin")) {
......@@ -623,7 +574,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
623574 } else if (mem.eql(u8, arg, "--check")) {
624575 check_flag = true;
625576 } else {
626 try stderr.print("unrecognized parameter: '{}'", .{arg});
577 std.debug.warn("unrecognized parameter: '{}'", .{arg});
627578 process.exit(1);
628579 }
629580 } else {
......@@ -633,60 +584,55 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
633584 }
634585
635586 if (stdin_flag) {
636 if (input_files.len != 0) {
637 try stderr.writeAll("cannot use --stdin with positional arguments\n");
587 if (input_files.items.len != 0) {
588 std.debug.warn("cannot use --stdin with positional arguments\n", .{});
638589 process.exit(1);
639590 }
640591
641592 const stdin = io.getStdIn().inStream();
642593
643 const source_code = try stdin.readAllAlloc(allocator, max_src_size);
644 defer allocator.free(source_code);
594 const source_code = try stdin.readAllAlloc(gpa, max_src_size);
595 defer gpa.free(source_code);
645596
646 const tree = std.zig.parse(allocator, source_code) catch |err| {
647 try stderr.print("error parsing stdin: {}\n", .{err});
597 const tree = std.zig.parse(gpa, source_code) catch |err| {
598 std.debug.warn("error parsing stdin: {}\n", .{err});
648599 process.exit(1);
649600 };
650601 defer tree.deinit();
651602
652603 var error_it = tree.errors.iterator(0);
653604 while (error_it.next()) |parse_error| {
654 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, tree, "<stdin>");
655 defer msg.destroy();
656
657 try msg.printToFile(io.getStdErr(), color);
605 try printErrMsgToFile(gpa, parse_error, tree, "<stdin>", stderr_file, color);
658606 }
659607 if (tree.errors.len != 0) {
660608 process.exit(1);
661609 }
662610 if (check_flag) {
663 const anything_changed = try std.zig.render(allocator, io.null_out_stream, tree);
664 const code: u8 = if (anything_changed) 1 else 0;
611 const anything_changed = try std.zig.render(gpa, io.null_out_stream, tree);
612 const code = if (anything_changed) @as(u8, 1) else @as(u8, 0);
665613 process.exit(code);
666614 }
667615
668616 const stdout = io.getStdOut().outStream();
669 _ = try std.zig.render(allocator, stdout, tree);
617 _ = try std.zig.render(gpa, stdout, tree);
670618 return;
671619 }
672620
673 if (input_files.len == 0) {
674 try stderr.writeAll("expected at least one source file argument\n");
621 if (input_files.items.len == 0) {
622 std.debug.warn("expected at least one source file argument\n", .{});
675623 process.exit(1);
676624 }
677625
678626 var fmt = Fmt{
679 .allocator = allocator,
680 .seen = event.Locked(Fmt.SeenMap).init(Fmt.SeenMap.init(allocator)),
627 .gpa = gpa,
628 .seen = Fmt.SeenMap.init(gpa),
681629 .any_error = false,
682630 .color = color,
683631 };
684632
685 var group = event.Group(FmtError!void).init(allocator);
686633 for (input_files.span()) |file_path| {
687 try group.call(fmtPath, .{ &fmt, file_path, check_flag });
634 try fmtPath(&fmt, file_path, check_flag);
688635 }
689 try group.wait();
690636 if (fmt.any_error) {
691637 process.exit(1);
692638 }
......@@ -711,53 +657,45 @@ const FmtError = error{
711657 ReadOnlyFileSystem,
712658 LinkQuotaExceeded,
713659 FileBusy,
714 CurrentWorkingDirectoryUnlinked,
715660} || fs.File.OpenError;
716fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) callconv(.Async) FmtError!void {
717 const stderr_file = io.getStdErr();
718 const stderr = stderr_file.outStream();
719
720 const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref);
721 defer fmt.allocator.free(file_path);
722661
723 {
724 const held = fmt.seen.acquire();
725 defer held.release();
662fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
663 // get the real path here to avoid Windows failing on relative file paths with . or .. in them
664 var real_path = fs.realpathAlloc(fmt.gpa, file_path) catch |err| {
665 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
666 fmt.any_error = true;
667 return;
668 };
669 defer fmt.gpa.free(real_path);
726670
727 if (try held.value.put(file_path, {})) |_| return;
728 }
671 if (fmt.seen.exists(real_path)) return;
672 try fmt.seen.put(real_path);
729673
730 const source_code = fs.cwd().readFileAlloc(
731 fmt.allocator,
732 file_path,
733 max_src_size,
734 ) catch |err| switch (err) {
674 const source_code = fs.cwd().readFileAlloc(fmt.gpa, real_path, max_src_size) catch |err| switch (err) {
735675 error.IsDir, error.AccessDenied => {
736676 var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
737677 defer dir.close();
738678
739 var group = event.Group(FmtError!void).init(fmt.allocator);
740 var it = dir.iterate();
741 while (try it.next()) |entry| {
679 var dir_it = dir.iterate();
680
681 while (try dir_it.next()) |entry| {
742682 if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
743 const full_path = try fs.path.join(fmt.allocator, &[_][]const u8{ file_path, entry.name });
744 @panic("TODO https://github.com/ziglang/zig/issues/3777");
745 // try group.call(fmtPath, .{fmt, full_path, check_mode});
683 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
684 try fmtPath(fmt, full_path, check_mode);
746685 }
747686 }
748 return group.wait();
687 return;
749688 },
750689 else => {
751 // TODO lock stderr printing
752 try stderr.print("unable to open '{}': {}\n", .{ file_path, err });
690 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
753691 fmt.any_error = true;
754692 return;
755693 },
756694 };
757 defer fmt.allocator.free(source_code);
695 defer fmt.gpa.free(source_code);
758696
759 const tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
760 try stderr.print("error parsing file '{}': {}\n", .{ file_path, err });
697 const tree = std.zig.parse(fmt.gpa, source_code) catch |err| {
698 std.debug.warn("error parsing file '{}': {}\n", .{ file_path, err });
761699 fmt.any_error = true;
762700 return;
763701 };
......@@ -765,10 +703,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) callconv(.Asy
765703
766704 var error_it = tree.errors.iterator(0);
767705 while (error_it.next()) |parse_error| {
768 const msg = try errmsg.Msg.createFromParseError(fmt.allocator, parse_error, tree, file_path);
769 defer fmt.allocator.destroy(msg);
770
771 try msg.printToFile(stderr_file, fmt.color);
706 try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color);
772707 }
773708 if (tree.errors.len != 0) {
774709 fmt.any_error = true;
......@@ -776,32 +711,67 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) callconv(.Asy
776711 }
777712
778713 if (check_mode) {
779 const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, tree);
714 const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);
780715 if (anything_changed) {
781 try stderr.print("{}\n", .{file_path});
716 std.debug.warn("{}\n", .{file_path});
782717 fmt.any_error = true;
783718 }
784719 } else {
785 // TODO make this evented
786 const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path);
720 const baf = try io.BufferedAtomicFile.create(fmt.gpa, fs.cwd(), real_path, .{});
787721 defer baf.destroy();
788722
789 const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), tree);
723 const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);
790724 if (anything_changed) {
791 try stderr.print("{}\n", .{file_path});
725 std.debug.warn("{}\n", .{file_path});
792726 try baf.finish();
793727 }
794728 }
795729}
796730
797fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void {
798 const stdout = io.getStdOut().outStream();
799 try stdout.print("{}\n", .{c.ZIG_VERSION_STRING});
800}
801
802fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void {
803 const stdout = io.getStdOut();
804 try stdout.writeAll(usage);
731fn printErrMsgToFile(
732 gpa: *mem.Allocator,
733 parse_error: *const ast.Error,
734 tree: *ast.Tree,
735 path: []const u8,
736 file: fs.File,
737 color: Color,
738) !void {
739 const color_on = switch (color) {
740 .Auto => file.isTty(),
741 .On => true,
742 .Off => false,
743 };
744 const lok_token = parse_error.loc();
745 const span_first = lok_token;
746 const span_last = lok_token;
747
748 const first_token = tree.tokens.at(span_first);
749 const last_token = tree.tokens.at(span_last);
750 const start_loc = tree.tokenLocationPtr(0, first_token);
751 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
752
753 var text_buf = std.ArrayList(u8).init(gpa);
754 defer text_buf.deinit();
755 const out_stream = text_buf.outStream();
756 try parse_error.render(&tree.tokens, out_stream);
757 const text = text_buf.span();
758
759 const stream = file.outStream();
760 try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
761
762 if (!color_on) return;
763
764 // Print \r and \t as one space each so that column counts line up
765 for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| {
766 try stream.writeByte(switch (byte) {
767 '\r', '\t' => ' ',
768 else => byte,
769 });
770 }
771 try stream.writeByte('\n');
772 try stream.writeByteNTimes(' ', start_loc.column);
773 try stream.writeByteNTimes('~', last_token.end - first_token.start);
774 try stream.writeByte('\n');
805775}
806776
807777pub const info_zen =
......@@ -816,90 +786,8 @@ pub const info_zen =
816786 \\ * Avoid local maximums.
817787 \\ * Reduce the amount one must remember.
818788 \\ * Minimize energy spent on coding style.
789 \\ * Resource deallocation must succeed.
819790 \\ * Together we serve end users.
820791 \\
821792 \\
822793;
823
824fn cmdZen(allocator: *Allocator, args: []const []const u8) !void {
825 try io.getStdOut().writeAll(info_zen);
826}
827
828const usage_internal =
829 \\usage: zig internal [subcommand]
830 \\
831 \\Sub-Commands:
832 \\ build-info Print static compiler build-info
833 \\
834 \\
835;
836
837fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void {
838 const stderr = io.getStdErr().outStream();
839 if (args.len == 0) {
840 try stderr.writeAll(usage_internal);
841 process.exit(1);
842 }
843
844 const sub_commands = [_]Command{Command{
845 .name = "build-info",
846 .exec = cmdInternalBuildInfo,
847 }};
848
849 inline for (sub_commands) |sub_command| {
850 if (mem.eql(u8, sub_command.name, args[0])) {
851 var frame = try allocator.create(@Frame(sub_command.exec));
852 defer allocator.destroy(frame);
853 frame.* = async sub_command.exec(allocator, args[1..]);
854 return await frame;
855 }
856 }
857
858 try stderr.print("unknown sub command: {}\n\n", .{args[0]});
859 try stderr.writeAll(usage_internal);
860}
861
862fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void {
863 const stdout = io.getStdOut().outStream();
864 try stdout.print(
865 \\ZIG_CMAKE_BINARY_DIR {}
866 \\ZIG_CXX_COMPILER {}
867 \\ZIG_LLD_INCLUDE_PATH {}
868 \\ZIG_LLD_LIBRARIES {}
869 \\ZIG_LLVM_CONFIG_EXE {}
870 \\ZIG_DIA_GUIDS_LIB {}
871 \\
872 , .{
873 c.ZIG_CMAKE_BINARY_DIR,
874 c.ZIG_CXX_COMPILER,
875 c.ZIG_LLD_INCLUDE_PATH,
876 c.ZIG_LLD_LIBRARIES,
877 c.ZIG_LLVM_CONFIG_EXE,
878 c.ZIG_DIA_GUIDS_LIB,
879 });
880}
881
882const CliPkg = struct {
883 name: []const u8,
884 path: []const u8,
885 children: ArrayList(*CliPkg),
886 parent: ?*CliPkg,
887
888 pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg {
889 var pkg = try allocator.create(CliPkg);
890 pkg.* = CliPkg{
891 .name = name,
892 .path = path,
893 .children = ArrayList(*CliPkg).init(allocator),
894 .parent = parent,
895 };
896 return pkg;
897 }
898
899 pub fn deinit(self: *CliPkg) void {
900 for (self.children.span()) |child| {
901 child.deinit();
902 }
903 self.children.deinit();
904 }
905};
src-self-hosted/package.zig deleted-31
......@@ -1,31 +0,0 @@
1const std = @import("std");
2const mem = std.mem;
3const assert = std.debug.assert;
4const ArrayListSentineled = std.ArrayListSentineled;
5
6pub const Package = struct {
7 root_src_dir: ArrayListSentineled(u8, 0),
8 root_src_path: ArrayListSentineled(u8, 0),
9
10 /// relative to root_src_dir
11 table: Table,
12
13 pub const Table = std.StringHashMap(*Package);
14
15 /// makes internal copies of root_src_dir and root_src_path
16 /// allocator should be an arena allocator because Package never frees anything
17 pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package {
18 const ptr = try allocator.create(Package);
19 ptr.* = Package{
20 .root_src_dir = try ArrayListSentineled(u8, 0).init(allocator, root_src_dir),
21 .root_src_path = try ArrayListSentineled(u8, 0).init(allocator, root_src_path),
22 .table = Table.init(allocator),
23 };
24 return ptr;
25 }
26
27 pub fn add(self: *Package, name: []const u8, package: *Package) !void {
28 const entry = try self.table.put(try mem.dupe(self.table.allocator, u8, name), package);
29 assert(entry == null);
30 }
31};
src-self-hosted/scope.zig deleted-418
......@@ -1,418 +0,0 @@
1const std = @import("std");
2const Allocator = mem.Allocator;
3const Decl = @import("decl.zig").Decl;
4const Compilation = @import("compilation.zig").Compilation;
5const mem = std.mem;
6const ast = std.zig.ast;
7const Value = @import("value.zig").Value;
8const Type = @import("type.zig").Type;
9const ir = @import("ir.zig");
10const Span = @import("errmsg.zig").Span;
11const assert = std.debug.assert;
12const event = std.event;
13const llvm = @import("llvm.zig");
14
15pub const Scope = struct {
16 id: Id,
17 parent: ?*Scope,
18 ref_count: std.atomic.Int(usize),
19
20 /// Thread-safe
21 pub fn ref(base: *Scope) void {
22 _ = base.ref_count.incr();
23 }
24
25 /// Thread-safe
26 pub fn deref(base: *Scope, comp: *Compilation) void {
27 if (base.ref_count.decr() == 1) {
28 if (base.parent) |parent| parent.deref(comp);
29 switch (base.id) {
30 .Root => @fieldParentPtr(Root, "base", base).destroy(comp),
31 .Decls => @fieldParentPtr(Decls, "base", base).destroy(comp),
32 .Block => @fieldParentPtr(Block, "base", base).destroy(comp),
33 .FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp),
34 .CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
35 .Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),
36 .DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),
37 .Var => @fieldParentPtr(Var, "base", base).destroy(comp),
38 .AstTree => @fieldParentPtr(AstTree, "base", base).destroy(comp),
39 }
40 }
41 }
42
43 pub fn findRoot(base: *Scope) *Root {
44 var scope = base;
45 while (scope.parent) |parent| {
46 scope = parent;
47 }
48 assert(scope.id == .Root);
49 return @fieldParentPtr(Root, "base", scope);
50 }
51
52 pub fn findFnDef(base: *Scope) ?*FnDef {
53 var scope = base;
54 while (true) {
55 switch (scope.id) {
56 .FnDef => return @fieldParentPtr(FnDef, "base", scope),
57 .Root, .Decls => return null,
58
59 .Block,
60 .Defer,
61 .DeferExpr,
62 .CompTime,
63 .Var,
64 => scope = scope.parent.?,
65
66 .AstTree => unreachable,
67 }
68 }
69 }
70
71 pub fn findDeferExpr(base: *Scope) ?*DeferExpr {
72 var scope = base;
73 while (true) {
74 switch (scope.id) {
75 .DeferExpr => return @fieldParentPtr(DeferExpr, "base", scope),
76
77 .FnDef,
78 .Decls,
79 => return null,
80
81 .Block,
82 .Defer,
83 .CompTime,
84 .Root,
85 .Var,
86 => scope = scope.parent orelse return null,
87
88 .AstTree => unreachable,
89 }
90 }
91 }
92
93 fn init(base: *Scope, id: Id, parent: *Scope) void {
94 base.* = Scope{
95 .id = id,
96 .parent = parent,
97 .ref_count = std.atomic.Int(usize).init(1),
98 };
99 parent.ref();
100 }
101
102 pub const Id = enum {
103 Root,
104 AstTree,
105 Decls,
106 Block,
107 FnDef,
108 CompTime,
109 Defer,
110 DeferExpr,
111 Var,
112 };
113
114 pub const Root = struct {
115 base: Scope,
116 realpath: []const u8,
117 decls: *Decls,
118
119 /// Creates a Root scope with 1 reference
120 /// Takes ownership of realpath
121 pub fn create(comp: *Compilation, realpath: []u8) !*Root {
122 const self = try comp.gpa().create(Root);
123 self.* = Root{
124 .base = Scope{
125 .id = .Root,
126 .parent = null,
127 .ref_count = std.atomic.Int(usize).init(1),
128 },
129 .realpath = realpath,
130 .decls = undefined,
131 };
132 errdefer comp.gpa().destroy(self);
133 self.decls = try Decls.create(comp, &self.base);
134 return self;
135 }
136
137 pub fn destroy(self: *Root, comp: *Compilation) void {
138 // TODO comp.fs_watch.removeFile(self.realpath);
139 self.decls.base.deref(comp);
140 comp.gpa().free(self.realpath);
141 comp.gpa().destroy(self);
142 }
143 };
144
145 pub const AstTree = struct {
146 base: Scope,
147 tree: *ast.Tree,
148
149 /// Creates a scope with 1 reference
150 /// Takes ownership of tree, will deinit and destroy when done.
151 pub fn create(comp: *Compilation, tree: *ast.Tree, root_scope: *Root) !*AstTree {
152 const self = try comp.gpa().create(AstTree);
153 self.* = AstTree{
154 .base = undefined,
155 .tree = tree,
156 };
157 self.base.init(.AstTree, &root_scope.base);
158
159 return self;
160 }
161
162 pub fn destroy(self: *AstTree, comp: *Compilation) void {
163 comp.gpa().free(self.tree.source);
164 self.tree.deinit();
165 comp.gpa().destroy(self);
166 }
167
168 pub fn root(self: *AstTree) *Root {
169 return self.base.findRoot();
170 }
171 };
172
173 pub const Decls = struct {
174 base: Scope,
175
176 /// This table remains Write Locked when the names are incomplete or possibly outdated.
177 /// So if a reader manages to grab a lock, it can be sure that the set of names is complete
178 /// and correct.
179 table: event.RwLocked(Decl.Table),
180
181 /// Creates a Decls scope with 1 reference
182 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
183 const self = try comp.gpa().create(Decls);
184 self.* = Decls{
185 .base = undefined,
186 .table = event.RwLocked(Decl.Table).init(Decl.Table.init(comp.gpa())),
187 };
188 self.base.init(.Decls, parent);
189 return self;
190 }
191
192 pub fn destroy(self: *Decls, comp: *Compilation) void {
193 self.table.deinit();
194 comp.gpa().destroy(self);
195 }
196 };
197
198 pub const Block = struct {
199 base: Scope,
200 incoming_values: std.ArrayList(*ir.Inst),
201 incoming_blocks: std.ArrayList(*ir.BasicBlock),
202 end_block: *ir.BasicBlock,
203 is_comptime: *ir.Inst,
204
205 safety: Safety,
206
207 const Safety = union(enum) {
208 Auto,
209 Manual: Manual,
210
211 const Manual = struct {
212 /// the source span that disabled the safety value
213 span: Span,
214
215 /// whether safety is enabled
216 enabled: bool,
217 };
218
219 fn get(self: Safety, comp: *Compilation) bool {
220 return switch (self) {
221 .Auto => switch (comp.build_mode) {
222 .Debug,
223 .ReleaseSafe,
224 => true,
225 .ReleaseFast,
226 .ReleaseSmall,
227 => false,
228 },
229 .Manual => |man| man.enabled,
230 };
231 }
232 };
233
234 /// Creates a Block scope with 1 reference
235 pub fn create(comp: *Compilation, parent: *Scope) !*Block {
236 const self = try comp.gpa().create(Block);
237 self.* = Block{
238 .base = undefined,
239 .incoming_values = undefined,
240 .incoming_blocks = undefined,
241 .end_block = undefined,
242 .is_comptime = undefined,
243 .safety = Safety.Auto,
244 };
245 self.base.init(.Block, parent);
246 return self;
247 }
248
249 pub fn destroy(self: *Block, comp: *Compilation) void {
250 comp.gpa().destroy(self);
251 }
252 };
253
254 pub const FnDef = struct {
255 base: Scope,
256
257 /// This reference is not counted so that the scope can get destroyed with the function
258 fn_val: ?*Value.Fn,
259
260 /// Creates a FnDef scope with 1 reference
261 /// Must set the fn_val later
262 pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {
263 const self = try comp.gpa().create(FnDef);
264 self.* = FnDef{
265 .base = undefined,
266 .fn_val = null,
267 };
268 self.base.init(.FnDef, parent);
269 return self;
270 }
271
272 pub fn destroy(self: *FnDef, comp: *Compilation) void {
273 comp.gpa().destroy(self);
274 }
275 };
276
277 pub const CompTime = struct {
278 base: Scope,
279
280 /// Creates a CompTime scope with 1 reference
281 pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {
282 const self = try comp.gpa().create(CompTime);
283 self.* = CompTime{ .base = undefined };
284 self.base.init(.CompTime, parent);
285 return self;
286 }
287
288 pub fn destroy(self: *CompTime, comp: *Compilation) void {
289 comp.gpa().destroy(self);
290 }
291 };
292
293 pub const Defer = struct {
294 base: Scope,
295 defer_expr_scope: *DeferExpr,
296 kind: Kind,
297
298 pub const Kind = enum {
299 ScopeExit,
300 ErrorExit,
301 };
302
303 /// Creates a Defer scope with 1 reference
304 pub fn create(
305 comp: *Compilation,
306 parent: *Scope,
307 kind: Kind,
308 defer_expr_scope: *DeferExpr,
309 ) !*Defer {
310 const self = try comp.gpa().create(Defer);
311 self.* = Defer{
312 .base = undefined,
313 .defer_expr_scope = defer_expr_scope,
314 .kind = kind,
315 };
316 self.base.init(.Defer, parent);
317 defer_expr_scope.base.ref();
318 return self;
319 }
320
321 pub fn destroy(self: *Defer, comp: *Compilation) void {
322 self.defer_expr_scope.base.deref(comp);
323 comp.gpa().destroy(self);
324 }
325 };
326
327 pub const DeferExpr = struct {
328 base: Scope,
329 expr_node: *ast.Node,
330 reported_err: bool,
331
332 /// Creates a DeferExpr scope with 1 reference
333 pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {
334 const self = try comp.gpa().create(DeferExpr);
335 self.* = DeferExpr{
336 .base = undefined,
337 .expr_node = expr_node,
338 .reported_err = false,
339 };
340 self.base.init(.DeferExpr, parent);
341 return self;
342 }
343
344 pub fn destroy(self: *DeferExpr, comp: *Compilation) void {
345 comp.gpa().destroy(self);
346 }
347 };
348
349 pub const Var = struct {
350 base: Scope,
351 name: []const u8,
352 src_node: *ast.Node,
353 data: Data,
354
355 pub const Data = union(enum) {
356 Param: Param,
357 Const: *Value,
358 };
359
360 pub const Param = struct {
361 index: usize,
362 typ: *Type,
363 llvm_value: *llvm.Value,
364 };
365
366 pub fn createParam(
367 comp: *Compilation,
368 parent: *Scope,
369 name: []const u8,
370 src_node: *ast.Node,
371 param_index: usize,
372 param_type: *Type,
373 ) !*Var {
374 const self = try create(comp, parent, name, src_node);
375 self.data = Data{
376 .Param = Param{
377 .index = param_index,
378 .typ = param_type,
379 .llvm_value = undefined,
380 },
381 };
382 return self;
383 }
384
385 pub fn createConst(
386 comp: *Compilation,
387 parent: *Scope,
388 name: []const u8,
389 src_node: *ast.Node,
390 value: *Value,
391 ) !*Var {
392 const self = try create(comp, parent, name, src_node);
393 self.data = Data{ .Const = value };
394 value.ref();
395 return self;
396 }
397
398 fn create(comp: *Compilation, parent: *Scope, name: []const u8, src_node: *ast.Node) !*Var {
399 const self = try comp.gpa().create(Var);
400 self.* = Var{
401 .base = undefined,
402 .name = name,
403 .src_node = src_node,
404 .data = undefined,
405 };
406 self.base.init(.Var, parent);
407 return self;
408 }
409
410 pub fn destroy(self: *Var, comp: *Compilation) void {
411 switch (self.data) {
412 .Param => {},
413 .Const => |value| value.deref(comp),
414 }
415 comp.gpa().destroy(self);
416 }
417 };
418};
src-self-hosted/stage2.zig+1-253
......@@ -12,7 +12,6 @@ const ArrayListSentineled = std.ArrayListSentineled;
1212const Target = std.Target;
1313const CrossTarget = std.zig.CrossTarget;
1414const self_hosted_main = @import("main.zig");
15const errmsg = @import("errmsg.zig");
1615const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer;
1716const assert = std.debug.assert;
1817const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
......@@ -168,8 +167,6 @@ export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
168167 return .None;
169168}
170169
171// TODO: just use the actual self-hosted zig fmt. Until https://github.com/ziglang/zig/issues/2377,
172// we use a blocking implementation.
173170export fn stage2_fmt(argc: c_int, argv: [*]const [*:0]const u8) c_int {
174171 if (std.debug.runtime_safety) {
175172 fmtMain(argc, argv) catch unreachable;
......@@ -191,258 +188,9 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
191188 try args_list.append(mem.spanZ(argv[arg_i]));
192189 }
193190
194 stdout = std.io.getStdOut().outStream();
195 stderr_file = std.io.getStdErr();
196 stderr = stderr_file.outStream();
197
198191 const args = args_list.span()[2..];
199192
200 var color: errmsg.Color = .Auto;
201 var stdin_flag: bool = false;
202 var check_flag: bool = false;
203 var input_files = ArrayList([]const u8).init(allocator);
204
205 {
206 var i: usize = 0;
207 while (i < args.len) : (i += 1) {
208 const arg = args[i];
209 if (mem.startsWith(u8, arg, "-")) {
210 if (mem.eql(u8, arg, "--help")) {
211 try stdout.writeAll(self_hosted_main.usage_fmt);
212 process.exit(0);
213 } else if (mem.eql(u8, arg, "--color")) {
214 if (i + 1 >= args.len) {
215 try stderr.writeAll("expected [auto|on|off] after --color\n");
216 process.exit(1);
217 }
218 i += 1;
219 const next_arg = args[i];
220 if (mem.eql(u8, next_arg, "auto")) {
221 color = .Auto;
222 } else if (mem.eql(u8, next_arg, "on")) {
223 color = .On;
224 } else if (mem.eql(u8, next_arg, "off")) {
225 color = .Off;
226 } else {
227 try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
228 process.exit(1);
229 }
230 } else if (mem.eql(u8, arg, "--stdin")) {
231 stdin_flag = true;
232 } else if (mem.eql(u8, arg, "--check")) {
233 check_flag = true;
234 } else {
235 try stderr.print("unrecognized parameter: '{}'", .{arg});
236 process.exit(1);
237 }
238 } else {
239 try input_files.append(arg);
240 }
241 }
242 }
243
244 if (stdin_flag) {
245 if (input_files.items.len != 0) {
246 try stderr.writeAll("cannot use --stdin with positional arguments\n");
247 process.exit(1);
248 }
249
250 const stdin_file = io.getStdIn();
251 var stdin = stdin_file.inStream();
252
253 const source_code = try stdin.readAllAlloc(allocator, self_hosted_main.max_src_size);
254 defer allocator.free(source_code);
255
256 const tree = std.zig.parse(allocator, source_code) catch |err| {
257 try stderr.print("error parsing stdin: {}\n", .{err});
258 process.exit(1);
259 };
260 defer tree.deinit();
261
262 var error_it = tree.errors.iterator(0);
263 while (error_it.next()) |parse_error| {
264 try printErrMsgToFile(allocator, parse_error, tree, "<stdin>", stderr_file, color);
265 }
266 if (tree.errors.len != 0) {
267 process.exit(1);
268 }
269 if (check_flag) {
270 const anything_changed = try std.zig.render(allocator, io.null_out_stream, tree);
271 const code = if (anything_changed) @as(u8, 1) else @as(u8, 0);
272 process.exit(code);
273 }
274
275 _ = try std.zig.render(allocator, stdout, tree);
276 return;
277 }
278
279 if (input_files.items.len == 0) {
280 try stderr.writeAll("expected at least one source file argument\n");
281 process.exit(1);
282 }
283
284 var fmt = Fmt{
285 .seen = Fmt.SeenMap.init(allocator),
286 .any_error = false,
287 .color = color,
288 .allocator = allocator,
289 };
290
291 for (input_files.span()) |file_path| {
292 try fmtPath(&fmt, file_path, check_flag);
293 }
294 if (fmt.any_error) {
295 process.exit(1);
296 }
297}
298
299const FmtError = error{
300 SystemResources,
301 OperationAborted,
302 IoPending,
303 BrokenPipe,
304 Unexpected,
305 WouldBlock,
306 FileClosed,
307 DestinationAddressRequired,
308 DiskQuota,
309 FileTooBig,
310 InputOutput,
311 NoSpaceLeft,
312 AccessDenied,
313 OutOfMemory,
314 RenameAcrossMountPoints,
315 ReadOnlyFileSystem,
316 LinkQuotaExceeded,
317 FileBusy,
318} || fs.File.OpenError;
319
320fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
321 // get the real path here to avoid Windows failing on relative file paths with . or .. in them
322 var real_path = fs.realpathAlloc(fmt.allocator, file_path) catch |err| {
323 try stderr.print("unable to open '{}': {}\n", .{ file_path, err });
324 fmt.any_error = true;
325 return;
326 };
327 defer fmt.allocator.free(real_path);
328
329 if (fmt.seen.exists(real_path)) return;
330 try fmt.seen.put(real_path);
331
332 const source_code = fs.cwd().readFileAlloc(fmt.allocator, real_path, self_hosted_main.max_src_size) catch |err| switch (err) {
333 error.IsDir, error.AccessDenied => {
334 // TODO make event based (and dir.next())
335 var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
336 defer dir.close();
337
338 var dir_it = dir.iterate();
339
340 while (try dir_it.next()) |entry| {
341 if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
342 const full_path = try fs.path.join(fmt.allocator, &[_][]const u8{ file_path, entry.name });
343 try fmtPath(fmt, full_path, check_mode);
344 }
345 }
346 return;
347 },
348 else => {
349 // TODO lock stderr printing
350 try stderr.print("unable to open '{}': {}\n", .{ file_path, err });
351 fmt.any_error = true;
352 return;
353 },
354 };
355 defer fmt.allocator.free(source_code);
356
357 const tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
358 try stderr.print("error parsing file '{}': {}\n", .{ file_path, err });
359 fmt.any_error = true;
360 return;
361 };
362 defer tree.deinit();
363
364 var error_it = tree.errors.iterator(0);
365 while (error_it.next()) |parse_error| {
366 try printErrMsgToFile(fmt.allocator, parse_error, tree, file_path, stderr_file, fmt.color);
367 }
368 if (tree.errors.len != 0) {
369 fmt.any_error = true;
370 return;
371 }
372
373 if (check_mode) {
374 const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, tree);
375 if (anything_changed) {
376 try stderr.print("{}\n", .{file_path});
377 fmt.any_error = true;
378 }
379 } else {
380 const baf = try io.BufferedAtomicFile.create(fmt.allocator, fs.cwd(), real_path, .{});
381 defer baf.destroy();
382
383 const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), tree);
384 if (anything_changed) {
385 try stderr.print("{}\n", .{file_path});
386 try baf.finish();
387 }
388 }
389}
390
391const Fmt = struct {
392 seen: SeenMap,
393 any_error: bool,
394 color: errmsg.Color,
395 allocator: *mem.Allocator,
396
397 const SeenMap = std.BufSet;
398};
399
400fn printErrMsgToFile(
401 allocator: *mem.Allocator,
402 parse_error: *const ast.Error,
403 tree: *ast.Tree,
404 path: []const u8,
405 file: fs.File,
406 color: errmsg.Color,
407) !void {
408 const color_on = switch (color) {
409 .Auto => file.isTty(),
410 .On => true,
411 .Off => false,
412 };
413 const lok_token = parse_error.loc();
414 const span = errmsg.Span{
415 .first = lok_token,
416 .last = lok_token,
417 };
418
419 const first_token = tree.tokens.at(span.first);
420 const last_token = tree.tokens.at(span.last);
421 const start_loc = tree.tokenLocationPtr(0, first_token);
422 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
423
424 var text_buf = std.ArrayList(u8).init(allocator);
425 defer text_buf.deinit();
426 const out_stream = text_buf.outStream();
427 try parse_error.render(&tree.tokens, out_stream);
428 const text = text_buf.span();
429
430 const stream = file.outStream();
431 try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
432
433 if (!color_on) return;
434
435 // Print \r and \t as one space each so that column counts line up
436 for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| {
437 try stream.writeByte(switch (byte) {
438 '\r', '\t' => ' ',
439 else => byte,
440 });
441 }
442 try stream.writeByte('\n');
443 try stream.writeByteNTimes(' ', start_loc.column);
444 try stream.writeByteNTimes('~', last_token.end - first_token.start);
445 try stream.writeByte('\n');
193 return self_hosted_main.cmdFmt(allocator, args);
446194}
447195
448196export fn stage2_DepTokenizer_init(input: [*]const u8, len: usize) stage2_DepTokenizer {
src-self-hosted/test.zig+97-102
......@@ -1,17 +1,18 @@
11const std = @import("std");
22const link = @import("link.zig");
3const ir = @import("ir.zig");
3const Module = @import("Module.zig");
44const Allocator = std.mem.Allocator;
5
6var global_ctx: TestContext = undefined;
5const zir = @import("zir.zig");
6const Package = @import("Package.zig");
77
88test "self-hosted" {
9 try global_ctx.init();
10 defer global_ctx.deinit();
9 var ctx: TestContext = undefined;
10 try ctx.init();
11 defer ctx.deinit();
1112
12 try @import("stage2_tests").addCases(&global_ctx);
13 try @import("stage2_tests").addCases(&ctx);
1314
14 try global_ctx.run();
15 try ctx.run();
1516}
1617
1718pub const TestContext = struct {
......@@ -20,32 +21,34 @@ pub const TestContext = struct {
2021
2122 pub const ZIRCompareOutputCase = struct {
2223 name: []const u8,
23 src: [:0]const u8,
24 expected_stdout: []const u8,
24 src_list: []const []const u8,
25 expected_stdout_list: []const []const u8,
2526 };
2627
2728 pub const ZIRTransformCase = struct {
2829 name: []const u8,
2930 src: [:0]const u8,
3031 expected_zir: []const u8,
32 cross_target: std.zig.CrossTarget,
3133 };
3234
3335 pub fn addZIRCompareOutput(
3436 ctx: *TestContext,
3537 name: []const u8,
36 src: [:0]const u8,
37 expected_stdout: []const u8,
38 src_list: []const []const u8,
39 expected_stdout_list: []const []const u8,
3840 ) void {
3941 ctx.zir_cmp_output_cases.append(.{
4042 .name = name,
41 .src = src,
42 .expected_stdout = expected_stdout,
43 .src_list = src_list,
44 .expected_stdout_list = expected_stdout_list,
4345 }) catch unreachable;
4446 }
4547
4648 pub fn addZIRTransform(
4749 ctx: *TestContext,
4850 name: []const u8,
51 cross_target: std.zig.CrossTarget,
4952 src: [:0]const u8,
5053 expected_zir: []const u8,
5154 ) void {
......@@ -53,6 +56,7 @@ pub const TestContext = struct {
5356 .name = name,
5457 .src = src,
5558 .expected_zir = expected_zir,
59 .cross_target = cross_target,
5660 }) catch unreachable;
5761 }
5862
......@@ -84,7 +88,8 @@ pub const TestContext = struct {
8488 }
8589 for (self.zir_transform_cases.items) |case| {
8690 std.testing.base_allocator_instance.reset();
87 try self.runOneZIRTransformCase(std.testing.allocator, root_node, case, native_info.target);
91 const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.cross_target);
92 try self.runOneZIRTransformCase(std.testing.allocator, root_node, case, info.target);
8893 try std.testing.allocator_instance.validate();
8994 }
9095 }
......@@ -99,77 +104,68 @@ pub const TestContext = struct {
99104 var tmp = std.testing.tmpDir(.{});
100105 defer tmp.cleanup();
101106
102 var prg_node = root_node.start(case.name, 4);
107 const tmp_src_path = "test-case.zir";
108 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
109 defer root_pkg.destroy();
110
111 var prg_node = root_node.start(case.name, case.src_list.len);
103112 prg_node.activate();
104113 defer prg_node.end();
105114
106 var zir_module = x: {
107 var parse_node = prg_node.start("parse", null);
108 parse_node.activate();
109 defer parse_node.end();
110
111 break :x try ir.text.parse(allocator, case.src);
112 };
113 defer zir_module.deinit(allocator);
114 if (zir_module.errors.len != 0) {
115 debugPrintErrors(case.src, zir_module.errors);
116 return error.ParseFailure;
117 }
118
119 var analyzed_module = x: {
120 var analyze_node = prg_node.start("analyze", null);
121 analyze_node.activate();
122 defer analyze_node.end();
123
124 break :x try ir.analyze(allocator, zir_module, .{
125 .target = target,
126 .output_mode = .Exe,
127 .link_mode = .Static,
128 .optimize_mode = .Debug,
129 });
130 };
131 defer analyzed_module.deinit(allocator);
132 if (analyzed_module.errors.len != 0) {
133 debugPrintErrors(case.src, analyzed_module.errors);
134 return error.ParseFailure;
135 }
136
137 var link_result = x: {
138 var link_node = prg_node.start("link", null);
139 link_node.activate();
140 defer link_node.end();
141
142 break :x try link.updateFilePath(allocator, analyzed_module, tmp.dir, "a.out");
143 };
144 defer link_result.deinit(allocator);
145 if (link_result.errors.len != 0) {
146 debugPrintErrors(case.src, link_result.errors);
147 return error.LinkFailure;
148 }
149
150 var exec_result = x: {
151 var exec_node = prg_node.start("execute", null);
152 exec_node.activate();
153 defer exec_node.end();
154
155 break :x try std.ChildProcess.exec(.{
156 .allocator = allocator,
157 .argv = &[_][]const u8{"./a.out"},
158 .cwd_dir = tmp.dir,
159 });
160 };
161 defer allocator.free(exec_result.stdout);
162 defer allocator.free(exec_result.stderr);
163 switch (exec_result.term) {
164 .Exited => |code| {
165 if (code != 0) {
166 std.debug.warn("elf file exited with code {}\n", .{code});
167 return error.BinaryBadExitCode;
168 }
169 },
170 else => return error.BinaryCrashed,
115 var module = try Module.init(allocator, .{
116 .target = target,
117 .output_mode = .Exe,
118 .optimize_mode = .Debug,
119 .bin_file_dir = tmp.dir,
120 .bin_file_path = "a.out",
121 .root_pkg = root_pkg,
122 });
123 defer module.deinit();
124
125 for (case.src_list) |source, i| {
126 var src_node = prg_node.start("update", 2);
127 src_node.activate();
128 defer src_node.end();
129
130 try tmp.dir.writeFile(tmp_src_path, source);
131
132 var update_node = src_node.start("parse,analysis,codegen", null);
133 update_node.activate();
134 try module.makeBinFileWritable();
135 try module.update();
136 update_node.end();
137
138 var exec_result = x: {
139 var exec_node = src_node.start("execute", null);
140 exec_node.activate();
141 defer exec_node.end();
142
143 try module.makeBinFileExecutable();
144 break :x try std.ChildProcess.exec(.{
145 .allocator = allocator,
146 .argv = &[_][]const u8{"./a.out"},
147 .cwd_dir = tmp.dir,
148 });
149 };
150 defer allocator.free(exec_result.stdout);
151 defer allocator.free(exec_result.stderr);
152 switch (exec_result.term) {
153 .Exited => |code| {
154 if (code != 0) {
155 std.debug.warn("elf file exited with code {}\n", .{code});
156 return error.BinaryBadExitCode;
157 }
158 },
159 else => return error.BinaryCrashed,
160 }
161 const expected_stdout = case.expected_stdout_list[i];
162 if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) {
163 std.debug.panic(
164 "update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n",
165 .{ i, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout },
166 );
167 }
171168 }
172 std.testing.expectEqualSlices(u8, case.expected_stdout, exec_result.stdout);
173169 }
174170
175171 fn runOneZIRTransformCase(
......@@ -179,38 +175,37 @@ pub const TestContext = struct {
179175 case: ZIRTransformCase,
180176 target: std.Target,
181177 ) !void {
182 var prg_node = root_node.start(case.name, 4);
178 var tmp = std.testing.tmpDir(.{});
179 defer tmp.cleanup();
180
181 var prg_node = root_node.start(case.name, 3);
183182 prg_node.activate();
184183 defer prg_node.end();
185184
186 var parse_node = prg_node.start("parse", null);
187 parse_node.activate();
188 var zir_module = try ir.text.parse(allocator, case.src);
189 defer zir_module.deinit(allocator);
190 if (zir_module.errors.len != 0) {
191 debugPrintErrors(case.src, zir_module.errors);
192 return error.ParseFailure;
193 }
194 parse_node.end();
185 const tmp_src_path = "test-case.zir";
186 try tmp.dir.writeFile(tmp_src_path, case.src);
195187
196 var analyze_node = prg_node.start("analyze", null);
197 analyze_node.activate();
198 var analyzed_module = try ir.analyze(allocator, zir_module, .{
188 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
189 defer root_pkg.destroy();
190
191 var module = try Module.init(allocator, .{
199192 .target = target,
200193 .output_mode = .Obj,
201 .link_mode = .Static,
202194 .optimize_mode = .Debug,
195 .bin_file_dir = tmp.dir,
196 .bin_file_path = "test-case.o",
197 .root_pkg = root_pkg,
203198 });
204 defer analyzed_module.deinit(allocator);
205 if (analyzed_module.errors.len != 0) {
206 debugPrintErrors(case.src, analyzed_module.errors);
207 return error.ParseFailure;
208 }
209 analyze_node.end();
199 defer module.deinit();
200
201 var module_node = prg_node.start("parse/analysis/codegen", null);
202 module_node.activate();
203 try module.update();
204 module_node.end();
210205
211206 var emit_node = prg_node.start("emit", null);
212207 emit_node.activate();
213 var new_zir_module = try ir.text.emit_zir(allocator, analyzed_module);
208 var new_zir_module = try zir.emit(allocator, module);
214209 defer new_zir_module.deinit(allocator);
215210 emit_node.end();
216211
src-self-hosted/type.zig+231-3
......@@ -5,8 +5,7 @@ const Allocator = std.mem.Allocator;
55const Target = std.Target;
66
77/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
8/// It's important for this struct to be small.
9/// It is not copyable since it may contain references to its inner data.
8/// It's important for this type to be small.
109/// Types are not de-duplicated, which helps with multi-threading since it obviates the requirement
1110/// of obtaining a lock on a global type table, as well as making the
1211/// garbage collection bookkeeping simpler.
......@@ -51,7 +50,9 @@ pub const Type = extern union {
5150 .comptime_int => return .ComptimeInt,
5251 .comptime_float => return .ComptimeFloat,
5352 .noreturn => return .NoReturn,
53 .@"null" => return .Null,
5454
55 .fn_noreturn_no_args => return .Fn,
5556 .fn_naked_noreturn_no_args => return .Fn,
5657 .fn_ccc_void_no_args => return .Fn,
5758
......@@ -183,7 +184,10 @@ pub const Type = extern union {
183184 .noreturn,
184185 => return out_stream.writeAll(@tagName(t)),
185186
187 .@"null" => return out_stream.writeAll("@TypeOf(null)"),
188
186189 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
190 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
187191 .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
188192 .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
189193 .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
......@@ -244,6 +248,8 @@ pub const Type = extern union {
244248 .comptime_int => return Value.initTag(.comptime_int_type),
245249 .comptime_float => return Value.initTag(.comptime_float_type),
246250 .noreturn => return Value.initTag(.noreturn_type),
251 .@"null" => return Value.initTag(.null_type),
252 .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
247253 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
248254 .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
249255 .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
......@@ -256,6 +262,110 @@ pub const Type = extern union {
256262 }
257263 }
258264
265 pub fn hasCodeGenBits(self: Type) bool {
266 return switch (self.tag()) {
267 .u8,
268 .i8,
269 .isize,
270 .usize,
271 .c_short,
272 .c_ushort,
273 .c_int,
274 .c_uint,
275 .c_long,
276 .c_ulong,
277 .c_longlong,
278 .c_ulonglong,
279 .c_longdouble,
280 .f16,
281 .f32,
282 .f64,
283 .f128,
284 .bool,
285 .anyerror,
286 .fn_noreturn_no_args,
287 .fn_naked_noreturn_no_args,
288 .fn_ccc_void_no_args,
289 .single_const_pointer_to_comptime_int,
290 .const_slice_u8,
291 .array_u8_sentinel_0,
292 .array, // TODO check for zero bits
293 .single_const_pointer,
294 .int_signed, // TODO check for zero bits
295 .int_unsigned, // TODO check for zero bits
296 => true,
297
298 .c_void,
299 .void,
300 .type,
301 .comptime_int,
302 .comptime_float,
303 .noreturn,
304 .@"null",
305 => false,
306 };
307 }
308
309 /// Asserts that hasCodeGenBits() is true.
310 pub fn abiAlignment(self: Type, target: Target) u32 {
311 return switch (self.tag()) {
312 .u8,
313 .i8,
314 .bool,
315 .fn_noreturn_no_args, // represents machine code; not a pointer
316 .fn_naked_noreturn_no_args, // represents machine code; not a pointer
317 .fn_ccc_void_no_args, // represents machine code; not a pointer
318 .array_u8_sentinel_0,
319 => return 1,
320
321 .isize,
322 .usize,
323 .single_const_pointer_to_comptime_int,
324 .const_slice_u8,
325 .single_const_pointer,
326 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
327
328 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
329 .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
330 .c_int => return @divExact(CType.int.sizeInBits(target), 8),
331 .c_uint => return @divExact(CType.uint.sizeInBits(target), 8),
332 .c_long => return @divExact(CType.long.sizeInBits(target), 8),
333 .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8),
334 .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8),
335 .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8),
336
337 .f16 => return 2,
338 .f32 => return 4,
339 .f64 => return 8,
340 .f128 => return 16,
341 .c_longdouble => return 16,
342
343 .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type
344
345 .array => return self.cast(Payload.Array).?.elem_type.abiAlignment(target),
346
347 .int_signed, .int_unsigned => {
348 const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
349 pl.bits
350 else if (self.cast(Payload.IntUnsigned)) |pl|
351 pl.bits
352 else
353 unreachable;
354
355 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
356 },
357
358 .c_void,
359 .void,
360 .type,
361 .comptime_int,
362 .comptime_float,
363 .noreturn,
364 .@"null",
365 => unreachable,
366 };
367 }
368
259369 pub fn isSinglePointer(self: Type) bool {
260370 return switch (self.tag()) {
261371 .u8,
......@@ -283,9 +393,11 @@ pub const Type = extern union {
283393 .comptime_int,
284394 .comptime_float,
285395 .noreturn,
396 .@"null",
286397 .array,
287398 .array_u8_sentinel_0,
288399 .const_slice_u8,
400 .fn_noreturn_no_args,
289401 .fn_naked_noreturn_no_args,
290402 .fn_ccc_void_no_args,
291403 .int_unsigned,
......@@ -325,10 +437,12 @@ pub const Type = extern union {
325437 .comptime_int,
326438 .comptime_float,
327439 .noreturn,
440 .@"null",
328441 .array,
329442 .array_u8_sentinel_0,
330443 .single_const_pointer,
331444 .single_const_pointer_to_comptime_int,
445 .fn_noreturn_no_args,
332446 .fn_naked_noreturn_no_args,
333447 .fn_ccc_void_no_args,
334448 .int_unsigned,
......@@ -367,8 +481,10 @@ pub const Type = extern union {
367481 .comptime_int,
368482 .comptime_float,
369483 .noreturn,
484 .@"null",
370485 .array,
371486 .array_u8_sentinel_0,
487 .fn_noreturn_no_args,
372488 .fn_naked_noreturn_no_args,
373489 .fn_ccc_void_no_args,
374490 .int_unsigned,
......@@ -410,6 +526,8 @@ pub const Type = extern union {
410526 .comptime_int,
411527 .comptime_float,
412528 .noreturn,
529 .@"null",
530 .fn_noreturn_no_args,
413531 .fn_naked_noreturn_no_args,
414532 .fn_ccc_void_no_args,
415533 .int_unsigned,
......@@ -451,6 +569,8 @@ pub const Type = extern union {
451569 .comptime_int,
452570 .comptime_float,
453571 .noreturn,
572 .@"null",
573 .fn_noreturn_no_args,
454574 .fn_naked_noreturn_no_args,
455575 .fn_ccc_void_no_args,
456576 .single_const_pointer,
......@@ -465,6 +585,50 @@ pub const Type = extern union {
465585 };
466586 }
467587
588 /// Asserts the type is an array or vector.
589 pub fn arraySentinel(self: Type) ?Value {
590 return switch (self.tag()) {
591 .u8,
592 .i8,
593 .isize,
594 .usize,
595 .c_short,
596 .c_ushort,
597 .c_int,
598 .c_uint,
599 .c_long,
600 .c_ulong,
601 .c_longlong,
602 .c_ulonglong,
603 .c_longdouble,
604 .f16,
605 .f32,
606 .f64,
607 .f128,
608 .c_void,
609 .bool,
610 .void,
611 .type,
612 .anyerror,
613 .comptime_int,
614 .comptime_float,
615 .noreturn,
616 .@"null",
617 .fn_noreturn_no_args,
618 .fn_naked_noreturn_no_args,
619 .fn_ccc_void_no_args,
620 .single_const_pointer,
621 .single_const_pointer_to_comptime_int,
622 .const_slice_u8,
623 .int_unsigned,
624 .int_signed,
625 => unreachable,
626
627 .array => return null,
628 .array_u8_sentinel_0 => return Value.initTag(.zero),
629 };
630 }
631
468632 /// Returns true if and only if the type is a fixed-width, signed integer.
469633 pub fn isSignedInt(self: Type) bool {
470634 return switch (self.tag()) {
......@@ -481,6 +645,8 @@ pub const Type = extern union {
481645 .comptime_int,
482646 .comptime_float,
483647 .noreturn,
648 .@"null",
649 .fn_noreturn_no_args,
484650 .fn_naked_noreturn_no_args,
485651 .fn_ccc_void_no_args,
486652 .array,
......@@ -524,6 +690,8 @@ pub const Type = extern union {
524690 .comptime_int,
525691 .comptime_float,
526692 .noreturn,
693 .@"null",
694 .fn_noreturn_no_args,
527695 .fn_naked_noreturn_no_args,
528696 .fn_ccc_void_no_args,
529697 .array,
......@@ -579,6 +747,7 @@ pub const Type = extern union {
579747 /// Asserts the type is a function.
580748 pub fn fnParamLen(self: Type) usize {
581749 return switch (self.tag()) {
750 .fn_noreturn_no_args => 0,
582751 .fn_naked_noreturn_no_args => 0,
583752 .fn_ccc_void_no_args => 0,
584753
......@@ -595,6 +764,7 @@ pub const Type = extern union {
595764 .comptime_int,
596765 .comptime_float,
597766 .noreturn,
767 .@"null",
598768 .array,
599769 .single_const_pointer,
600770 .single_const_pointer_to_comptime_int,
......@@ -622,6 +792,7 @@ pub const Type = extern union {
622792 /// given by `fnParamLen`.
623793 pub fn fnParamTypes(self: Type, types: []Type) void {
624794 switch (self.tag()) {
795 .fn_noreturn_no_args => return,
625796 .fn_naked_noreturn_no_args => return,
626797 .fn_ccc_void_no_args => return,
627798
......@@ -638,6 +809,7 @@ pub const Type = extern union {
638809 .comptime_int,
639810 .comptime_float,
640811 .noreturn,
812 .@"null",
641813 .array,
642814 .single_const_pointer,
643815 .single_const_pointer_to_comptime_int,
......@@ -664,6 +836,7 @@ pub const Type = extern union {
664836 /// Asserts the type is a function.
665837 pub fn fnReturnType(self: Type) Type {
666838 return switch (self.tag()) {
839 .fn_noreturn_no_args => Type.initTag(.noreturn),
667840 .fn_naked_noreturn_no_args => Type.initTag(.noreturn),
668841 .fn_ccc_void_no_args => Type.initTag(.void),
669842
......@@ -680,6 +853,7 @@ pub const Type = extern union {
680853 .comptime_int,
681854 .comptime_float,
682855 .noreturn,
856 .@"null",
683857 .array,
684858 .single_const_pointer,
685859 .single_const_pointer_to_comptime_int,
......@@ -706,6 +880,7 @@ pub const Type = extern union {
706880 /// Asserts the type is a function.
707881 pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
708882 return switch (self.tag()) {
883 .fn_noreturn_no_args => .Unspecified,
709884 .fn_naked_noreturn_no_args => .Naked,
710885 .fn_ccc_void_no_args => .C,
711886
......@@ -722,6 +897,51 @@ pub const Type = extern union {
722897 .comptime_int,
723898 .comptime_float,
724899 .noreturn,
900 .@"null",
901 .array,
902 .single_const_pointer,
903 .single_const_pointer_to_comptime_int,
904 .array_u8_sentinel_0,
905 .const_slice_u8,
906 .u8,
907 .i8,
908 .usize,
909 .isize,
910 .c_short,
911 .c_ushort,
912 .c_int,
913 .c_uint,
914 .c_long,
915 .c_ulong,
916 .c_longlong,
917 .c_ulonglong,
918 .int_unsigned,
919 .int_signed,
920 => unreachable,
921 };
922 }
923
924 /// Asserts the type is a function.
925 pub fn fnIsVarArgs(self: Type) bool {
926 return switch (self.tag()) {
927 .fn_noreturn_no_args => false,
928 .fn_naked_noreturn_no_args => false,
929 .fn_ccc_void_no_args => false,
930
931 .f16,
932 .f32,
933 .f64,
934 .f128,
935 .c_longdouble,
936 .c_void,
937 .bool,
938 .void,
939 .type,
940 .anyerror,
941 .comptime_int,
942 .comptime_float,
943 .noreturn,
944 .@"null",
725945 .array,
726946 .single_const_pointer,
727947 .single_const_pointer_to_comptime_int,
......@@ -776,6 +996,8 @@ pub const Type = extern union {
776996 .type,
777997 .anyerror,
778998 .noreturn,
999 .@"null",
1000 .fn_noreturn_no_args,
7791001 .fn_naked_noreturn_no_args,
7801002 .fn_ccc_void_no_args,
7811003 .array,
......@@ -812,6 +1034,7 @@ pub const Type = extern union {
8121034 .bool,
8131035 .type,
8141036 .anyerror,
1037 .fn_noreturn_no_args,
8151038 .fn_naked_noreturn_no_args,
8161039 .fn_ccc_void_no_args,
8171040 .single_const_pointer_to_comptime_int,
......@@ -822,6 +1045,7 @@ pub const Type = extern union {
8221045 .c_void,
8231046 .void,
8241047 .noreturn,
1048 .@"null",
8251049 => return true,
8261050
8271051 .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,
......@@ -865,6 +1089,7 @@ pub const Type = extern union {
8651089 .bool,
8661090 .type,
8671091 .anyerror,
1092 .fn_noreturn_no_args,
8681093 .fn_naked_noreturn_no_args,
8691094 .fn_ccc_void_no_args,
8701095 .single_const_pointer_to_comptime_int,
......@@ -873,6 +1098,7 @@ pub const Type = extern union {
8731098 .c_void,
8741099 .void,
8751100 .noreturn,
1101 .@"null",
8761102 .int_unsigned,
8771103 .int_signed,
8781104 .array,
......@@ -902,11 +1128,11 @@ pub const Type = extern union {
9021128 c_longlong,
9031129 c_ulonglong,
9041130 c_longdouble,
905 c_void,
9061131 f16,
9071132 f32,
9081133 f64,
9091134 f128,
1135 c_void,
9101136 bool,
9111137 void,
9121138 type,
......@@ -914,6 +1140,8 @@ pub const Type = extern union {
9141140 comptime_int,
9151141 comptime_float,
9161142 noreturn,
1143 @"null",
1144 fn_noreturn_no_args,
9171145 fn_naked_noreturn_no_args,
9181146 fn_ccc_void_no_args,
9191147 single_const_pointer_to_comptime_int,
src-self-hosted/util.zig deleted-47
......@@ -1,47 +0,0 @@
1const std = @import("std");
2const Target = std.Target;
3const llvm = @import("llvm.zig");
4
5pub fn getDarwinArchString(self: Target) [:0]const u8 {
6 switch (self.cpu.arch) {
7 .aarch64 => return "arm64",
8 .thumb,
9 .arm,
10 => return "arm",
11 .powerpc => return "ppc",
12 .powerpc64 => return "ppc64",
13 .powerpc64le => return "ppc64le",
14 // @tagName should be able to return sentinel terminated slice
15 else => @panic("TODO https://github.com/ziglang/zig/issues/3779"), //return @tagName(arch),
16 }
17}
18
19pub fn llvmTargetFromTriple(triple: [:0]const u8) !*llvm.Target {
20 var result: *llvm.Target = undefined;
21 var err_msg: [*:0]u8 = undefined;
22 if (llvm.GetTargetFromTriple(triple, &result, &err_msg) != 0) {
23 std.debug.warn("triple: {s} error: {s}\n", .{ triple, err_msg });
24 return error.UnsupportedTarget;
25 }
26 return result;
27}
28
29pub fn initializeAllTargets() void {
30 llvm.InitializeAllTargets();
31 llvm.InitializeAllTargetInfos();
32 llvm.InitializeAllTargetMCs();
33 llvm.InitializeAllAsmPrinters();
34 llvm.InitializeAllAsmParsers();
35}
36
37pub fn getLLVMTriple(allocator: *std.mem.Allocator, target: std.Target) ![:0]u8 {
38 var result = try std.ArrayListSentineled(u8, 0).initSize(allocator, 0);
39 defer result.deinit();
40
41 try result.outStream().print(
42 "{}-unknown-{}-{}",
43 .{ @tagName(target.cpu.arch), @tagName(target.os.tag), @tagName(target.abi) },
44 );
45
46 return result.toOwnedSlice();
47}
src-self-hosted/value.zig+124-80
......@@ -6,10 +6,11 @@ const BigIntConst = std.math.big.int.Const;
66const BigIntMutable = std.math.big.int.Mutable;
77const Target = std.Target;
88const Allocator = std.mem.Allocator;
9const Module = @import("Module.zig");
910
1011/// This is the raw data, with no bookkeeping, no memory awareness,
1112/// no de-duplication, and no type system awareness.
12/// It's important for this struct to be small.
13/// It's important for this type to be small.
1314/// This union takes advantage of the fact that the first page of memory
1415/// is unmapped, giving us 4096 possible enum tags that have no payload.
1516pub const Value = extern union {
......@@ -45,6 +46,8 @@ pub const Value = extern union {
4546 comptime_int_type,
4647 comptime_float_type,
4748 noreturn_type,
49 null_type,
50 fn_noreturn_no_args_type,
4851 fn_naked_noreturn_no_args_type,
4952 fn_ccc_void_no_args_type,
5053 single_const_pointer_to_comptime_int_type,
......@@ -64,8 +67,9 @@ pub const Value = extern union {
6467 int_big_positive,
6568 int_big_negative,
6669 function,
67 ref,
6870 ref_val,
71 decl_ref,
72 elem_ptr,
6973 bytes,
7074 repeated, // the value is a value repeated some number of times
7175
......@@ -136,6 +140,8 @@ pub const Value = extern union {
136140 .comptime_int_type => return out_stream.writeAll("comptime_int"),
137141 .comptime_float_type => return out_stream.writeAll("comptime_float"),
138142 .noreturn_type => return out_stream.writeAll("noreturn"),
143 .null_type => return out_stream.writeAll("@TypeOf(null)"),
144 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
139145 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
140146 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
141147 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
......@@ -153,11 +159,16 @@ pub const Value = extern union {
153159 .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
154160 .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
155161 .function => return out_stream.writeAll("(function)"),
156 .ref => return out_stream.writeAll("(ref)"),
157162 .ref_val => {
158 try out_stream.writeAll("*const ");
159 val = val.cast(Payload.RefVal).?.val;
160 continue;
163 const ref_val = val.cast(Payload.RefVal).?;
164 try out_stream.writeAll("&const ");
165 val = ref_val.val;
166 },
167 .decl_ref => return out_stream.writeAll("(decl ref)"),
168 .elem_ptr => {
169 const elem_ptr = val.cast(Payload.ElemPtr).?;
170 try out_stream.print("&[{}] ", .{elem_ptr.index});
171 val = elem_ptr.array_ptr;
161172 },
162173 .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
163174 .repeated => {
......@@ -169,10 +180,17 @@ pub const Value = extern union {
169180
170181 /// Asserts that the value is representable as an array of bytes.
171182 /// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
172 pub fn toAllocatedBytes(self: Value, allocator: *Allocator) Allocator.Error![]u8 {
183 pub fn toAllocatedBytes(self: Value, allocator: *Allocator) ![]u8 {
173184 if (self.cast(Payload.Bytes)) |bytes| {
174185 return std.mem.dupe(allocator, u8, bytes.data);
175186 }
187 if (self.cast(Payload.Repeated)) |repeated| {
188 @panic("TODO implement toAllocatedBytes for this Value tag");
189 }
190 if (self.cast(Payload.DeclRef)) |declref| {
191 const val = try declref.decl.value();
192 return val.toAllocatedBytes(allocator);
193 }
176194 unreachable;
177195 }
178196
......@@ -181,31 +199,33 @@ pub const Value = extern union {
181199 return switch (self.tag()) {
182200 .ty => self.cast(Payload.Ty).?.ty,
183201
184 .u8_type => Type.initTag(.@"u8"),
185 .i8_type => Type.initTag(.@"i8"),
186 .isize_type => Type.initTag(.@"isize"),
187 .usize_type => Type.initTag(.@"usize"),
188 .c_short_type => Type.initTag(.@"c_short"),
189 .c_ushort_type => Type.initTag(.@"c_ushort"),
190 .c_int_type => Type.initTag(.@"c_int"),
191 .c_uint_type => Type.initTag(.@"c_uint"),
192 .c_long_type => Type.initTag(.@"c_long"),
193 .c_ulong_type => Type.initTag(.@"c_ulong"),
194 .c_longlong_type => Type.initTag(.@"c_longlong"),
195 .c_ulonglong_type => Type.initTag(.@"c_ulonglong"),
196 .c_longdouble_type => Type.initTag(.@"c_longdouble"),
197 .f16_type => Type.initTag(.@"f16"),
198 .f32_type => Type.initTag(.@"f32"),
199 .f64_type => Type.initTag(.@"f64"),
200 .f128_type => Type.initTag(.@"f128"),
201 .c_void_type => Type.initTag(.@"c_void"),
202 .bool_type => Type.initTag(.@"bool"),
203 .void_type => Type.initTag(.@"void"),
204 .type_type => Type.initTag(.@"type"),
205 .anyerror_type => Type.initTag(.@"anyerror"),
206 .comptime_int_type => Type.initTag(.@"comptime_int"),
207 .comptime_float_type => Type.initTag(.@"comptime_float"),
208 .noreturn_type => Type.initTag(.@"noreturn"),
202 .u8_type => Type.initTag(.u8),
203 .i8_type => Type.initTag(.i8),
204 .isize_type => Type.initTag(.isize),
205 .usize_type => Type.initTag(.usize),
206 .c_short_type => Type.initTag(.c_short),
207 .c_ushort_type => Type.initTag(.c_ushort),
208 .c_int_type => Type.initTag(.c_int),
209 .c_uint_type => Type.initTag(.c_uint),
210 .c_long_type => Type.initTag(.c_long),
211 .c_ulong_type => Type.initTag(.c_ulong),
212 .c_longlong_type => Type.initTag(.c_longlong),
213 .c_ulonglong_type => Type.initTag(.c_ulonglong),
214 .c_longdouble_type => Type.initTag(.c_longdouble),
215 .f16_type => Type.initTag(.f16),
216 .f32_type => Type.initTag(.f32),
217 .f64_type => Type.initTag(.f64),
218 .f128_type => Type.initTag(.f128),
219 .c_void_type => Type.initTag(.c_void),
220 .bool_type => Type.initTag(.bool),
221 .void_type => Type.initTag(.void),
222 .type_type => Type.initTag(.type),
223 .anyerror_type => Type.initTag(.anyerror),
224 .comptime_int_type => Type.initTag(.comptime_int),
225 .comptime_float_type => Type.initTag(.comptime_float),
226 .noreturn_type => Type.initTag(.noreturn),
227 .null_type => Type.initTag(.@"null"),
228 .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
209229 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
210230 .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
211231 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
......@@ -222,8 +242,9 @@ pub const Value = extern union {
222242 .int_big_positive,
223243 .int_big_negative,
224244 .function,
225 .ref,
226245 .ref_val,
246 .decl_ref,
247 .elem_ptr,
227248 .bytes,
228249 .repeated,
229250 => unreachable,
......@@ -259,6 +280,8 @@ pub const Value = extern union {
259280 .comptime_int_type,
260281 .comptime_float_type,
261282 .noreturn_type,
283 .null_type,
284 .fn_noreturn_no_args_type,
262285 .fn_naked_noreturn_no_args_type,
263286 .fn_ccc_void_no_args_type,
264287 .single_const_pointer_to_comptime_int_type,
......@@ -267,8 +290,9 @@ pub const Value = extern union {
267290 .bool_false,
268291 .null_value,
269292 .function,
270 .ref,
271293 .ref_val,
294 .decl_ref,
295 .elem_ptr,
272296 .bytes,
273297 .undef,
274298 .repeated,
......@@ -314,6 +338,8 @@ pub const Value = extern union {
314338 .comptime_int_type,
315339 .comptime_float_type,
316340 .noreturn_type,
341 .null_type,
342 .fn_noreturn_no_args_type,
317343 .fn_naked_noreturn_no_args_type,
318344 .fn_ccc_void_no_args_type,
319345 .single_const_pointer_to_comptime_int_type,
......@@ -322,8 +348,9 @@ pub const Value = extern union {
322348 .bool_false,
323349 .null_value,
324350 .function,
325 .ref,
326351 .ref_val,
352 .decl_ref,
353 .elem_ptr,
327354 .bytes,
328355 .undef,
329356 .repeated,
......@@ -370,6 +397,8 @@ pub const Value = extern union {
370397 .comptime_int_type,
371398 .comptime_float_type,
372399 .noreturn_type,
400 .null_type,
401 .fn_noreturn_no_args_type,
373402 .fn_naked_noreturn_no_args_type,
374403 .fn_ccc_void_no_args_type,
375404 .single_const_pointer_to_comptime_int_type,
......@@ -378,8 +407,9 @@ pub const Value = extern union {
378407 .bool_false,
379408 .null_value,
380409 .function,
381 .ref,
382410 .ref_val,
411 .decl_ref,
412 .elem_ptr,
383413 .bytes,
384414 .undef,
385415 .repeated,
......@@ -431,6 +461,8 @@ pub const Value = extern union {
431461 .comptime_int_type,
432462 .comptime_float_type,
433463 .noreturn_type,
464 .null_type,
465 .fn_noreturn_no_args_type,
434466 .fn_naked_noreturn_no_args_type,
435467 .fn_ccc_void_no_args_type,
436468 .single_const_pointer_to_comptime_int_type,
......@@ -439,8 +471,9 @@ pub const Value = extern union {
439471 .bool_false,
440472 .null_value,
441473 .function,
442 .ref,
443474 .ref_val,
475 .decl_ref,
476 .elem_ptr,
444477 .bytes,
445478 .repeated,
446479 => unreachable,
......@@ -521,6 +554,8 @@ pub const Value = extern union {
521554 .comptime_int_type,
522555 .comptime_float_type,
523556 .noreturn_type,
557 .null_type,
558 .fn_noreturn_no_args_type,
524559 .fn_naked_noreturn_no_args_type,
525560 .fn_ccc_void_no_args_type,
526561 .single_const_pointer_to_comptime_int_type,
......@@ -529,8 +564,9 @@ pub const Value = extern union {
529564 .bool_false,
530565 .null_value,
531566 .function,
532 .ref,
533567 .ref_val,
568 .decl_ref,
569 .elem_ptr,
534570 .bytes,
535571 .repeated,
536572 .undef,
......@@ -573,6 +609,8 @@ pub const Value = extern union {
573609 .comptime_int_type,
574610 .comptime_float_type,
575611 .noreturn_type,
612 .null_type,
613 .fn_noreturn_no_args_type,
576614 .fn_naked_noreturn_no_args_type,
577615 .fn_ccc_void_no_args_type,
578616 .single_const_pointer_to_comptime_int_type,
......@@ -581,8 +619,9 @@ pub const Value = extern union {
581619 .bool_false,
582620 .null_value,
583621 .function,
584 .ref,
585622 .ref_val,
623 .decl_ref,
624 .elem_ptr,
586625 .bytes,
587626 .repeated,
588627 .undef,
......@@ -636,7 +675,8 @@ pub const Value = extern union {
636675 }
637676
638677 /// Asserts the value is a pointer and dereferences it.
639 pub fn pointerDeref(self: Value) Value {
678 /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
679 pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
640680 return switch (self.tag()) {
641681 .ty,
642682 .u8_type,
......@@ -664,6 +704,8 @@ pub const Value = extern union {
664704 .comptime_int_type,
665705 .comptime_float_type,
666706 .noreturn_type,
707 .null_type,
708 .fn_noreturn_no_args_type,
667709 .fn_naked_noreturn_no_args_type,
668710 .fn_ccc_void_no_args_type,
669711 .single_const_pointer_to_comptime_int_type,
......@@ -683,14 +725,19 @@ pub const Value = extern union {
683725 => unreachable,
684726
685727 .the_one_possible_value => Value.initTag(.the_one_possible_value),
686 .ref => self.cast(Payload.Ref).?.cell.contents,
687728 .ref_val => self.cast(Payload.RefVal).?.val,
729 .decl_ref => self.cast(Payload.DeclRef).?.decl.value(),
730 .elem_ptr => {
731 const elem_ptr = self.cast(Payload.ElemPtr).?;
732 const array_val = try elem_ptr.array_ptr.pointerDeref(allocator);
733 return array_val.elemValue(allocator, elem_ptr.index);
734 },
688735 };
689736 }
690737
691738 /// Asserts the value is a single-item pointer to an array, or an array,
692739 /// or an unknown-length pointer, and returns the element value at the index.
693 pub fn elemValueAt(self: Value, allocator: *Allocator, index: usize) Allocator.Error!Value {
740 pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value {
694741 switch (self.tag()) {
695742 .ty,
696743 .u8_type,
......@@ -718,6 +765,8 @@ pub const Value = extern union {
718765 .comptime_int_type,
719766 .comptime_float_type,
720767 .noreturn_type,
768 .null_type,
769 .fn_noreturn_no_args_type,
721770 .fn_naked_noreturn_no_args_type,
722771 .fn_ccc_void_no_args_type,
723772 .single_const_pointer_to_comptime_int_type,
......@@ -733,13 +782,13 @@ pub const Value = extern union {
733782 .int_big_positive,
734783 .int_big_negative,
735784 .undef,
785 .elem_ptr,
786 .ref_val,
787 .decl_ref,
736788 => unreachable,
737789
738 .ref => @panic("TODO figure out how MemoryCell works"),
739 .ref_val => @panic("TODO figure out how MemoryCell works"),
740
741790 .bytes => {
742 const int_payload = try allocator.create(Value.Payload.Int_u64);
791 const int_payload = try allocator.create(Payload.Int_u64);
743792 int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
744793 return Value.initPayload(&int_payload.base);
745794 },
......@@ -749,6 +798,17 @@ pub const Value = extern union {
749798 }
750799 }
751800
801 /// Returns a pointer to the element value at the index.
802 pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value {
803 const payload = try allocator.create(Payload.ElemPtr);
804 if (self.cast(Payload.ElemPtr)) |elem_ptr| {
805 payload.* = .{ .array_ptr = elem_ptr.array_ptr, .index = elem_ptr.index + index };
806 } else {
807 payload.* = .{ .array_ptr = self, .index = index };
808 }
809 return Value.initPayload(&payload.base);
810 }
811
752812 pub fn isUndef(self: Value) bool {
753813 return self.tag() == .undef;
754814 }
......@@ -783,6 +843,8 @@ pub const Value = extern union {
783843 .comptime_int_type,
784844 .comptime_float_type,
785845 .noreturn_type,
846 .null_type,
847 .fn_noreturn_no_args_type,
786848 .fn_naked_noreturn_no_args_type,
787849 .fn_ccc_void_no_args_type,
788850 .single_const_pointer_to_comptime_int_type,
......@@ -796,8 +858,9 @@ pub const Value = extern union {
796858 .int_i64,
797859 .int_big_positive,
798860 .int_big_negative,
799 .ref,
800861 .ref_val,
862 .decl_ref,
863 .elem_ptr,
801864 .bytes,
802865 .repeated,
803866 => false,
......@@ -841,8 +904,7 @@ pub const Value = extern union {
841904
842905 pub const Function = struct {
843906 base: Payload = Payload{ .tag = .function },
844 /// Index into the `fns` array of the `ir.Module`
845 index: usize,
907 func: *Module.Fn,
846908 };
847909
848910 pub const ArraySentinel0_u8_Type = struct {
......@@ -855,16 +917,24 @@ pub const Value = extern union {
855917 elem_type: *Type,
856918 };
857919
858 pub const Ref = struct {
859 base: Payload = Payload{ .tag = .ref },
860 cell: *MemoryCell,
861 };
862
920 /// Represents a pointer to another immutable value.
863921 pub const RefVal = struct {
864922 base: Payload = Payload{ .tag = .ref_val },
865923 val: Value,
866924 };
867925
926 /// Represents a pointer to a decl, not the value of the decl.
927 pub const DeclRef = struct {
928 base: Payload = Payload{ .tag = .decl_ref },
929 decl: *Module.Decl,
930 };
931
932 pub const ElemPtr = struct {
933 base: Payload = Payload{ .tag = .elem_ptr },
934 array_ptr: Value,
935 index: usize,
936 };
937
868938 pub const Bytes = struct {
869939 base: Payload = Payload{ .tag = .bytes },
870940 data: []const u8,
......@@ -890,29 +960,3 @@ pub const Value = extern union {
890960 limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
891961 };
892962};
893
894/// This is the heart of resource management of the Zig compiler. The Zig compiler uses
895/// stop-the-world mark-and-sweep garbage collection during compilation to manage the resources
896/// associated with evaluating compile-time code and semantic analysis. Each `MemoryCell` represents
897/// a root.
898pub const MemoryCell = struct {
899 parent: Parent,
900 contents: Value,
901
902 pub const Parent = union(enum) {
903 none,
904 struct_field: struct {
905 struct_base: *MemoryCell,
906 field_index: usize,
907 },
908 array_elem: struct {
909 array_base: *MemoryCell,
910 elem_index: usize,
911 },
912 union_field: *MemoryCell,
913 err_union_code: *MemoryCell,
914 err_union_payload: *MemoryCell,
915 optional_payload: *MemoryCell,
916 optional_flag: *MemoryCell,
917 };
918};
src-self-hosted/visib.zig deleted-4
......@@ -1,4 +0,0 @@
1pub const Visib = enum {
2 Private,
3 Pub,
4};
src-self-hosted/zir.zig created+1477
......@@ -0,0 +1,1477 @@
1//! This file has to do with parsing and rendering the ZIR text format.
2
3const std = @import("std");
4const mem = std.mem;
5const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;
7const BigIntConst = std.math.big.int.Const;
8const BigIntMutable = std.math.big.int.Mutable;
9const Type = @import("type.zig").Type;
10const Value = @import("value.zig").Value;
11const TypedValue = @import("TypedValue.zig");
12const ir = @import("ir.zig");
13const IrModule = @import("Module.zig");
14
15/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for
16/// in-memory, analyzed instructions with types and values.
17pub const Inst = struct {
18 tag: Tag,
19 /// Byte offset into the source.
20 src: usize,
21 name: []const u8,
22
23 /// Slice into the source of the part after the = and before the next instruction.
24 contents: []const u8 = &[0]u8{},
25
26 /// These names are used directly as the instruction names in the text format.
27 pub const Tag = enum {
28 breakpoint,
29 call,
30 /// Represents a reference to a global decl by name.
31 /// The syntax `@foo` is equivalent to `declref("foo")`.
32 declref,
33 str,
34 int,
35 ptrtoint,
36 fieldptr,
37 deref,
38 as,
39 @"asm",
40 @"unreachable",
41 @"return",
42 @"fn",
43 @"export",
44 primitive,
45 ref,
46 fntype,
47 intcast,
48 bitcast,
49 elemptr,
50 add,
51 cmp,
52 condbr,
53 isnull,
54 isnonnull,
55 };
56
57 pub fn TagToType(tag: Tag) type {
58 return switch (tag) {
59 .breakpoint => Breakpoint,
60 .call => Call,
61 .declref => DeclRef,
62 .str => Str,
63 .int => Int,
64 .ptrtoint => PtrToInt,
65 .fieldptr => FieldPtr,
66 .deref => Deref,
67 .as => As,
68 .@"asm" => Asm,
69 .@"unreachable" => Unreachable,
70 .@"return" => Return,
71 .@"fn" => Fn,
72 .@"export" => Export,
73 .primitive => Primitive,
74 .ref => Ref,
75 .fntype => FnType,
76 .intcast => IntCast,
77 .bitcast => BitCast,
78 .elemptr => ElemPtr,
79 .add => Add,
80 .cmp => Cmp,
81 .condbr => CondBr,
82 .isnull => IsNull,
83 .isnonnull => IsNonNull,
84 };
85 }
86
87 pub fn cast(base: *Inst, comptime T: type) ?*T {
88 if (base.tag != T.base_tag)
89 return null;
90
91 return @fieldParentPtr(T, "base", base);
92 }
93
94 pub const Breakpoint = struct {
95 pub const base_tag = Tag.breakpoint;
96 base: Inst,
97
98 positionals: struct {},
99 kw_args: struct {},
100 };
101
102 pub const Call = struct {
103 pub const base_tag = Tag.call;
104 base: Inst,
105
106 positionals: struct {
107 func: *Inst,
108 args: []*Inst,
109 },
110 kw_args: struct {
111 modifier: std.builtin.CallOptions.Modifier = .auto,
112 },
113 };
114
115 pub const DeclRef = struct {
116 pub const base_tag = Tag.declref;
117 base: Inst,
118
119 positionals: struct {
120 name: *Inst,
121 },
122 kw_args: struct {},
123 };
124
125 pub const Str = struct {
126 pub const base_tag = Tag.str;
127 base: Inst,
128
129 positionals: struct {
130 bytes: []const u8,
131 },
132 kw_args: struct {},
133 };
134
135 pub const Int = struct {
136 pub const base_tag = Tag.int;
137 base: Inst,
138
139 positionals: struct {
140 int: BigIntConst,
141 },
142 kw_args: struct {},
143 };
144
145 pub const PtrToInt = struct {
146 pub const base_tag = Tag.ptrtoint;
147 base: Inst,
148
149 positionals: struct {
150 ptr: *Inst,
151 },
152 kw_args: struct {},
153 };
154
155 pub const FieldPtr = struct {
156 pub const base_tag = Tag.fieldptr;
157 base: Inst,
158
159 positionals: struct {
160 object_ptr: *Inst,
161 field_name: *Inst,
162 },
163 kw_args: struct {},
164 };
165
166 pub const Deref = struct {
167 pub const base_tag = Tag.deref;
168 base: Inst,
169
170 positionals: struct {
171 ptr: *Inst,
172 },
173 kw_args: struct {},
174 };
175
176 pub const As = struct {
177 pub const base_tag = Tag.as;
178 base: Inst,
179
180 positionals: struct {
181 dest_type: *Inst,
182 value: *Inst,
183 },
184 kw_args: struct {},
185 };
186
187 pub const Asm = struct {
188 pub const base_tag = Tag.@"asm";
189 base: Inst,
190
191 positionals: struct {
192 asm_source: *Inst,
193 return_type: *Inst,
194 },
195 kw_args: struct {
196 @"volatile": bool = false,
197 output: ?*Inst = null,
198 inputs: []*Inst = &[0]*Inst{},
199 clobbers: []*Inst = &[0]*Inst{},
200 args: []*Inst = &[0]*Inst{},
201 },
202 };
203
204 pub const Unreachable = struct {
205 pub const base_tag = Tag.@"unreachable";
206 base: Inst,
207
208 positionals: struct {},
209 kw_args: struct {},
210 };
211
212 pub const Return = struct {
213 pub const base_tag = Tag.@"return";
214 base: Inst,
215
216 positionals: struct {},
217 kw_args: struct {},
218 };
219
220 pub const Fn = struct {
221 pub const base_tag = Tag.@"fn";
222 base: Inst,
223
224 positionals: struct {
225 fn_type: *Inst,
226 body: Module.Body,
227 },
228 kw_args: struct {},
229 };
230
231 pub const Export = struct {
232 pub const base_tag = Tag.@"export";
233 base: Inst,
234
235 positionals: struct {
236 symbol_name: *Inst,
237 value: *Inst,
238 },
239 kw_args: struct {},
240 };
241
242 pub const Ref = struct {
243 pub const base_tag = Tag.ref;
244 base: Inst,
245
246 positionals: struct {
247 operand: *Inst,
248 },
249 kw_args: struct {},
250 };
251
252 pub const Primitive = struct {
253 pub const base_tag = Tag.primitive;
254 base: Inst,
255
256 positionals: struct {
257 tag: BuiltinType,
258 },
259 kw_args: struct {},
260
261 pub const BuiltinType = enum {
262 isize,
263 usize,
264 c_short,
265 c_ushort,
266 c_int,
267 c_uint,
268 c_long,
269 c_ulong,
270 c_longlong,
271 c_ulonglong,
272 c_longdouble,
273 c_void,
274 f16,
275 f32,
276 f64,
277 f128,
278 bool,
279 void,
280 noreturn,
281 type,
282 anyerror,
283 comptime_int,
284 comptime_float,
285
286 pub fn toType(self: BuiltinType) Type {
287 return switch (self) {
288 .isize => Type.initTag(.isize),
289 .usize => Type.initTag(.usize),
290 .c_short => Type.initTag(.c_short),
291 .c_ushort => Type.initTag(.c_ushort),
292 .c_int => Type.initTag(.c_int),
293 .c_uint => Type.initTag(.c_uint),
294 .c_long => Type.initTag(.c_long),
295 .c_ulong => Type.initTag(.c_ulong),
296 .c_longlong => Type.initTag(.c_longlong),
297 .c_ulonglong => Type.initTag(.c_ulonglong),
298 .c_longdouble => Type.initTag(.c_longdouble),
299 .c_void => Type.initTag(.c_void),
300 .f16 => Type.initTag(.f16),
301 .f32 => Type.initTag(.f32),
302 .f64 => Type.initTag(.f64),
303 .f128 => Type.initTag(.f128),
304 .bool => Type.initTag(.bool),
305 .void => Type.initTag(.void),
306 .noreturn => Type.initTag(.noreturn),
307 .type => Type.initTag(.type),
308 .anyerror => Type.initTag(.anyerror),
309 .comptime_int => Type.initTag(.comptime_int),
310 .comptime_float => Type.initTag(.comptime_float),
311 };
312 }
313 };
314 };
315
316 pub const FnType = struct {
317 pub const base_tag = Tag.fntype;
318 base: Inst,
319
320 positionals: struct {
321 param_types: []*Inst,
322 return_type: *Inst,
323 },
324 kw_args: struct {
325 cc: std.builtin.CallingConvention = .Unspecified,
326 },
327 };
328
329 pub const IntCast = struct {
330 pub const base_tag = Tag.intcast;
331 base: Inst,
332
333 positionals: struct {
334 dest_type: *Inst,
335 value: *Inst,
336 },
337 kw_args: struct {},
338 };
339
340 pub const BitCast = struct {
341 pub const base_tag = Tag.bitcast;
342 base: Inst,
343
344 positionals: struct {
345 dest_type: *Inst,
346 operand: *Inst,
347 },
348 kw_args: struct {},
349 };
350
351 pub const ElemPtr = struct {
352 pub const base_tag = Tag.elemptr;
353 base: Inst,
354
355 positionals: struct {
356 array_ptr: *Inst,
357 index: *Inst,
358 },
359 kw_args: struct {},
360 };
361
362 pub const Add = struct {
363 pub const base_tag = Tag.add;
364 base: Inst,
365
366 positionals: struct {
367 lhs: *Inst,
368 rhs: *Inst,
369 },
370 kw_args: struct {},
371 };
372
373 pub const Cmp = struct {
374 pub const base_tag = Tag.cmp;
375 base: Inst,
376
377 positionals: struct {
378 lhs: *Inst,
379 op: std.math.CompareOperator,
380 rhs: *Inst,
381 },
382 kw_args: struct {},
383 };
384
385 pub const CondBr = struct {
386 pub const base_tag = Tag.condbr;
387 base: Inst,
388
389 positionals: struct {
390 condition: *Inst,
391 true_body: Module.Body,
392 false_body: Module.Body,
393 },
394 kw_args: struct {},
395 };
396
397 pub const IsNull = struct {
398 pub const base_tag = Tag.isnull;
399 base: Inst,
400
401 positionals: struct {
402 operand: *Inst,
403 },
404 kw_args: struct {},
405 };
406
407 pub const IsNonNull = struct {
408 pub const base_tag = Tag.isnonnull;
409 base: Inst,
410
411 positionals: struct {
412 operand: *Inst,
413 },
414 kw_args: struct {},
415 };
416};
417
418pub const ErrorMsg = struct {
419 byte_offset: usize,
420 msg: []const u8,
421};
422
423pub const Module = struct {
424 decls: []*Inst,
425 arena: std.heap.ArenaAllocator,
426 error_msg: ?ErrorMsg = null,
427
428 pub const Body = struct {
429 instructions: []*Inst,
430 };
431
432 pub fn deinit(self: *Module, allocator: *Allocator) void {
433 allocator.free(self.decls);
434 self.arena.deinit();
435 self.* = undefined;
436 }
437
438 /// This is a debugging utility for rendering the tree to stderr.
439 pub fn dump(self: Module) void {
440 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
441 }
442
443 const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body });
444
445 /// The allocator is used for temporary storage, but this function always returns
446 /// with no resources allocated.
447 pub fn writeToStream(self: Module, allocator: *Allocator, stream: var) !void {
448 // First, build a map of *Inst to @ or % indexes
449 var inst_table = InstPtrTable.init(allocator);
450 defer inst_table.deinit();
451
452 try inst_table.ensureCapacity(self.decls.len);
453
454 for (self.decls) |decl, decl_i| {
455 try inst_table.putNoClobber(decl, .{ .index = decl_i, .fn_body = null });
456
457 if (decl.cast(Inst.Fn)) |fn_inst| {
458 for (fn_inst.positionals.body.instructions) |inst, inst_i| {
459 try inst_table.putNoClobber(inst, .{ .index = inst_i, .fn_body = &fn_inst.positionals.body });
460 }
461 }
462 }
463
464 for (self.decls) |decl, i| {
465 try stream.print("@{} ", .{i});
466 try self.writeInstToStream(stream, decl, &inst_table);
467 try stream.writeByte('\n');
468 }
469 }
470
471 fn writeInstToStream(
472 self: Module,
473 stream: var,
474 decl: *Inst,
475 inst_table: *const InstPtrTable,
476 ) @TypeOf(stream).Error!void {
477 // TODO I tried implementing this with an inline for loop and hit a compiler bug
478 switch (decl.tag) {
479 .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),
480 .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table),
481 .declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table),
482 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
483 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
484 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
485 .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, decl, inst_table),
486 .deref => return self.writeInstToStreamGeneric(stream, .deref, decl, inst_table),
487 .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table),
488 .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
489 .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
490 .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table),
491 .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
492 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
493 .ref => return self.writeInstToStreamGeneric(stream, .ref, decl, inst_table),
494 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
495 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
496 .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
497 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
498 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table),
499 .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table),
500 .cmp => return self.writeInstToStreamGeneric(stream, .cmp, decl, inst_table),
501 .condbr => return self.writeInstToStreamGeneric(stream, .condbr, decl, inst_table),
502 .isnull => return self.writeInstToStreamGeneric(stream, .isnull, decl, inst_table),
503 .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, decl, inst_table),
504 }
505 }
506
507 fn writeInstToStreamGeneric(
508 self: Module,
509 stream: var,
510 comptime inst_tag: Inst.Tag,
511 base: *Inst,
512 inst_table: *const InstPtrTable,
513 ) !void {
514 const SpecificInst = Inst.TagToType(inst_tag);
515 const inst = @fieldParentPtr(SpecificInst, "base", base);
516 const Positionals = @TypeOf(inst.positionals);
517 try stream.writeAll("= " ++ @tagName(inst_tag) ++ "(");
518 const pos_fields = @typeInfo(Positionals).Struct.fields;
519 inline for (pos_fields) |arg_field, i| {
520 if (i != 0) {
521 try stream.writeAll(", ");
522 }
523 try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name), inst_table);
524 }
525
526 comptime var need_comma = pos_fields.len != 0;
527 const KW_Args = @TypeOf(inst.kw_args);
528 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| {
529 if (@typeInfo(arg_field.field_type) == .Optional) {
530 if (@field(inst.kw_args, arg_field.name)) |non_optional| {
531 if (need_comma) try stream.writeAll(", ");
532 try stream.print("{}=", .{arg_field.name});
533 try self.writeParamToStream(stream, non_optional, inst_table);
534 need_comma = true;
535 }
536 } else {
537 if (need_comma) try stream.writeAll(", ");
538 try stream.print("{}=", .{arg_field.name});
539 try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name), inst_table);
540 need_comma = true;
541 }
542 }
543
544 try stream.writeByte(')');
545 }
546
547 fn writeParamToStream(self: Module, stream: var, param: var, inst_table: *const InstPtrTable) !void {
548 if (@typeInfo(@TypeOf(param)) == .Enum) {
549 return stream.writeAll(@tagName(param));
550 }
551 switch (@TypeOf(param)) {
552 *Inst => return self.writeInstParamToStream(stream, param, inst_table),
553 []*Inst => {
554 try stream.writeByte('[');
555 for (param) |inst, i| {
556 if (i != 0) {
557 try stream.writeAll(", ");
558 }
559 try self.writeInstParamToStream(stream, inst, inst_table);
560 }
561 try stream.writeByte(']');
562 },
563 Module.Body => {
564 try stream.writeAll("{\n");
565 for (param.instructions) |inst, i| {
566 try stream.print(" %{} ", .{i});
567 try self.writeInstToStream(stream, inst, inst_table);
568 try stream.writeByte('\n');
569 }
570 try stream.writeByte('}');
571 },
572 bool => return stream.writeByte("01"[@boolToInt(param)]),
573 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
574 BigIntConst => return stream.print("{}", .{param}),
575 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
576 }
577 }
578
579 fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void {
580 const info = inst_table.getValue(inst).?;
581 const prefix = if (info.fn_body == null) "@" else "%";
582 try stream.print("{}{}", .{ prefix, info.index });
583 }
584};
585
586pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module {
587 var global_name_map = std.StringHashMap(usize).init(allocator);
588 defer global_name_map.deinit();
589
590 var parser: Parser = .{
591 .allocator = allocator,
592 .arena = std.heap.ArenaAllocator.init(allocator),
593 .i = 0,
594 .source = source,
595 .global_name_map = &global_name_map,
596 .decls = .{},
597 .unnamed_index = 0,
598 };
599 errdefer parser.arena.deinit();
600
601 parser.parseRoot() catch |err| switch (err) {
602 error.ParseFailure => {
603 assert(parser.error_msg != null);
604 },
605 else => |e| return e,
606 };
607
608 return Module{
609 .decls = parser.decls.toOwnedSlice(allocator),
610 .arena = parser.arena,
611 .error_msg = parser.error_msg,
612 };
613}
614
615const Parser = struct {
616 allocator: *Allocator,
617 arena: std.heap.ArenaAllocator,
618 i: usize,
619 source: [:0]const u8,
620 decls: std.ArrayListUnmanaged(*Inst),
621 global_name_map: *std.StringHashMap(usize),
622 error_msg: ?ErrorMsg = null,
623 unnamed_index: usize,
624
625 const Body = struct {
626 instructions: std.ArrayList(*Inst),
627 name_map: std.StringHashMap(usize),
628 };
629
630 fn parseBody(self: *Parser) !Module.Body {
631 var body_context = Body{
632 .instructions = std.ArrayList(*Inst).init(self.allocator),
633 .name_map = std.StringHashMap(usize).init(self.allocator),
634 };
635 defer body_context.instructions.deinit();
636 defer body_context.name_map.deinit();
637
638 try requireEatBytes(self, "{");
639 skipSpace(self);
640
641 while (true) : (self.i += 1) switch (self.source[self.i]) {
642 ';' => _ = try skipToAndOver(self, '\n'),
643 '%' => {
644 self.i += 1;
645 const ident = try skipToAndOver(self, ' ');
646 skipSpace(self);
647 try requireEatBytes(self, "=");
648 skipSpace(self);
649 const inst = try parseInstruction(self, &body_context, ident);
650 const ident_index = body_context.instructions.items.len;
651 if (try body_context.name_map.put(ident, ident_index)) |_| {
652 return self.fail("redefinition of identifier '{}'", .{ident});
653 }
654 try body_context.instructions.append(inst);
655 continue;
656 },
657 ' ', '\n' => continue,
658 '}' => {
659 self.i += 1;
660 break;
661 },
662 else => |byte| return self.failByte(byte),
663 };
664
665 // Move the instructions to the arena
666 const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len);
667 mem.copy(*Inst, instrs, body_context.instructions.items);
668 return Module.Body{ .instructions = instrs };
669 }
670
671 fn parseStringLiteral(self: *Parser) ![]u8 {
672 const start = self.i;
673 try self.requireEatBytes("\"");
674
675 while (true) : (self.i += 1) switch (self.source[self.i]) {
676 '"' => {
677 self.i += 1;
678 const span = self.source[start..self.i];
679 var bad_index: usize = undefined;
680 const parsed = std.zig.parseStringLiteral(&self.arena.allocator, span, &bad_index) catch |err| switch (err) {
681 error.InvalidCharacter => {
682 self.i = start + bad_index;
683 const bad_byte = self.source[self.i];
684 return self.fail("invalid string literal character: '{c}'\n", .{bad_byte});
685 },
686 else => |e| return e,
687 };
688 return parsed;
689 },
690 '\\' => {
691 self.i += 1;
692 continue;
693 },
694 0 => return self.failByte(0),
695 else => continue,
696 };
697 }
698
699 fn parseIntegerLiteral(self: *Parser) !BigIntConst {
700 const start = self.i;
701 if (self.source[self.i] == '-') self.i += 1;
702 while (true) : (self.i += 1) switch (self.source[self.i]) {
703 '0'...'9' => continue,
704 else => break,
705 };
706 const number_text = self.source[start..self.i];
707 const base = 10;
708 // TODO reuse the same array list for this
709 const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
710 const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len);
711 defer self.allocator.free(limbs_buffer);
712 const limb_len = std.math.big.int.calcSetStringLimbCount(base, number_text.len);
713 const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len);
714 var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
715 result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) {
716 error.InvalidCharacter => {
717 self.i = start;
718 return self.fail("invalid digit in integer literal", .{});
719 },
720 };
721 return result.toConst();
722 }
723
724 fn parseRoot(self: *Parser) !void {
725 // The IR format is designed so that it can be tokenized and parsed at the same time.
726 while (true) {
727 switch (self.source[self.i]) {
728 ';' => _ = try skipToAndOver(self, '\n'),
729 '@' => {
730 self.i += 1;
731 const ident = try skipToAndOver(self, ' ');
732 skipSpace(self);
733 try requireEatBytes(self, "=");
734 skipSpace(self);
735 const inst = try parseInstruction(self, null, ident);
736 const ident_index = self.decls.items.len;
737 if (try self.global_name_map.put(ident, ident_index)) |_| {
738 return self.fail("redefinition of identifier '{}'", .{ident});
739 }
740 try self.decls.append(self.allocator, inst);
741 },
742 ' ', '\n' => self.i += 1,
743 0 => break,
744 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
745 }
746 }
747 }
748
749 fn eatByte(self: *Parser, byte: u8) bool {
750 if (self.source[self.i] != byte) return false;
751 self.i += 1;
752 return true;
753 }
754
755 fn skipSpace(self: *Parser) void {
756 while (self.source[self.i] == ' ' or self.source[self.i] == '\n') {
757 self.i += 1;
758 }
759 }
760
761 fn requireEatBytes(self: *Parser, bytes: []const u8) !void {
762 const start = self.i;
763 for (bytes) |byte| {
764 if (self.source[self.i] != byte) {
765 self.i = start;
766 return self.fail("expected '{}'", .{bytes});
767 }
768 self.i += 1;
769 }
770 }
771
772 fn skipToAndOver(self: *Parser, byte: u8) ![]const u8 {
773 const start_i = self.i;
774 while (self.source[self.i] != 0) : (self.i += 1) {
775 if (self.source[self.i] == byte) {
776 const result = self.source[start_i..self.i];
777 self.i += 1;
778 return result;
779 }
780 }
781 return self.fail("unexpected EOF", .{});
782 }
783
784 /// ParseFailure is an internal error code; handled in `parse`.
785 const InnerError = error{ ParseFailure, OutOfMemory };
786
787 fn failByte(self: *Parser, byte: u8) InnerError {
788 if (byte == 0) {
789 return self.fail("unexpected EOF", .{});
790 } else {
791 return self.fail("unexpected byte: '{c}'", .{byte});
792 }
793 }
794
795 fn fail(self: *Parser, comptime format: []const u8, args: var) InnerError {
796 @setCold(true);
797 self.error_msg = ErrorMsg{
798 .byte_offset = self.i,
799 .msg = try std.fmt.allocPrint(&self.arena.allocator, format, args),
800 };
801 return error.ParseFailure;
802 }
803
804 fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Inst {
805 const contents_start = self.i;
806 const fn_name = try skipToAndOver(self, '(');
807 inline for (@typeInfo(Inst.Tag).Enum.fields) |field| {
808 if (mem.eql(u8, field.name, fn_name)) {
809 const tag = @field(Inst.Tag, field.name);
810 return parseInstructionGeneric(self, field.name, Inst.TagToType(tag), body_ctx, name, contents_start);
811 }
812 }
813 return self.fail("unknown instruction '{}'", .{fn_name});
814 }
815
816 fn parseInstructionGeneric(
817 self: *Parser,
818 comptime fn_name: []const u8,
819 comptime InstType: type,
820 body_ctx: ?*Body,
821 inst_name: []const u8,
822 contents_start: usize,
823 ) InnerError!*Inst {
824 const inst_specific = try self.arena.allocator.create(InstType);
825 inst_specific.base = .{
826 .name = inst_name,
827 .src = self.i,
828 .tag = InstType.base_tag,
829 };
830
831 if (@hasField(InstType, "ty")) {
832 inst_specific.ty = opt_type orelse {
833 return self.fail("instruction '" ++ fn_name ++ "' requires type", .{});
834 };
835 }
836
837 const Positionals = @TypeOf(inst_specific.positionals);
838 inline for (@typeInfo(Positionals).Struct.fields) |arg_field| {
839 if (self.source[self.i] == ',') {
840 self.i += 1;
841 skipSpace(self);
842 } else if (self.source[self.i] == ')') {
843 return self.fail("expected positional parameter '{}'", .{arg_field.name});
844 }
845 @field(inst_specific.positionals, arg_field.name) = try parseParameterGeneric(
846 self,
847 arg_field.field_type,
848 body_ctx,
849 );
850 skipSpace(self);
851 }
852
853 const KW_Args = @TypeOf(inst_specific.kw_args);
854 inst_specific.kw_args = .{}; // assign defaults
855 skipSpace(self);
856 while (eatByte(self, ',')) {
857 skipSpace(self);
858 const name = try skipToAndOver(self, '=');
859 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| {
860 const field_name = arg_field.name;
861 if (mem.eql(u8, name, field_name)) {
862 const NonOptional = switch (@typeInfo(arg_field.field_type)) {
863 .Optional => |info| info.child,
864 else => arg_field.field_type,
865 };
866 @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(self, NonOptional, body_ctx);
867 break;
868 }
869 } else {
870 return self.fail("unrecognized keyword parameter: '{}'", .{name});
871 }
872 skipSpace(self);
873 }
874 try requireEatBytes(self, ")");
875
876 inst_specific.base.contents = self.source[contents_start..self.i];
877
878 return &inst_specific.base;
879 }
880
881 fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T {
882 if (@typeInfo(T) == .Enum) {
883 const start = self.i;
884 while (true) : (self.i += 1) switch (self.source[self.i]) {
885 ' ', '\n', ',', ')' => {
886 const enum_name = self.source[start..self.i];
887 return std.meta.stringToEnum(T, enum_name) orelse {
888 return self.fail("tag '{}' not a member of enum '{}'", .{ enum_name, @typeName(T) });
889 };
890 },
891 0 => return self.failByte(0),
892 else => continue,
893 };
894 }
895 switch (T) {
896 Module.Body => return parseBody(self),
897 bool => {
898 const bool_value = switch (self.source[self.i]) {
899 '0' => false,
900 '1' => true,
901 else => |byte| return self.fail("expected '0' or '1' for boolean value, found {c}", .{byte}),
902 };
903 self.i += 1;
904 return bool_value;
905 },
906 []*Inst => {
907 try requireEatBytes(self, "[");
908 skipSpace(self);
909 if (eatByte(self, ']')) return &[0]*Inst{};
910
911 var instructions = std.ArrayList(*Inst).init(&self.arena.allocator);
912 while (true) {
913 skipSpace(self);
914 try instructions.append(try parseParameterInst(self, body_ctx));
915 skipSpace(self);
916 if (!eatByte(self, ',')) break;
917 }
918 try requireEatBytes(self, "]");
919 return instructions.toOwnedSlice();
920 },
921 *Inst => return parseParameterInst(self, body_ctx),
922 []u8, []const u8 => return self.parseStringLiteral(),
923 BigIntConst => return self.parseIntegerLiteral(),
924 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
925 }
926 return self.fail("TODO parse parameter {}", .{@typeName(T)});
927 }
928
929 fn parseParameterInst(self: *Parser, body_ctx: ?*Body) !*Inst {
930 const local_ref = switch (self.source[self.i]) {
931 '@' => false,
932 '%' => true,
933 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
934 };
935 const map = if (local_ref)
936 if (body_ctx) |bc|
937 &bc.name_map
938 else
939 return self.fail("referencing a % instruction in global scope", .{})
940 else
941 self.global_name_map;
942
943 self.i += 1;
944 const name_start = self.i;
945 while (true) : (self.i += 1) switch (self.source[self.i]) {
946 0, ' ', '\n', ',', ')', ']' => break,
947 else => continue,
948 };
949 const ident = self.source[name_start..self.i];
950 const kv = map.get(ident) orelse {
951 const bad_name = self.source[name_start - 1 .. self.i];
952 const src = name_start - 1;
953 if (local_ref) {
954 self.i = src;
955 return self.fail("unrecognized identifier: {}", .{bad_name});
956 } else {
957 const name = try self.arena.allocator.create(Inst.Str);
958 name.* = .{
959 .base = .{
960 .name = try self.generateName(),
961 .src = src,
962 .tag = Inst.Str.base_tag,
963 },
964 .positionals = .{ .bytes = ident },
965 .kw_args = .{},
966 };
967 const declref = try self.arena.allocator.create(Inst.DeclRef);
968 declref.* = .{
969 .base = .{
970 .name = try self.generateName(),
971 .src = src,
972 .tag = Inst.DeclRef.base_tag,
973 },
974 .positionals = .{ .name = &name.base },
975 .kw_args = .{},
976 };
977 return &declref.base;
978 }
979 };
980 if (local_ref) {
981 return body_ctx.?.instructions.items[kv.value];
982 } else {
983 return self.decls.items[kv.value];
984 }
985 }
986
987 fn generateName(self: *Parser) ![]u8 {
988 const result = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.unnamed_index});
989 self.unnamed_index += 1;
990 return result;
991 }
992};
993
994pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {
995 var ctx: EmitZIR = .{
996 .allocator = allocator,
997 .decls = .{},
998 .decl_table = std.AutoHashMap(*ir.Inst, *Inst).init(allocator),
999 .arena = std.heap.ArenaAllocator.init(allocator),
1000 .old_module = &old_module,
1001 };
1002 defer ctx.decls.deinit(allocator);
1003 defer ctx.decl_table.deinit();
1004 errdefer ctx.arena.deinit();
1005
1006 try ctx.emit();
1007
1008 return Module{
1009 .decls = ctx.decls.toOwnedSlice(allocator),
1010 .arena = ctx.arena,
1011 };
1012}
1013
1014const EmitZIR = struct {
1015 allocator: *Allocator,
1016 arena: std.heap.ArenaAllocator,
1017 old_module: *const IrModule,
1018 decls: std.ArrayListUnmanaged(*Inst),
1019 decl_table: std.AutoHashMap(*ir.Inst, *Inst),
1020
1021 fn emit(self: *EmitZIR) !void {
1022 var it = self.old_module.decl_exports.iterator();
1023 while (it.next()) |kv| {
1024 const decl = kv.key;
1025 const exports = kv.value;
1026 const export_value = try self.emitTypedValue(decl.src, decl.typed_value.most_recent.typed_value);
1027 for (exports) |module_export| {
1028 const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
1029 const export_inst = try self.arena.allocator.create(Inst.Export);
1030 export_inst.* = .{
1031 .base = .{
1032 .name = try self.autoName(),
1033 .src = module_export.src,
1034 .tag = Inst.Export.base_tag,
1035 },
1036 .positionals = .{
1037 .symbol_name = symbol_name,
1038 .value = export_value,
1039 },
1040 .kw_args = .{},
1041 };
1042 try self.decls.append(self.allocator, &export_inst.base);
1043 }
1044 }
1045 }
1046
1047 fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
1048 if (inst.cast(ir.Inst.Constant)) |const_inst| {
1049 if (self.decl_table.getValue(inst)) |decl| {
1050 return decl;
1051 }
1052 const new_decl = try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
1053 try self.decl_table.putNoClobber(inst, new_decl);
1054 return new_decl;
1055 } else {
1056 return inst_table.getValue(inst).?;
1057 }
1058 }
1059
1060 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
1061 const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
1062 const int_inst = try self.arena.allocator.create(Inst.Int);
1063 int_inst.* = .{
1064 .base = .{
1065 .name = try self.autoName(),
1066 .src = src,
1067 .tag = Inst.Int.base_tag,
1068 },
1069 .positionals = .{
1070 .int = val.toBigInt(big_int_space),
1071 },
1072 .kw_args = .{},
1073 };
1074 try self.decls.append(self.allocator, &int_inst.base);
1075 return &int_inst.base;
1076 }
1077
1078 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Inst {
1079 const allocator = &self.arena.allocator;
1080 switch (typed_value.ty.zigTypeTag()) {
1081 .Pointer => {
1082 const ptr_elem_type = typed_value.ty.elemType();
1083 switch (ptr_elem_type.zigTypeTag()) {
1084 .Array => {
1085 // TODO more checks to make sure this can be emitted as a string literal
1086 //const array_elem_type = ptr_elem_type.elemType();
1087 //if (array_elem_type.eql(Type.initTag(.u8)) and
1088 // ptr_elem_type.hasSentinel(Value.initTag(.zero)))
1089 //{
1090 //}
1091 const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) {
1092 error.AnalysisFail => unreachable,
1093 else => |e| return e,
1094 };
1095 return self.emitStringLiteral(src, bytes);
1096 },
1097 else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}),
1098 }
1099 },
1100 .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val),
1101 .Int => {
1102 const as_inst = try self.arena.allocator.create(Inst.As);
1103 as_inst.* = .{
1104 .base = .{
1105 .name = try self.autoName(),
1106 .src = src,
1107 .tag = Inst.As.base_tag,
1108 },
1109 .positionals = .{
1110 .dest_type = try self.emitType(src, typed_value.ty),
1111 .value = try self.emitComptimeIntVal(src, typed_value.val),
1112 },
1113 .kw_args = .{},
1114 };
1115 try self.decls.append(self.allocator, &as_inst.base);
1116
1117 return &as_inst.base;
1118 },
1119 .Type => {
1120 const ty = typed_value.val.toType();
1121 return self.emitType(src, ty);
1122 },
1123 .Fn => {
1124 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
1125
1126 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
1127 defer inst_table.deinit();
1128
1129 var instructions = std.ArrayList(*Inst).init(self.allocator);
1130 defer instructions.deinit();
1131
1132 try self.emitBody(module_fn.analysis.success, &inst_table, &instructions);
1133
1134 const fn_type = try self.emitType(src, module_fn.fn_type);
1135
1136 const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
1137 mem.copy(*Inst, arena_instrs, instructions.items);
1138
1139 const fn_inst = try self.arena.allocator.create(Inst.Fn);
1140 fn_inst.* = .{
1141 .base = .{
1142 .name = try self.autoName(),
1143 .src = src,
1144 .tag = Inst.Fn.base_tag,
1145 },
1146 .positionals = .{
1147 .fn_type = fn_type,
1148 .body = .{ .instructions = arena_instrs },
1149 },
1150 .kw_args = .{},
1151 };
1152 try self.decls.append(self.allocator, &fn_inst.base);
1153 return &fn_inst.base;
1154 },
1155 else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),
1156 }
1157 }
1158
1159 fn emitTrivial(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {
1160 const new_inst = try self.arena.allocator.create(T);
1161 new_inst.* = .{
1162 .base = .{
1163 .name = try self.autoName(),
1164 .src = src,
1165 .tag = T.base_tag,
1166 },
1167 .positionals = .{},
1168 .kw_args = .{},
1169 };
1170 return &new_inst.base;
1171 }
1172
1173 fn emitBody(
1174 self: *EmitZIR,
1175 body: IrModule.Body,
1176 inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
1177 instructions: *std.ArrayList(*Inst),
1178 ) Allocator.Error!void {
1179 for (body.instructions) |inst| {
1180 const new_inst = switch (inst.tag) {
1181 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
1182 .call => blk: {
1183 const old_inst = inst.cast(ir.Inst.Call).?;
1184 const new_inst = try self.arena.allocator.create(Inst.Call);
1185
1186 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1187 for (args) |*elem, i| {
1188 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
1189 }
1190 new_inst.* = .{
1191 .base = .{
1192 .name = try self.autoName(),
1193 .src = inst.src,
1194 .tag = Inst.Call.base_tag,
1195 },
1196 .positionals = .{
1197 .func = try self.resolveInst(inst_table, old_inst.args.func),
1198 .args = args,
1199 },
1200 .kw_args = .{},
1201 };
1202 break :blk &new_inst.base;
1203 },
1204 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
1205 .ret => try self.emitTrivial(inst.src, Inst.Return),
1206 .constant => unreachable, // excluded from function bodies
1207 .assembly => blk: {
1208 const old_inst = inst.cast(ir.Inst.Assembly).?;
1209 const new_inst = try self.arena.allocator.create(Inst.Asm);
1210
1211 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
1212 for (inputs) |*elem, i| {
1213 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
1214 }
1215
1216 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
1217 for (clobbers) |*elem, i| {
1218 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
1219 }
1220
1221 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1222 for (args) |*elem, i| {
1223 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
1224 }
1225
1226 new_inst.* = .{
1227 .base = .{
1228 .name = try self.autoName(),
1229 .src = inst.src,
1230 .tag = Inst.Asm.base_tag,
1231 },
1232 .positionals = .{
1233 .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
1234 .return_type = try self.emitType(inst.src, inst.ty),
1235 },
1236 .kw_args = .{
1237 .@"volatile" = old_inst.args.is_volatile,
1238 .output = if (old_inst.args.output) |o|
1239 try self.emitStringLiteral(inst.src, o)
1240 else
1241 null,
1242 .inputs = inputs,
1243 .clobbers = clobbers,
1244 .args = args,
1245 },
1246 };
1247 break :blk &new_inst.base;
1248 },
1249 .ptrtoint => blk: {
1250 const old_inst = inst.cast(ir.Inst.PtrToInt).?;
1251 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
1252 new_inst.* = .{
1253 .base = .{
1254 .name = try self.autoName(),
1255 .src = inst.src,
1256 .tag = Inst.PtrToInt.base_tag,
1257 },
1258 .positionals = .{
1259 .ptr = try self.resolveInst(inst_table, old_inst.args.ptr),
1260 },
1261 .kw_args = .{},
1262 };
1263 break :blk &new_inst.base;
1264 },
1265 .bitcast => blk: {
1266 const old_inst = inst.cast(ir.Inst.BitCast).?;
1267 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1268 new_inst.* = .{
1269 .base = .{
1270 .name = try self.autoName(),
1271 .src = inst.src,
1272 .tag = Inst.BitCast.base_tag,
1273 },
1274 .positionals = .{
1275 .dest_type = try self.emitType(inst.src, inst.ty),
1276 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1277 },
1278 .kw_args = .{},
1279 };
1280 break :blk &new_inst.base;
1281 },
1282 .cmp => blk: {
1283 const old_inst = inst.cast(ir.Inst.Cmp).?;
1284 const new_inst = try self.arena.allocator.create(Inst.Cmp);
1285 new_inst.* = .{
1286 .base = .{
1287 .name = try self.autoName(),
1288 .src = inst.src,
1289 .tag = Inst.Cmp.base_tag,
1290 },
1291 .positionals = .{
1292 .lhs = try self.resolveInst(inst_table, old_inst.args.lhs),
1293 .rhs = try self.resolveInst(inst_table, old_inst.args.rhs),
1294 .op = old_inst.args.op,
1295 },
1296 .kw_args = .{},
1297 };
1298 break :blk &new_inst.base;
1299 },
1300 .condbr => blk: {
1301 const old_inst = inst.cast(ir.Inst.CondBr).?;
1302
1303 var true_body = std.ArrayList(*Inst).init(self.allocator);
1304 var false_body = std.ArrayList(*Inst).init(self.allocator);
1305
1306 defer true_body.deinit();
1307 defer false_body.deinit();
1308
1309 try self.emitBody(old_inst.args.true_body, inst_table, &true_body);
1310 try self.emitBody(old_inst.args.false_body, inst_table, &false_body);
1311
1312 const new_inst = try self.arena.allocator.create(Inst.CondBr);
1313 new_inst.* = .{
1314 .base = .{
1315 .name = try self.autoName(),
1316 .src = inst.src,
1317 .tag = Inst.CondBr.base_tag,
1318 },
1319 .positionals = .{
1320 .condition = try self.resolveInst(inst_table, old_inst.args.condition),
1321 .true_body = .{ .instructions = true_body.toOwnedSlice() },
1322 .false_body = .{ .instructions = false_body.toOwnedSlice() },
1323 },
1324 .kw_args = .{},
1325 };
1326 break :blk &new_inst.base;
1327 },
1328 .isnull => blk: {
1329 const old_inst = inst.cast(ir.Inst.IsNull).?;
1330 const new_inst = try self.arena.allocator.create(Inst.IsNull);
1331 new_inst.* = .{
1332 .base = .{
1333 .name = try self.autoName(),
1334 .src = inst.src,
1335 .tag = Inst.IsNull.base_tag,
1336 },
1337 .positionals = .{
1338 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1339 },
1340 .kw_args = .{},
1341 };
1342 break :blk &new_inst.base;
1343 },
1344 .isnonnull => blk: {
1345 const old_inst = inst.cast(ir.Inst.IsNonNull).?;
1346 const new_inst = try self.arena.allocator.create(Inst.IsNonNull);
1347 new_inst.* = .{
1348 .base = .{
1349 .name = try self.autoName(),
1350 .src = inst.src,
1351 .tag = Inst.IsNonNull.base_tag,
1352 },
1353 .positionals = .{
1354 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1355 },
1356 .kw_args = .{},
1357 };
1358 break :blk &new_inst.base;
1359 },
1360 };
1361 try instructions.append(new_inst);
1362 try inst_table.putNoClobber(inst, new_inst);
1363 }
1364 }
1365
1366 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
1367 switch (ty.tag()) {
1368 .isize => return self.emitPrimitiveType(src, .isize),
1369 .usize => return self.emitPrimitiveType(src, .usize),
1370 .c_short => return self.emitPrimitiveType(src, .c_short),
1371 .c_ushort => return self.emitPrimitiveType(src, .c_ushort),
1372 .c_int => return self.emitPrimitiveType(src, .c_int),
1373 .c_uint => return self.emitPrimitiveType(src, .c_uint),
1374 .c_long => return self.emitPrimitiveType(src, .c_long),
1375 .c_ulong => return self.emitPrimitiveType(src, .c_ulong),
1376 .c_longlong => return self.emitPrimitiveType(src, .c_longlong),
1377 .c_ulonglong => return self.emitPrimitiveType(src, .c_ulonglong),
1378 .c_longdouble => return self.emitPrimitiveType(src, .c_longdouble),
1379 .c_void => return self.emitPrimitiveType(src, .c_void),
1380 .f16 => return self.emitPrimitiveType(src, .f16),
1381 .f32 => return self.emitPrimitiveType(src, .f32),
1382 .f64 => return self.emitPrimitiveType(src, .f64),
1383 .f128 => return self.emitPrimitiveType(src, .f128),
1384 .anyerror => return self.emitPrimitiveType(src, .anyerror),
1385 else => switch (ty.zigTypeTag()) {
1386 .Bool => return self.emitPrimitiveType(src, .bool),
1387 .Void => return self.emitPrimitiveType(src, .void),
1388 .NoReturn => return self.emitPrimitiveType(src, .noreturn),
1389 .Type => return self.emitPrimitiveType(src, .type),
1390 .ComptimeInt => return self.emitPrimitiveType(src, .comptime_int),
1391 .ComptimeFloat => return self.emitPrimitiveType(src, .comptime_float),
1392 .Fn => {
1393 const param_types = try self.allocator.alloc(Type, ty.fnParamLen());
1394 defer self.allocator.free(param_types);
1395
1396 ty.fnParamTypes(param_types);
1397 const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len);
1398 for (param_types) |param_type, i| {
1399 emitted_params[i] = try self.emitType(src, param_type);
1400 }
1401
1402 const fntype_inst = try self.arena.allocator.create(Inst.FnType);
1403 fntype_inst.* = .{
1404 .base = .{
1405 .name = try self.autoName(),
1406 .src = src,
1407 .tag = Inst.FnType.base_tag,
1408 },
1409 .positionals = .{
1410 .param_types = emitted_params,
1411 .return_type = try self.emitType(src, ty.fnReturnType()),
1412 },
1413 .kw_args = .{
1414 .cc = ty.fnCallingConvention(),
1415 },
1416 };
1417 try self.decls.append(self.allocator, &fntype_inst.base);
1418 return &fntype_inst.base;
1419 },
1420 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
1421 },
1422 }
1423 }
1424
1425 fn autoName(self: *EmitZIR) ![]u8 {
1426 return std.fmt.allocPrint(&self.arena.allocator, "{}", .{self.decls.items.len});
1427 }
1428
1429 fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst {
1430 const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
1431 primitive_inst.* = .{
1432 .base = .{
1433 .name = try self.autoName(),
1434 .src = src,
1435 .tag = Inst.Primitive.base_tag,
1436 },
1437 .positionals = .{
1438 .tag = tag,
1439 },
1440 .kw_args = .{},
1441 };
1442 try self.decls.append(self.allocator, &primitive_inst.base);
1443 return &primitive_inst.base;
1444 }
1445
1446 fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {
1447 const str_inst = try self.arena.allocator.create(Inst.Str);
1448 str_inst.* = .{
1449 .base = .{
1450 .name = try self.autoName(),
1451 .src = src,
1452 .tag = Inst.Str.base_tag,
1453 },
1454 .positionals = .{
1455 .bytes = str,
1456 },
1457 .kw_args = .{},
1458 };
1459 try self.decls.append(self.allocator, &str_inst.base);
1460
1461 const ref_inst = try self.arena.allocator.create(Inst.Ref);
1462 ref_inst.* = .{
1463 .base = .{
1464 .name = try self.autoName(),
1465 .src = src,
1466 .tag = Inst.Ref.base_tag,
1467 },
1468 .positionals = .{
1469 .operand = &str_inst.base,
1470 },
1471 .kw_args = .{},
1472 };
1473 try self.decls.append(self.allocator, &ref_inst.base);
1474
1475 return &ref_inst.base;
1476 }
1477};
src/codegen.cpp+10
......@@ -1794,6 +1794,16 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstGen *instruction) {
17941794}
17951795
17961796void codegen_report_errors_and_exit(CodeGen *g) {
1797 // Clear progress indicator before printing errors
1798 if (g->sub_progress_node != nullptr) {
1799 stage2_progress_end(g->sub_progress_node);
1800 g->sub_progress_node = nullptr;
1801 }
1802 if (g->main_progress_node != nullptr) {
1803 stage2_progress_end(g->main_progress_node);
1804 g->main_progress_node = nullptr;
1805 }
1806
17971807 assert(g->errors.length != 0);
17981808 for (size_t i = 0; i < g->errors.length; i += 1) {
17991809 ErrorMsg *err = g->errors.at(i);
test/stage2/zir.zig+163-58
......@@ -1,7 +1,15 @@
1const std = @import("std");
12const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
3// self-hosted does not yet support PE executable files / COFF object files
4// or mach-o files. So we do the ZIR transform test cases cross compiling for
5// x86_64-linux.
6const linux_x64 = std.zig.CrossTarget{
7 .cpu_arch = .x86_64,
8 .os_tag = .linux,
9};
210
311pub fn addCases(ctx: *TestContext) void {
4 ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint",
12 ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,
513 \\@void = primitive(void)
614 \\@usize = primitive(usize)
715 \\@fnty = fntype([], @void, cc=C)
......@@ -12,10 +20,11 @@ pub fn addCases(ctx: *TestContext) void {
1220 \\
1321 \\@entry = fn(@fnty, {
1422 \\ %a = str("\x32\x08\x01\x0a")
15 \\ %eptr0 = elemptr(%a, @0)
16 \\ %eptr1 = elemptr(%a, @1)
17 \\ %eptr2 = elemptr(%a, @2)
18 \\ %eptr3 = elemptr(%a, @3)
23 \\ %aref = ref(%a)
24 \\ %eptr0 = elemptr(%aref, @0)
25 \\ %eptr1 = elemptr(%aref, @1)
26 \\ %eptr2 = elemptr(%aref, @2)
27 \\ %eptr3 = elemptr(%aref, @3)
1928 \\ %v0 = deref(%eptr0)
2029 \\ %v1 = deref(%eptr1)
2130 \\ %v2 = deref(%eptr2)
......@@ -34,7 +43,8 @@ pub fn addCases(ctx: *TestContext) void {
3443 \\})
3544 \\
3645 \\@9 = str("entry")
37 \\@10 = export(@9, @entry)
46 \\@10 = ref(@9)
47 \\@11 = export(@10, @entry)
3848 ,
3949 \\@0 = primitive(void)
4050 \\@1 = fntype([], @0, cc=C)
......@@ -42,66 +52,161 @@ pub fn addCases(ctx: *TestContext) void {
4252 \\ %0 = return()
4353 \\})
4454 \\@3 = str("entry")
45 \\@4 = export(@3, @2)
55 \\@4 = ref(@3)
56 \\@5 = export(@4, @2)
4657 \\
4758 );
4859
49 if (@import("std").Target.current.os.tag != .linux or
50 @import("std").Target.current.cpu.arch != .x86_64)
60 if (std.Target.current.os.tag != .linux or
61 std.Target.current.cpu.arch != .x86_64)
5162 {
5263 // TODO implement self-hosted PE (.exe file) linking
5364 // TODO implement more ZIR so we don't depend on x86_64-linux
5465 return;
5566 }
5667
57 ctx.addZIRCompareOutput("hello world ZIR",
58 \\@0 = str("Hello, world!\n")
59 \\@1 = primitive(noreturn)
60 \\@2 = primitive(usize)
61 \\@3 = fntype([], @1, cc=Naked)
62 \\@4 = int(0)
63 \\@5 = int(1)
64 \\@6 = int(231)
65 \\@7 = str("len")
66 \\
67 \\@8 = fn(@3, {
68 \\ %0 = as(@2, @5) ; SYS_write
69 \\ %1 = as(@2, @5) ; STDOUT_FILENO
70 \\ %2 = ptrtoint(@0) ; msg ptr
71 \\ %3 = fieldptr(@0, @7) ; msg len ptr
72 \\ %4 = deref(%3) ; msg len
73 \\ %sysoutreg = str("={rax}")
74 \\ %rax = str("{rax}")
75 \\ %rdi = str("{rdi}")
76 \\ %rsi = str("{rsi}")
77 \\ %rdx = str("{rdx}")
78 \\ %rcx = str("rcx")
79 \\ %r11 = str("r11")
80 \\ %memory = str("memory")
81 \\ %syscall = str("syscall")
82 \\ %5 = asm(%syscall, @2,
83 \\ volatile=1,
84 \\ output=%sysoutreg,
85 \\ inputs=[%rax, %rdi, %rsi, %rdx],
86 \\ clobbers=[%rcx, %r11, %memory],
87 \\ args=[%0, %1, %2, %4])
88 \\
89 \\ %6 = as(@2, @6) ;SYS_exit_group
90 \\ %7 = as(@2, @4) ;exit code
91 \\ %8 = asm(%syscall, @2,
92 \\ volatile=1,
93 \\ output=%sysoutreg,
94 \\ inputs=[%rax, %rdi],
95 \\ clobbers=[%rcx, %r11, %memory],
96 \\ args=[%6, %7])
97 \\
98 \\ %9 = unreachable()
99 \\})
100 \\
101 \\@9 = str("_start")
102 \\@10 = export(@9, @8)
103 ,
104 \\Hello, world!
105 \\
68 ctx.addZIRCompareOutput(
69 "hello world ZIR, update msg",
70 &[_][]const u8{
71 \\@noreturn = primitive(noreturn)
72 \\@void = primitive(void)
73 \\@usize = primitive(usize)
74 \\@0 = int(0)
75 \\@1 = int(1)
76 \\@2 = int(2)
77 \\@3 = int(3)
78 \\
79 \\@syscall_array = str("syscall")
80 \\@sysoutreg_array = str("={rax}")
81 \\@rax_array = str("{rax}")
82 \\@rdi_array = str("{rdi}")
83 \\@rcx_array = str("rcx")
84 \\@r11_array = str("r11")
85 \\@rdx_array = str("{rdx}")
86 \\@rsi_array = str("{rsi}")
87 \\@memory_array = str("memory")
88 \\@len_array = str("len")
89 \\
90 \\@msg = str("Hello, world!\n")
91 \\
92 \\@start_fnty = fntype([], @noreturn, cc=Naked)
93 \\@start = fn(@start_fnty, {
94 \\ %SYS_exit_group = int(231)
95 \\ %exit_code = as(@usize, @0)
96 \\
97 \\ %syscall = ref(@syscall_array)
98 \\ %sysoutreg = ref(@sysoutreg_array)
99 \\ %rax = ref(@rax_array)
100 \\ %rdi = ref(@rdi_array)
101 \\ %rcx = ref(@rcx_array)
102 \\ %rdx = ref(@rdx_array)
103 \\ %rsi = ref(@rsi_array)
104 \\ %r11 = ref(@r11_array)
105 \\ %memory = ref(@memory_array)
106 \\
107 \\ %SYS_write = as(@usize, @1)
108 \\ %STDOUT_FILENO = as(@usize, @1)
109 \\
110 \\ %msg_ptr = ref(@msg)
111 \\ %msg_addr = ptrtoint(%msg_ptr)
112 \\
113 \\ %len_name = ref(@len_array)
114 \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name)
115 \\ %msg_len = deref(%msg_len_ptr)
116 \\ %rc_write = asm(%syscall, @usize,
117 \\ volatile=1,
118 \\ output=%sysoutreg,
119 \\ inputs=[%rax, %rdi, %rsi, %rdx],
120 \\ clobbers=[%rcx, %r11, %memory],
121 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
122 \\
123 \\ %rc_exit = asm(%syscall, @usize,
124 \\ volatile=1,
125 \\ output=%sysoutreg,
126 \\ inputs=[%rax, %rdi],
127 \\ clobbers=[%rcx, %r11, %memory],
128 \\ args=[%SYS_exit_group, %exit_code])
129 \\
130 \\ %99 = unreachable()
131 \\});
132 \\
133 \\@9 = str("_start")
134 \\@10 = ref(@9)
135 \\@11 = export(@10, @start)
136 ,
137 \\@noreturn = primitive(noreturn)
138 \\@void = primitive(void)
139 \\@usize = primitive(usize)
140 \\@0 = int(0)
141 \\@1 = int(1)
142 \\@2 = int(2)
143 \\@3 = int(3)
144 \\
145 \\@syscall_array = str("syscall")
146 \\@sysoutreg_array = str("={rax}")
147 \\@rax_array = str("{rax}")
148 \\@rdi_array = str("{rdi}")
149 \\@rcx_array = str("rcx")
150 \\@r11_array = str("r11")
151 \\@rdx_array = str("{rdx}")
152 \\@rsi_array = str("{rsi}")
153 \\@memory_array = str("memory")
154 \\@len_array = str("len")
155 \\
156 \\@msg = str("Hello, world!\n")
157 \\@msg2 = str("HELL WORLD\n")
158 \\
159 \\@start_fnty = fntype([], @noreturn, cc=Naked)
160 \\@start = fn(@start_fnty, {
161 \\ %SYS_exit_group = int(231)
162 \\ %exit_code = as(@usize, @0)
163 \\
164 \\ %syscall = ref(@syscall_array)
165 \\ %sysoutreg = ref(@sysoutreg_array)
166 \\ %rax = ref(@rax_array)
167 \\ %rdi = ref(@rdi_array)
168 \\ %rcx = ref(@rcx_array)
169 \\ %rdx = ref(@rdx_array)
170 \\ %rsi = ref(@rsi_array)
171 \\ %r11 = ref(@r11_array)
172 \\ %memory = ref(@memory_array)
173 \\
174 \\ %SYS_write = as(@usize, @1)
175 \\ %STDOUT_FILENO = as(@usize, @1)
176 \\
177 \\ %msg_ptr = ref(@msg2)
178 \\ %msg_addr = ptrtoint(%msg_ptr)
179 \\
180 \\ %len_name = ref(@len_array)
181 \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name)
182 \\ %msg_len = deref(%msg_len_ptr)
183 \\ %rc_write = asm(%syscall, @usize,
184 \\ volatile=1,
185 \\ output=%sysoutreg,
186 \\ inputs=[%rax, %rdi, %rsi, %rdx],
187 \\ clobbers=[%rcx, %r11, %memory],
188 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
189 \\
190 \\ %rc_exit = asm(%syscall, @usize,
191 \\ volatile=1,
192 \\ output=%sysoutreg,
193 \\ inputs=[%rax, %rdi],
194 \\ clobbers=[%rcx, %r11, %memory],
195 \\ args=[%SYS_exit_group, %exit_code])
196 \\
197 \\ %99 = unreachable()
198 \\});
199 \\
200 \\@9 = str("_start")
201 \\@10 = ref(@9)
202 \\@11 = export(@10, @start)
203 },
204 &[_][]const u8{
205 \\Hello, world!
206 \\
207 ,
208 \\HELL WORLD
209 \\
210 },
106211 );
107212}