authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-10 02:05:54-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-10 02:05:54-04:00
loga32d3a85d21d614e5960b9eadcd85374954b910f
tree8712bfc619205eaf23201215a4a19f7c0c108cfa
parentae080b5c217fbcfd350a5d52b8b4626a95540ab3

rework self-hosted compiler for incremental builds

* introduce std.ArrayListUnmanaged for when you have the allocator stored elsewhere * move std.heap.ArenaAllocator implementation to its own file. extract the main state into std.heap.ArenaAllocator.State, which can be stored as an alternative to storing the entire ArenaAllocator, saving 24 bytes per ArenaAllocator on 64 bit targets. * std.LinkedList.Node pointer field now defaults to being null initialized. * Rework self-hosted compiler Package API * Delete almost all the bitrotted self-hosted compiler code. The only bit rotted code left is in main.zig and compilation.zig * Add call instruction to ZIR * self-hosted compiler ir API and link API are reworked to support a long-running compiler that incrementally updates declarations * Introduce the concept of scopes to ZIR semantic analysis * ZIR text format supports referencing named decls that are declared later in the file * Figure out how memory management works for the long-running compiler and incremental compilation. The main roots are top level declarations. There is a table of decls. The key is a cryptographic hash of the fully qualified decl name. Each decl has an arena allocator where all of the memory related to that decl is stored. Each code block has its own arena allocator for the lifetime of the block. Values that want to survive when going out of scope in a block must get copied into the outer block. Finally, values must get copied into the Decl arena to be long-lived. * Delete the unused MemoryCell struct. Instead, comptime pointers are based on references to Decl structs. * Figure out how caching works. Each Decl will store a set of other Decls which must be recompiled when it changes. This branch is still work-in-progress; this commit breaks the build.

21 files changed, 1836 insertions(+), 1504 deletions(-)

lib/std/array_list.zig+236-7
......@@ -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);
......@@ -259,6 +262,232 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
259262 };
260263}
261264
265/// Bring-your-own allocator with every function call.
266/// Initialize directly and deinitialize with `deinit` or use `toOwnedSlice`.
267pub fn init() Self {
268 return .{
269 .items = &[_]T{},
270 .capacity = 0,
271 };
272}
273
274pub fn ArrayListUnmanaged(comptime T: type) type {
275 return ArrayListAlignedUnmanaged(T, null);
276}
277
278pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) type {
279 if (alignment) |a| {
280 if (a == @alignOf(T)) {
281 return ArrayListAlignedUnmanaged(T, null);
282 }
283 }
284 return struct {
285 const Self = @This();
286
287 /// Content of the ArrayList.
288 items: Slice = &[_]T{},
289 capacity: usize = 0,
290
291 pub const Slice = if (alignment) |a| ([]align(a) T) else []T;
292 pub const SliceConst = if (alignment) |a| ([]align(a) const T) else []const T;
293
294 /// Initialize with capacity to hold at least num elements.
295 /// Deinitialize with `deinit` or use `toOwnedSlice`.
296 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
297 var self = Self.init(allocator);
298 try self.ensureCapacity(allocator, num);
299 return self;
300 }
301
302 /// Release all allocated memory.
303 pub fn deinit(self: *Self, allocator: *Allocator) void {
304 allocator.free(self.allocatedSlice());
305 self.* = undefined;
306 }
307
308 pub fn toManaged(self: *Self, allocator: *Allocator) ArrayListAligned(T, alignment) {
309 return .{ .items = self.items, .capacity = self.capacity, .allocator = allocator };
310 }
311
312 /// The caller owns the returned memory. ArrayList becomes empty.
313 pub fn toOwnedSlice(self: *Self, allocator: *Allocator) Slice {
314 const result = allocator.shrink(self.allocatedSlice(), self.items.len);
315 self.* = init(allocator);
316 return result;
317 }
318
319 /// Insert `item` at index `n`. Moves `list[n .. list.len]`
320 /// to make room.
321 pub fn insert(self: *Self, allocator: *Allocator, n: usize, item: T) !void {
322 try self.ensureCapacity(allocator, self.items.len + 1);
323 self.items.len += 1;
324
325 mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]);
326 self.items[n] = item;
327 }
328
329 /// Insert slice `items` at index `i`. Moves
330 /// `list[i .. list.len]` to make room.
331 /// This operation is O(N).
332 pub fn insertSlice(self: *Self, allocator: *Allocator, i: usize, items: SliceConst) !void {
333 try self.ensureCapacity(allocator, self.items.len + items.len);
334 self.items.len += items.len;
335
336 mem.copyBackwards(T, self.items[i + items.len .. self.items.len], self.items[i .. self.items.len - items.len]);
337 mem.copy(T, self.items[i .. i + items.len], items);
338 }
339
340 /// Extend the list by 1 element. Allocates more memory as necessary.
341 pub fn append(self: *Self, allocator: *Allocator, item: T) !void {
342 const new_item_ptr = try self.addOne(allocator);
343 new_item_ptr.* = item;
344 }
345
346 /// Extend the list by 1 element, but asserting `self.capacity`
347 /// is sufficient to hold an additional item.
348 pub fn appendAssumeCapacity(self: *Self, item: T) void {
349 const new_item_ptr = self.addOneAssumeCapacity();
350 new_item_ptr.* = item;
351 }
352
353 /// Remove the element at index `i` from the list and return its value.
354 /// Asserts the array has at least one item.
355 /// This operation is O(N).
356 pub fn orderedRemove(self: *Self, i: usize) T {
357 const newlen = self.items.len - 1;
358 if (newlen == i) return self.pop();
359
360 const old_item = self.items[i];
361 for (self.items[i..newlen]) |*b, j| b.* = self.items[i + 1 + j];
362 self.items[newlen] = undefined;
363 self.items.len = newlen;
364 return old_item;
365 }
366
367 /// Removes the element at the specified index and returns it.
368 /// The empty slot is filled from the end of the list.
369 /// This operation is O(1).
370 pub fn swapRemove(self: *Self, i: usize) T {
371 if (self.items.len - 1 == i) return self.pop();
372
373 const old_item = self.items[i];
374 self.items[i] = self.pop();
375 return old_item;
376 }
377
378 /// Append the slice of items to the list. Allocates more
379 /// memory as necessary.
380 pub fn appendSlice(self: *Self, allocator: *Allocator, items: SliceConst) !void {
381 const oldlen = self.items.len;
382 const newlen = self.items.len + items.len;
383
384 try self.ensureCapacity(allocator, newlen);
385 self.items.len = newlen;
386 mem.copy(T, self.items[oldlen..], items);
387 }
388
389 /// Same as `append` except it returns the number of bytes written, which is always the same
390 /// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
391 /// This function may be called only when `T` is `u8`.
392 fn appendWrite(self: *Self, allocator: *Allocator, m: []const u8) !usize {
393 try self.appendSlice(allocator, m);
394 return m.len;
395 }
396
397 /// Append a value to the list `n` times.
398 /// Allocates more memory as necessary.
399 pub fn appendNTimes(self: *Self, allocator: *Allocator, value: T, n: usize) !void {
400 const old_len = self.items.len;
401 try self.resize(self.items.len + n);
402 mem.set(T, self.items[old_len..self.items.len], value);
403 }
404
405 /// Adjust the list's length to `new_len`.
406 /// Does not initialize added items if any.
407 pub fn resize(self: *Self, allocator: *Allocator, new_len: usize) !void {
408 try self.ensureCapacity(allocator, new_len);
409 self.items.len = new_len;
410 }
411
412 /// Reduce allocated capacity to `new_len`.
413 /// Invalidates element pointers.
414 pub fn shrink(self: *Self, allocator: *Allocator, new_len: usize) void {
415 assert(new_len <= self.items.len);
416
417 self.items = allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) {
418 error.OutOfMemory => { // no problem, capacity is still correct then.
419 self.items.len = new_len;
420 return;
421 },
422 };
423 self.capacity = new_len;
424 }
425
426 pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void {
427 var better_capacity = self.capacity;
428 if (better_capacity >= new_capacity) return;
429
430 while (true) {
431 better_capacity += better_capacity / 2 + 8;
432 if (better_capacity >= new_capacity) break;
433 }
434
435 const new_memory = try allocator.realloc(self.allocatedSlice(), better_capacity);
436 self.items.ptr = new_memory.ptr;
437 self.capacity = new_memory.len;
438 }
439
440 /// Increases the array's length to match the full capacity that is already allocated.
441 /// The new elements have `undefined` values.
442 /// This operation does not invalidate any element pointers.
443 pub fn expandToCapacity(self: *Self) void {
444 self.items.len = self.capacity;
445 }
446
447 /// Increase length by 1, returning pointer to the new item.
448 /// The returned pointer becomes invalid when the list is resized.
449 pub fn addOne(self: *Self, allocator: *Allocator) !*T {
450 const newlen = self.items.len + 1;
451 try self.ensureCapacity(allocator, newlen);
452 return self.addOneAssumeCapacity();
453 }
454
455 /// Increase length by 1, returning pointer to the new item.
456 /// Asserts that there is already space for the new item without allocating more.
457 /// The returned pointer becomes invalid when the list is resized.
458 /// This operation does not invalidate any element pointers.
459 pub fn addOneAssumeCapacity(self: *Self) *T {
460 assert(self.items.len < self.capacity);
461
462 self.items.len += 1;
463 return &self.items[self.items.len - 1];
464 }
465
466 /// Remove and return the last element from the list.
467 /// Asserts the list has at least one item.
468 /// This operation does not invalidate any element pointers.
469 pub fn pop(self: *Self) T {
470 const val = self.items[self.items.len - 1];
471 self.items.len -= 1;
472 return val;
473 }
474
475 /// Remove and return the last element from the list.
476 /// If the list is empty, returns `null`.
477 /// This operation does not invalidate any element pointers.
478 pub fn popOrNull(self: *Self) ?T {
479 if (self.items.len == 0) return null;
480 return self.pop();
481 }
482
483 /// For a nicer API, `items.len` is the length, not the capacity.
484 /// This requires "unsafe" slicing.
485 fn allocatedSlice(self: Self) Slice {
486 return self.items.ptr[0..self.capacity];
487 }
488 };
489}
490
262491test "std.ArrayList.init" {
263492 var list = ArrayList(i32).init(testing.allocator);
264493 defer list.deinit();
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/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/Package.zig created+52
......@@ -0,0 +1,52 @@
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;
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+25-33
......@@ -6,38 +6,24 @@ const Type = @import("type.zig").Type;
66const Value = @import("value.zig").Value;
77const Target = std.Target;
88
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 }
24};
25
26pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.ArrayList(u8)) !Symbol {
9pub fn generateSymbol(
10 typed_value: ir.TypedValue,
11 module: ir.Module,
12 code: *std.ArrayList(u8),
13 errors: *std.ArrayList(ir.ErrorMsg),
14) !void {
2715 switch (typed_value.ty.zigTypeTag()) {
2816 .Fn => {
29 const index = typed_value.val.cast(Value.Payload.Function).?.index;
30 const module_fn = module.fns[index];
17 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
3118
3219 var function = Function{
3320 .module = &module,
34 .mod_fn = &module_fn,
21 .mod_fn = module_fn,
3522 .code = code,
3623 .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator),
37 .errors = std.ArrayList(ErrorMsg).init(code.allocator),
24 .errors = errors,
3825 };
3926 defer function.inst_table.deinit();
40 defer function.errors.deinit();
4127
4228 for (module_fn.body.instructions) |inst| {
4329 const new_inst = function.genFuncInst(inst) catch |err| switch (err) {
......@@ -52,7 +38,7 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.
5238
5339 return Symbol{ .errors = function.errors.toOwnedSlice() };
5440 },
55 else => @panic("TODO implement generateSymbol for non-function types"),
41 else => @panic("TODO implement generateSymbol for non-function decls"),
5642 }
5743}
5844
......@@ -61,7 +47,7 @@ const Function = struct {
6147 mod_fn: *const ir.Module.Fn,
6248 code: *std.ArrayList(u8),
6349 inst_table: std.AutoHashMap(*ir.Inst, MCValue),
64 errors: std.ArrayList(ErrorMsg),
50 errors: *std.ArrayList(ir.ErrorMsg),
6551
6652 const MCValue = union(enum) {
6753 none,
......@@ -78,6 +64,7 @@ const Function = struct {
7864 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {
7965 switch (inst.tag) {
8066 .breakpoint => return self.genBreakpoint(inst.src),
67 .call => return self.genCall(inst.cast(ir.Inst.Call).?),
8168 .unreach => return MCValue{ .unreach = {} },
8269 .constant => unreachable, // excluded from function bodies
8370 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
......@@ -101,6 +88,13 @@ const Function = struct {
10188 return .unreach;
10289 }
10390
91 fn genCall(self: *Function, inst: *ir.Inst.Call) !MCValue {
92 switch (self.module.target.cpu.arch) {
93 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.module.target.cpu.arch}),
94 }
95 return .unreach;
96 }
97
10498 fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
10599 switch (self.module.target.cpu.arch) {
106100 .i386, .x86_64 => {
......@@ -140,6 +134,7 @@ const Function = struct {
140134 fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void {
141135 switch (self.module.target.cpu.arch) {
142136 .i386, .x86_64 => {
137 // TODO x86 treats the operands as signed
143138 if (amount <= std.math.maxInt(u8)) {
144139 try self.code.resize(self.code.items.len + 2);
145140 self.code.items[self.code.items.len - 2] = 0xeb;
......@@ -433,14 +428,11 @@ const Function = struct {
433428
434429 fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } {
435430 @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 }
431 try self.errors.ensureCapacity(self.errors.items.len + 1);
432 self.errors.appendAssumeCapacity(.{
433 .byte_offset = src,
434 .msg = try std.fmt.allocPrint(self.errors.allocator, format, args),
435 });
444436 return error.CodegenFail;
445437 }
446438};
src-self-hosted/compilation.zig+56-33
......@@ -19,7 +19,6 @@ const AtomicOrder = builtin.AtomicOrder;
1919const Scope = @import("scope.zig").Scope;
2020const Decl = @import("decl.zig").Decl;
2121const ir = @import("ir.zig");
22const Visib = @import("visib.zig").Visib;
2322const Value = @import("value.zig").Value;
2423const Type = Value.Type;
2524const Span = errmsg.Span;
......@@ -30,7 +29,11 @@ const link = @import("link.zig").link;
3029const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
3130const CInt = @import("c_int.zig").CInt;
3231const fs = std.fs;
33const util = @import("util.zig");
32
33pub const Visib = enum {
34 Private,
35 Pub,
36};
3437
3538const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
3639
......@@ -45,7 +48,7 @@ pub const ZigCompiler = struct {
4548
4649 native_libc: event.Future(LibCInstallation),
4750
48 var lazy_init_targets = std.once(util.initializeAllTargets);
51 var lazy_init_targets = std.once(initializeAllTargets);
4952
5053 pub fn init(allocator: *Allocator) !ZigCompiler {
5154 lazy_init_targets.call();
......@@ -119,6 +122,8 @@ pub const LlvmHandle = struct {
119122};
120123
121124pub const Compilation = struct {
125 pub const FnLinkSet = std.TailQueue(?*Value.Fn);
126
122127 zig_compiler: *ZigCompiler,
123128 name: ArrayListSentineled(u8, 0),
124129 llvm_triple: ArrayListSentineled(u8, 0),
......@@ -152,8 +157,6 @@ pub const Compilation = struct {
152157 /// it uses an optional pointer so that tombstone removals are possible
153158 fn_link_set: event.Locked(FnLinkSet) = event.Locked(FnLinkSet).init(FnLinkSet.init()),
154159
155 pub const FnLinkSet = std.TailQueue(?*Value.Fn);
156
157160 link_libs_list: ArrayList(*LinkLib),
158161 libc_link_lib: ?*LinkLib = null,
159162
......@@ -361,8 +364,7 @@ pub const Compilation = struct {
361364 return comp;
362365 } else if (await frame) |_| unreachable else |err| return err;
363366 }
364
365 async fn createAsync(
367 fn createAsync(
366368 out_comp: *?*Compilation,
367369 zig_compiler: *ZigCompiler,
368370 name: []const u8,
......@@ -372,7 +374,7 @@ pub const Compilation = struct {
372374 build_mode: builtin.Mode,
373375 is_static: bool,
374376 zig_lib_dir: []const u8,
375 ) !void {
377 ) callconv(.Async) !void {
376378 const allocator = zig_compiler.allocator;
377379
378380 // TODO merge this line with stage2.zig crossTargetToTarget
......@@ -442,8 +444,8 @@ pub const Compilation = struct {
442444 }
443445
444446 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.llvm_triple = try getLLVMTriple(comp.arena(), target);
448 comp.llvm_target = try llvmTargetFromTriple(comp.llvm_triple);
447449 comp.zig_std_dir = try fs.path.join(comp.arena(), &[_][]const u8{ zig_lib_dir, "std" });
448450
449451 const opt_level = switch (build_mode) {
......@@ -726,8 +728,7 @@ pub const Compilation = struct {
726728 fn start(self: *Compilation) void {
727729 self.main_loop_future.resolve();
728730 }
729
730 async fn mainLoop(self: *Compilation) void {
731 fn mainLoop(self: *Compilation) callconv(.Async) void {
731732 // wait until start() is called
732733 _ = self.main_loop_future.get();
733734
......@@ -790,8 +791,7 @@ pub const Compilation = struct {
790791 build_result = group.wait();
791792 }
792793 }
793
794 async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) BuildError!void {
794 fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) callconv(.Async) BuildError!void {
795795 const tree_scope = blk: {
796796 const source_code = fs.cwd().readFileAlloc(
797797 self.gpa(),
......@@ -964,15 +964,14 @@ pub const Compilation = struct {
964964 try link(self);
965965 }
966966 }
967
968967 /// caller takes ownership of resulting Code
969 async fn genAndAnalyzeCode(
968 fn genAndAnalyzeCode(
970969 comp: *Compilation,
971970 tree_scope: *Scope.AstTree,
972971 scope: *Scope,
973972 node: *ast.Node,
974973 expected_type: ?*Type,
975 ) !*ir.Code {
974 ) callconv(.Async) !*ir.Code {
976975 const unanalyzed_code = try ir.gen(
977976 comp,
978977 node,
......@@ -1000,13 +999,12 @@ pub const Compilation = struct {
1000999
10011000 return analyzed_code;
10021001 }
1003
1004 async fn addCompTimeBlock(
1002 fn addCompTimeBlock(
10051003 comp: *Compilation,
10061004 tree_scope: *Scope.AstTree,
10071005 scope: *Scope,
10081006 comptime_node: *ast.Node.Comptime,
1009 ) BuildError!void {
1007 ) callconv(.Async) BuildError!void {
10101008 const void_type = Type.Void.get(comp);
10111009 defer void_type.base.base.deref(comp);
10121010
......@@ -1024,12 +1022,11 @@ pub const Compilation = struct {
10241022 };
10251023 analyzed_code.destroy(comp.gpa());
10261024 }
1027
1028 async fn addTopLevelDecl(
1025 fn addTopLevelDecl(
10291026 self: *Compilation,
10301027 decl: *Decl,
10311028 locked_table: *Decl.Table,
1032 ) BuildError!void {
1029 ) callconv(.Async) BuildError!void {
10331030 const is_export = decl.isExported(decl.tree_scope.tree);
10341031
10351032 if (is_export) {
......@@ -1065,11 +1062,10 @@ pub const Compilation = struct {
10651062
10661063 try self.prelink_group.call(addCompileErrorAsync, .{ self, msg });
10671064 }
1068
1069 async fn addCompileErrorAsync(
1065 fn addCompileErrorAsync(
10701066 self: *Compilation,
10711067 msg: *Msg,
1072 ) BuildError!void {
1068 ) callconv(.Async) BuildError!void {
10731069 errdefer msg.destroy();
10741070
10751071 const compile_errors = self.compile_errors.acquire();
......@@ -1077,8 +1073,7 @@ pub const Compilation = struct {
10771073
10781074 try compile_errors.value.append(msg);
10791075 }
1080
1081 async fn verifyUniqueSymbol(self: *Compilation, decl: *Decl) BuildError!void {
1076 fn verifyUniqueSymbol(self: *Compilation, decl: *Decl) callconv(.Async) BuildError!void {
10821077 const exported_symbol_names = self.exported_symbol_names.acquire();
10831078 defer exported_symbol_names.release();
10841079
......@@ -1129,8 +1124,7 @@ pub const Compilation = struct {
11291124 }
11301125 return link_lib;
11311126 }
1132
1133 async fn startFindingNativeLibC(self: *Compilation) void {
1127 fn startFindingNativeLibC(self: *Compilation) callconv(.Async) void {
11341128 event.Loop.startCpuBoundOperation();
11351129 // we don't care if it fails, we're just trying to kick off the future resolution
11361130 _ = self.zig_compiler.getNativeLibC() catch return;
......@@ -1234,7 +1228,7 @@ pub const Compilation = struct {
12341228 }
12351229
12361230 /// This declaration has been blessed as going into the final code generation.
1237 pub async fn resolveDecl(comp: *Compilation, decl: *Decl) BuildError!void {
1231 pub fn resolveDecl(comp: *Compilation, decl: *Decl) callconv(.Async) BuildError!void {
12381232 if (decl.resolution.start()) |ptr| return ptr.*;
12391233
12401234 decl.resolution.data = try generateDecl(comp, decl);
......@@ -1335,8 +1329,7 @@ fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
13351329 try comp.prelink_group.call(codegen.renderToLlvm, .{ comp, fn_val, analyzed_code });
13361330 try comp.prelink_group.call(addFnToLinkSet, .{ comp, fn_val });
13371331}
1338
1339async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) Compilation.BuildError!void {
1332fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) callconv(.Async) Compilation.BuildError!void {
13401333 fn_val.base.ref();
13411334 defer fn_val.base.deref(comp);
13421335
......@@ -1432,3 +1425,33 @@ fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
14321425 fn_decl.value = .{ .FnProto = fn_proto_val };
14331426 symbol_name_consumed = true;
14341427}
1428
1429pub fn llvmTargetFromTriple(triple: [:0]const u8) !*llvm.Target {
1430 var result: *llvm.Target = undefined;
1431 var err_msg: [*:0]u8 = undefined;
1432 if (llvm.GetTargetFromTriple(triple, &result, &err_msg) != 0) {
1433 std.debug.warn("triple: {s} error: {s}\n", .{ triple, err_msg });
1434 return error.UnsupportedTarget;
1435 }
1436 return result;
1437}
1438
1439pub fn initializeAllTargets() void {
1440 llvm.InitializeAllTargets();
1441 llvm.InitializeAllTargetInfos();
1442 llvm.InitializeAllTargetMCs();
1443 llvm.InitializeAllAsmPrinters();
1444 llvm.InitializeAllAsmParsers();
1445}
1446
1447pub fn getLLVMTriple(allocator: *std.mem.Allocator, target: std.Target) ![:0]u8 {
1448 var result = try std.ArrayListSentineled(u8, 0).initSize(allocator, 0);
1449 defer result.deinit();
1450
1451 try result.outStream().print(
1452 "{}-unknown-{}-{}",
1453 .{ @tagName(target.cpu.arch), @tagName(target.os.tag), @tagName(target.abi) },
1454 );
1455
1456 return result.toOwnedSlice();
1457}
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/ir.zig+716-352
......@@ -1,12 +1,16 @@
11const std = @import("std");
22const mem = std.mem;
33const Allocator = std.mem.Allocator;
4const ArrayListUnmanaged = std.ArrayListUnmanaged;
5const LinkedList = std.TailQueue;
46const Value = @import("value.zig").Value;
57const Type = @import("type.zig").Type;
68const assert = std.debug.assert;
79const BigIntConst = std.math.big.int.Const;
810const BigIntMutable = std.math.big.int.Mutable;
911const Target = std.Target;
12const Package = @import("Package.zig");
13const link = @import("link.zig");
1014
1115pub const text = @import("ir/text.zig");
1216
......@@ -25,6 +29,7 @@ pub const Inst = struct {
2529 assembly,
2630 bitcast,
2731 breakpoint,
32 call,
2833 cmp,
2934 condbr,
3035 constant,
......@@ -84,6 +89,15 @@ pub const Inst = struct {
8489 args: void,
8590 };
8691
92 pub const Call = struct {
93 pub const base_tag = Tag.call;
94 base: Inst,
95 args: struct {
96 func: *Inst,
97 args: []const *Inst,
98 },
99 };
100
87101 pub const Cmp = struct {
88102 pub const base_tag = Tag.cmp;
89103
......@@ -158,170 +172,416 @@ pub const TypedValue = struct {
158172 val: Value,
159173};
160174
175fn swapRemoveElem(allocator: *Allocator, comptime T: type, item: T, list: *ArrayListUnmanaged(T)) void {
176 var i: usize = 0;
177 while (i < list.items.len) {
178 if (list.items[i] == item) {
179 list.swapRemove(allocator, i);
180 continue;
181 }
182 i += 1;
183 }
184}
185
161186pub 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,
187 /// General-purpose allocator.
188 allocator: *Allocator,
189 /// Module owns this resource.
190 root_pkg: *Package,
191 /// Module owns this resource.
192 root_scope: *Scope.ZIRModule,
193 /// Pointer to externally managed resource.
194 bin_file: *link.ElfFile,
195 failed_decls: ArrayListUnmanaged(*Decl) = .{},
196 failed_fns: ArrayListUnmanaged(*Fn) = .{},
197 failed_files: ArrayListUnmanaged(*Scope.ZIRModule) = .{},
198 decl_table: std.AutoHashMap(Decl.Hash, *Decl),
170199 optimize_mode: std.builtin.Mode,
171
172 pub const Export = struct {
173 name: []const u8,
174 typed_value: TypedValue,
200 link_error_flags: link.ElfFile.ErrorFlags = .{},
201
202 pub const Decl = struct {
203 /// Contains the memory for `typed_value` and this `Decl` itself.
204 /// If the Decl is a function, also contains that memory.
205 /// If the decl has any export nodes, also contains that memory.
206 /// TODO look into using a more memory efficient arena that will cost less bytes per decl.
207 /// This one has a minimum allocation of 4096 bytes.
208 arena: std.heap.ArenaAllocator.State,
209 /// This name is relative to the containing namespace of the decl. It uses a null-termination
210 /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
211 /// in symbol names, because executable file formats use null-terminated strings for symbol names.
212 name: [*:0]const u8,
213 /// It's rare for a decl to be exported, and it's even rarer for a decl to be mapped to more
214 /// than one export, so we use a linked list to save memory.
215 export_node: ?*LinkedList(std.builtin.ExportOptions).Node = null,
216 /// Byte offset into the source file that contains this declaration.
217 /// This is the base offset that src offsets within this Decl are relative to.
175218 src: usize,
219 /// Represents the "shallow" analysis status. For example, for decls that are functions,
220 /// the function type is analyzed with this set to `in_progress`, however, the semantic
221 /// analysis of the function body is performed with this value set to `success`. Functions
222 /// have their own analysis status field.
223 analysis: union(enum) {
224 in_progress,
225 failure: ErrorMsg,
226 success: TypedValue,
227 },
228 /// The direct container of the Decl. This field will need to get more fleshed out when
229 /// self-hosted supports proper struct types and Zig AST => ZIR.
230 scope: *Scope.ZIRModule,
231
232 pub fn destroy(self: *Decl, allocator: *Allocator) void {
233 var arena = self.arena.promote(allocator);
234 arena.deinit();
235 }
236
237 pub const Hash = [16]u8;
238
239 /// Must generate unique bytes with no collisions with other decls.
240 /// The point of hashing here is only to limit the number of bytes of
241 /// the unique identifier to a fixed size (16 bytes).
242 pub fn fullyQualifiedNameHash(self: Decl) Hash {
243 // Right now we only have ZIRModule as the source. So this is simply the
244 // relative name of the decl.
245 var out: Hash = undefined;
246 std.crypto.Blake3.hash(mem.spanZ(u8, self.name), &out);
247 return out;
248 }
176249 };
177250
251 /// Memory is managed by the arena of the owning Decl.
178252 pub const Fn = struct {
179 analysis_status: enum { in_progress, failure, success },
180 body: Body,
181253 fn_type: Type,
254 analysis: union(enum) {
255 in_progress: *Analysis,
256 failure: ErrorMsg,
257 success: Body,
258 },
259 /// The direct container of the Fn. This field will need to get more fleshed out when
260 /// self-hosted supports proper struct types and Zig AST => ZIR.
261 scope: *Scope.ZIRModule,
262
263 /// This memory managed by the general purpose allocator.
264 pub const Analysis = struct {
265 inner_block: Scope.Block,
266 /// null value means a semantic analysis error happened.
267 inst_table: std.AutoHashMap(*text.Inst, ?*Inst),
268 };
269 };
270
271 pub const Scope = struct {
272 tag: Tag,
273
274 pub fn cast(base: *Scope, comptime T: type) ?*T {
275 if (base.tag != T.base_tag)
276 return null;
277
278 return @fieldParentPtr(T, "base", base);
279 }
280
281 pub const Tag = enum {
282 zir_module,
283 block,
284 decl,
285 };
286
287 pub const ZIRModule = struct {
288 pub const base_tag: Tag = .zir_module;
289 base: Scope = Scope{ .tag = base_tag },
290 /// Relative to the owning package's root_src_dir.
291 /// Reference to external memory, not owned by ZIRModule.
292 sub_file_path: []const u8,
293 contents: union(enum) {
294 unloaded,
295 parse_failure: ParseFailure,
296 success: Contents,
297 },
298 pub const ParseFailure = struct {
299 source: [:0]const u8,
300 errors: []ErrorMsg,
301
302 pub fn deinit(self: *ParseFailure, allocator: *Allocator) void {
303 allocator.free(self.errors);
304 allocator.free(source);
305 }
306 };
307 pub const Contents = struct {
308 source: [:0]const u8,
309 module: *text.Module,
310 };
311
312 pub fn deinit(self: *ZIRModule, allocator: *Allocator) void {
313 switch (self.contents) {
314 .unloaded => {},
315 .parse_failure => |pf| pd.deinit(allocator),
316 .success => |contents| {
317 allocator.free(contents.source);
318 contents.src_zir_module.deinit(allocator);
319 },
320 }
321 self.* = undefined;
322 }
323
324 pub fn loadContents(self: *ZIRModule, allocator: *Allocator) !*Contents {
325 if (self.contents) |contents| return contents;
326
327 const max_size = std.math.maxInt(u32);
328 const source = try self.root_pkg_dir.readFileAllocOptions(allocator, self.root_src_path, max_size, 1, 0);
329 errdefer allocator.free(source);
330
331 var errors = std.ArrayList(ErrorMsg).init(allocator);
332 defer errors.deinit();
333
334 var src_zir_module = try text.parse(allocator, source, &errors);
335 errdefer src_zir_module.deinit(allocator);
336
337 switch (self.contents) {
338 .parse_failure => |pf| pf.deinit(allocator),
339 .unloaded => {},
340 .success => unreachable,
341 }
342
343 if (errors.items.len != 0) {
344 self.contents = .{ .parse_failure = errors.toOwnedSlice() };
345 return error.ParseFailure;
346 }
347 self.contents = .{
348 .success = .{
349 .source = source,
350 .module = src_zir_module,
351 },
352 };
353 return &self.contents.success;
354 }
355 };
356
357 /// This is a temporary structure, references to it are valid only
358 /// during semantic analysis of the block.
359 pub const Block = struct {
360 pub const base_tag: Tag = .block;
361 base: Scope = Scope{ .tag = base_tag },
362 func: *Fn,
363 instructions: ArrayListUnmanaged(*Inst),
364 };
365
366 /// This is a temporary structure, references to it are valid only
367 /// during semantic analysis of the decl.
368 pub const DeclAnalysis = struct {
369 pub const base_tag: Tag = .decl;
370 base: Scope = Scope{ .tag = base_tag },
371 decl: *Decl,
372 };
182373 };
183374
184375 pub const Body = struct {
185376 instructions: []*Inst,
186377 };
187378
188 pub fn deinit(self: *Module, allocator: *Allocator) void {
189 allocator.free(self.exports);
379 pub const AllErrors = struct {
380 arena: std.heap.ArenaAllocator.State,
381 list: []const Message,
382
383 pub const Message = struct {
384 src_path: []const u8,
385 line: usize,
386 column: usize,
387 byte_offset: usize,
388 msg: []const u8,
389 };
390
391 pub fn deinit(self: *AllErrors, allocator: *Allocator) void {
392 self.arena.promote(allocator).deinit();
393 }
394
395 fn add(
396 arena: *std.heap.ArenaAllocator,
397 errors: *std.ArrayList(Message),
398 sub_file_path: []const u8,
399 source: []const u8,
400 simple_err_msg: ErrorMsg,
401 ) !void {
402 const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset);
403 try errors.append(.{
404 .src_path = try mem.dupe(u8, &arena.allocator, sub_file_path),
405 .msg = try mem.dupe(u8, &arena.allocator, simple_err_msg.msg),
406 .byte_offset = simple_err_msg.byte_offset,
407 .line = loc.line,
408 .column = loc.column,
409 });
410 }
411 };
412
413 pub fn deinit(self: *Module) void {
414 const allocator = self.allocator;
190415 allocator.free(self.errors);
191 for (self.fns) |f| {
192 allocator.free(f.body.instructions);
416 {
417 var it = self.decl_table.iterator();
418 while (it.next()) |kv| {
419 kv.value.destroy(allocator);
420 }
421 self.decl_table.deinit();
193422 }
194 allocator.free(self.fns);
195 self.arena.deinit();
423 self.root_pkg.destroy();
424 self.root_scope.deinit();
196425 self.* = undefined;
197426 }
198};
199427
200pub const ErrorMsg = struct {
201 byte_offset: usize,
202 msg: []const u8,
203};
428 pub fn target(self: Module) std.Target {
429 return self.bin_file.options.target;
430 }
204431
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};
432 /// Detect changes to source files, perform semantic analysis, and update the output files.
433 pub fn update(self: *Module) !void {
434 // TODO Use the cache hash file system to detect which source files changed.
435 // Here we simulate a full cache miss.
436 // Analyze the root source file now.
437 self.analyzeRoot(self.root_scope) catch |err| switch (err) {
438 error.AnalysisFail => {
439 assert(self.totalErrorCount() != 0);
440 },
441 else => |e| return e,
442 };
212443
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}
444 try self.bin_file.flush();
445 self.link_error_flags = self.bin_file.error_flags;
446 }
251447
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,
448 pub fn totalErrorCount(self: *Module) usize {
449 return self.failed_decls.items.len +
450 self.failed_fns.items.len +
451 self.failed_decls.items.len +
452 @boolToInt(self.link_error_flags.no_entry_point_found);
453 }
264454
265 const NewDecl = struct {
266 /// null means a semantic analysis error happened
267 ptr: ?*Inst,
268 };
455 pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
456 var arena = std.heap.ArenaAllocator.init(self.allocator);
457 errdefer arena.deinit();
269458
270 const NewInst = struct {
271 /// null means a semantic analysis error happened
272 ptr: ?*Inst,
273 };
459 var errors = std.ArrayList(AllErrors.Message).init(self.allocator);
460 defer errors.deinit();
274461
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 };
462 for (self.failed_files.items) |scope| {
463 const source = scope.parse_failure.source;
464 for (scope.parse_failure.errors) |parse_error| {
465 AllErrors.add(&arena, &errors, scope.sub_file_path, source, parse_error);
466 }
467 }
281468
282 const Block = struct {
283 func: *Fn,
284 instructions: std.ArrayList(*Inst),
285 };
469 for (self.failed_fns.items) |func| {
470 const source = func.scope.success.source;
471 for (func.analysis.failure) |err_msg| {
472 AllErrors.add(&arena, &errors, func.scope.sub_file_path, source, err_msg);
473 }
474 }
475
476 for (self.failed_decls.items) |decl| {
477 const source = decl.scope.success.source;
478 for (decl.analysis.failure) |err_msg| {
479 AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg);
480 }
481 }
482
483 if (self.link_error_flags.no_entry_point_found) {
484 try errors.append(.{
485 .src_path = self.module.root_src_path,
486 .line = 0,
487 .column = 0,
488 .byte_offset = 0,
489 .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}),
490 });
491 }
492
493 assert(errors.items.len == self.totalErrorCount());
494
495 return AllErrors{
496 .arena = arena.state,
497 .list = try mem.dupe(&arena.allocator, AllErrors.Message, errors.items),
498 };
499 }
286500
287501 const InnerError = error{ OutOfMemory, AnalysisFail };
288502
289 fn analyzeRoot(self: *Analyze) !void {
290 for (self.old_module.decls) |decl| {
503 fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
504 // TODO use the cache to identify, from the modified source files, the decls which have
505 // changed based on the span of memory that represents the decl in the re-parsed source file.
506 // Use the cached dependency graph to recursively determine the set of decls which need
507 // regeneration.
508 // Here we simulate adding a source file which was previously not part of the compilation,
509 // which means scanning the decls looking for exports.
510 // TODO also identify decls that need to be deleted.
511 const contents = blk: {
512 // Clear parse errors.
513 swapRemoveElem(self.allocator, *Scope.ZIRModule, root_scope, self.failed_files);
514 try self.failed_files.ensureCapacity(self.allocator, self.failed_files.items.len + 1);
515 break :blk root_scope.loadContents(self.allocator) catch |err| switch (err) {
516 error.ParseFailure => {
517 self.failed_files.appendAssumeCapacity(root_scope);
518 return error.AnalysisFail;
519 },
520 else => |e| return e,
521 };
522 };
523 for (contents.module.decls) |decl| {
291524 if (decl.cast(text.Inst.Export)) |export_inst| {
292 try analyzeExport(self, null, export_inst);
525 try analyzeExport(self, &root_scope.base, export_inst);
293526 }
294527 }
295528 }
296529
297 fn resolveInst(self: *Analyze, opt_block: ?*Block, old_inst: *text.Inst) InnerError!*Inst {
298 if (opt_block) |block| {
530 fn resolveDecl(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Decl {
531 const hash = old_inst.fullyQualifiedNameHash();
532 if (self.decl_table.get(hash)) |kv| {
533 return kv.value;
534 } else {
535 const new_decl = blk: {
536 var decl_arena = std.heap.ArenaAllocator.init(self.allocator);
537 errdefer decl_arena.deinit();
538 const new_decl = try decl_arena.allocator.create(Decl);
539 const name = try mem.dupeZ(&decl_arena.allocator, u8, old_inst.name);
540 new_decl.* = .{
541 .arena = decl_arena.state,
542 .name = name,
543 .src = old_inst.src,
544 .analysis = .in_progress,
545 .scope = scope.findZIRModule(),
546 };
547 try self.decl_table.putNoClobber(hash, new_decl);
548 break :blk new_decl;
549 };
550
551 var decl_scope: Scope.DeclAnalysis = .{ .decl = new_decl };
552 const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) {
553 error.AnalysisFail => return error.AnalysisFail,
554 else => |e| return e,
555 };
556 new_decl.analysis = .{ .success = typed_value };
557 if (try self.bin_file.updateDecl(self.*, typed_value, new_decl.export_node, hash)) |err_msg| {
558 new_decl.analysis = .{ .success = typed_value };
559 } else |err| {
560 return err;
561 }
562 return new_decl;
563 }
564 }
565
566 fn resolveInst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Inst {
567 if (scope.cast(Scope.Block)) |block| {
299568 if (block.func.inst_table.get(old_inst)) |kv| {
300569 return kv.value.ptr orelse return error.AnalysisFail;
301570 }
302571 }
303572
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 }
573 const decl = try self.resolveDecl(scope, old_inst);
574 const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);
575 return self.analyzeDeref(scope, old_inst.src, decl_ref);
317576 }
318577
319 fn requireRuntimeBlock(self: *Analyze, block: ?*Block, src: usize) !*Block {
320 return block orelse return self.fail(src, "instruction illegal outside function body", .{});
578 fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
579 return scope.cast(Scope.Block) orelse
580 return self.fail(scope, src, "instruction illegal outside function body", .{});
321581 }
322582
323 fn resolveInstConst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!TypedValue {
324 const new_inst = try self.resolveInst(block, old_inst);
583 fn resolveInstConst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!TypedValue {
584 const new_inst = try self.resolveInst(scope, old_inst);
325585 const val = try self.resolveConstValue(new_inst);
326586 return TypedValue{
327587 .ty = new_inst.ty,
......@@ -329,60 +589,67 @@ const Analyze = struct {
329589 };
330590 }
331591
332 fn resolveConstValue(self: *Analyze, base: *Inst) !Value {
592 fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
333593 return (try self.resolveDefinedValue(base)) orelse
334 return self.fail(base.src, "unable to resolve comptime value", .{});
594 return self.fail(scope, base.src, "unable to resolve comptime value", .{});
335595 }
336596
337 fn resolveDefinedValue(self: *Analyze, base: *Inst) !?Value {
597 fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
338598 if (base.value()) |val| {
339599 if (val.isUndef()) {
340 return self.fail(base.src, "use of undefined value here causes undefined behavior", .{});
600 return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});
341601 }
342602 return val;
343603 }
344604 return null;
345605 }
346606
347 fn resolveConstString(self: *Analyze, block: ?*Block, old_inst: *text.Inst) ![]u8 {
348 const new_inst = try self.resolveInst(block, old_inst);
607 fn resolveConstString(self: *Module, scope: *Scope, old_inst: *text.Inst) ![]u8 {
608 const new_inst = try self.resolveInst(scope, old_inst);
349609 const wanted_type = Type.initTag(.const_slice_u8);
350 const coerced_inst = try self.coerce(block, wanted_type, new_inst);
610 const coerced_inst = try self.coerce(scope, wanted_type, new_inst);
351611 const val = try self.resolveConstValue(coerced_inst);
352612 return val.toAllocatedBytes(&self.arena.allocator);
353613 }
354614
355 fn resolveType(self: *Analyze, block: ?*Block, old_inst: *text.Inst) !Type {
356 const new_inst = try self.resolveInst(block, old_inst);
615 fn resolveType(self: *Module, scope: *Scope, old_inst: *text.Inst) !Type {
616 const new_inst = try self.resolveInst(scope, old_inst);
357617 const wanted_type = Type.initTag(.@"type");
358 const coerced_inst = try self.coerce(block, wanted_type, new_inst);
618 const coerced_inst = try self.coerce(scope, wanted_type, new_inst);
359619 const val = try self.resolveConstValue(coerced_inst);
360620 return val.toType();
361621 }
362622
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 ),
623 fn analyzeExport(self: *Module, scope: *Scope, export_inst: *text.Inst.Export) !void {
624 const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);
625 const decl = try self.resolveDecl(scope, export_inst.positionals.value);
626
627 switch (decl.analysis) {
628 .in_progress => unreachable,
629 .failure => return error.AnalysisFail,
630 .success => |typed_value| switch (typed_value.ty.zigTypeTag()) {
631 .Fn => {},
632 else => return self.fail(
633 scope,
634 export_inst.positionals.value.src,
635 "unable to export type '{}'",
636 .{typed_value.ty},
637 ),
638 },
374639 }
375 try self.exports.append(.{
376 .name = symbol_name,
377 .typed_value = typed_value,
378 .src = export_inst.base.src,
379 });
640 const Node = LinkedList(std.builtin.ExportOptions).Node;
641 export_node = try decl.arena.promote(self.allocator).allocator.create(Node);
642 export_node.* = .{ .data = .{ .name = symbol_name } };
643 decl.export_node = export_node;
644
645 // TODO Avoid double update in the case of exporting a decl that we just created.
646 self.bin_file.updateDeclExports();
380647 }
381648
382649 /// TODO should not need the cast on the last parameter at the callsites
383650 fn addNewInstArgs(
384 self: *Analyze,
385 block: *Block,
651 self: *Module,
652 block: *Scope.Block,
386653 src: usize,
387654 ty: Type,
388655 comptime T: type,
......@@ -393,7 +660,7 @@ const Analyze = struct {
393660 return &inst.base;
394661 }
395662
396 fn addNewInst(self: *Analyze, block: *Block, src: usize, ty: Type, comptime T: type) !*T {
663 fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T {
397664 const inst = try self.arena.allocator.create(T);
398665 inst.* = .{
399666 .base = .{
......@@ -403,11 +670,11 @@ const Analyze = struct {
403670 },
404671 .args = undefined,
405672 };
406 try block.instructions.append(&inst.base);
673 try block.instructions.append(self.allocator, &inst.base);
407674 return inst;
408675 }
409676
410 fn constInst(self: *Analyze, src: usize, typed_value: TypedValue) !*Inst {
677 fn constInst(self: *Module, src: usize, typed_value: TypedValue) !*Inst {
411678 const const_inst = try self.arena.allocator.create(Inst.Constant);
412679 const_inst.* = .{
413680 .base = .{
......@@ -420,7 +687,7 @@ const Analyze = struct {
420687 return &const_inst.base;
421688 }
422689
423 fn constStr(self: *Analyze, src: usize, str: []const u8) !*Inst {
690 fn constStr(self: *Module, src: usize, str: []const u8) !*Inst {
424691 const array_payload = try self.arena.allocator.create(Type.Payload.Array_u8_Sentinel0);
425692 array_payload.* = .{ .len = str.len };
426693
......@@ -436,35 +703,35 @@ const Analyze = struct {
436703 });
437704 }
438705
439 fn constType(self: *Analyze, src: usize, ty: Type) !*Inst {
706 fn constType(self: *Module, src: usize, ty: Type) !*Inst {
440707 return self.constInst(src, .{
441708 .ty = Type.initTag(.type),
442709 .val = try ty.toValue(&self.arena.allocator),
443710 });
444711 }
445712
446 fn constVoid(self: *Analyze, src: usize) !*Inst {
713 fn constVoid(self: *Module, src: usize) !*Inst {
447714 return self.constInst(src, .{
448715 .ty = Type.initTag(.void),
449716 .val = Value.initTag(.the_one_possible_value),
450717 });
451718 }
452719
453 fn constUndef(self: *Analyze, src: usize, ty: Type) !*Inst {
720 fn constUndef(self: *Module, src: usize, ty: Type) !*Inst {
454721 return self.constInst(src, .{
455722 .ty = ty,
456723 .val = Value.initTag(.undef),
457724 });
458725 }
459726
460 fn constBool(self: *Analyze, src: usize, v: bool) !*Inst {
727 fn constBool(self: *Module, src: usize, v: bool) !*Inst {
461728 return self.constInst(src, .{
462729 .ty = Type.initTag(.bool),
463730 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
464731 });
465732 }
466733
467 fn constIntUnsigned(self: *Analyze, src: usize, ty: Type, int: u64) !*Inst {
734 fn constIntUnsigned(self: *Module, src: usize, ty: Type, int: u64) !*Inst {
468735 const int_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
469736 int_payload.* = .{ .int = int };
470737
......@@ -474,7 +741,7 @@ const Analyze = struct {
474741 });
475742 }
476743
477 fn constIntSigned(self: *Analyze, src: usize, ty: Type, int: i64) !*Inst {
744 fn constIntSigned(self: *Module, src: usize, ty: Type, int: i64) !*Inst {
478745 const int_payload = try self.arena.allocator.create(Value.Payload.Int_i64);
479746 int_payload.* = .{ .int = int };
480747
......@@ -484,7 +751,7 @@ const Analyze = struct {
484751 });
485752 }
486753
487 fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
754 fn constIntBig(self: *Module, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
488755 const val_payload = if (big_int.positive) blk: {
489756 if (big_int.to(u64)) |x| {
490757 return self.constIntUnsigned(src, ty, x);
......@@ -513,9 +780,18 @@ const Analyze = struct {
513780 });
514781 }
515782
516 fn analyzeInst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!*Inst {
783 fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!TypedValue {
784 const new_inst = try self.analyzeInst(scope, old_inst);
785 return TypedValue{
786 .ty = new_inst.ty,
787 .val = try self.resolveConstValue(scope, new_inst),
788 };
789 }
790
791 fn analyzeInst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Inst {
517792 switch (old_inst.tag) {
518 .breakpoint => return self.analyzeInstBreakpoint(block, old_inst.cast(text.Inst.Breakpoint).?),
793 .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(text.Inst.Breakpoint).?),
794 .call => return self.analyzeInstCall(scope, old_inst.cast(text.Inst.Call).?),
519795 .str => {
520796 // We can use this reference because Inst.Const's Value is arena-allocated.
521797 // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends.
......@@ -526,53 +802,118 @@ const Analyze = struct {
526802 const big_int = old_inst.cast(text.Inst.Int).?.positionals.int;
527803 return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int);
528804 },
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).?),
805 .ptrtoint => return self.analyzeInstPtrToInt(scope, old_inst.cast(text.Inst.PtrToInt).?),
806 .fieldptr => return self.analyzeInstFieldPtr(scope, old_inst.cast(text.Inst.FieldPtr).?),
807 .deref => return self.analyzeInstDeref(scope, old_inst.cast(text.Inst.Deref).?),
808 .as => return self.analyzeInstAs(scope, old_inst.cast(text.Inst.As).?),
809 .@"asm" => return self.analyzeInstAsm(scope, old_inst.cast(text.Inst.Asm).?),
810 .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.cast(text.Inst.Unreachable).?),
811 .@"return" => return self.analyzeInstRet(scope, old_inst.cast(text.Inst.Return).?),
812 // TODO postpone function analysis until later
813 .@"fn" => return self.analyzeInstFn(scope, old_inst.cast(text.Inst.Fn).?),
537814 .@"export" => {
538 try self.analyzeExport(block, old_inst.cast(text.Inst.Export).?);
815 try self.analyzeExport(scope, old_inst.cast(text.Inst.Export).?);
539816 return self.constVoid(old_inst.src);
540817 },
541818 .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).?),
819 .fntype => return self.analyzeInstFnType(scope, old_inst.cast(text.Inst.FnType).?),
820 .intcast => return self.analyzeInstIntCast(scope, old_inst.cast(text.Inst.IntCast).?),
821 .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(text.Inst.BitCast).?),
822 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.cast(text.Inst.ElemPtr).?),
823 .add => return self.analyzeInstAdd(scope, old_inst.cast(text.Inst.Add).?),
824 .cmp => return self.analyzeInstCmp(scope, old_inst.cast(text.Inst.Cmp).?),
825 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(text.Inst.CondBr).?),
826 .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(text.Inst.IsNull).?),
827 .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.cast(text.Inst.IsNonNull).?),
551828 }
552829 }
553830
554 fn analyzeInstBreakpoint(self: *Analyze, block: ?*Block, inst: *text.Inst.Breakpoint) InnerError!*Inst {
555 const b = try self.requireRuntimeBlock(block, inst.base.src);
831 fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *text.Inst.Breakpoint) InnerError!*Inst {
832 const b = try self.requireRuntimeBlock(scope, inst.base.src);
556833 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});
557834 }
558835
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);
836 fn analyzeInstCall(self: *Module, scope: *Scope, inst: *text.Inst.Call) InnerError!*Inst {
837 const func = try self.resolveInst(scope, inst.positionals.func);
838 if (func.ty.zigTypeTag() != .Fn)
839 return self.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty});
840
841 const cc = func.ty.fnCallingConvention();
842 if (cc == .Naked) {
843 // TODO add error note: declared here
844 return self.fail(
845 scope,
846 inst.positionals.func.src,
847 "unable to call function with naked calling convention",
848 .{},
849 );
850 }
851 const call_params_len = inst.positionals.args.len;
852 const fn_params_len = func.ty.fnParamLen();
853 if (func.ty.fnIsVarArgs()) {
854 if (call_params_len < fn_params_len) {
855 // TODO add error note: declared here
856 return self.fail(
857 scope,
858 inst.positionals.func.src,
859 "expected at least {} arguments, found {}",
860 .{ fn_params_len, call_params_len },
861 );
862 }
863 return self.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{});
864 } else if (fn_params_len != call_params_len) {
865 // TODO add error note: declared here
866 return self.fail(
867 scope,
868 inst.positionals.func.src,
869 "expected {} arguments, found {}",
870 .{ fn_params_len, call_params_len },
871 );
872 }
873
874 if (inst.kw_args.modifier == .compile_time) {
875 return self.fail(scope, inst.base.src, "TODO implement comptime function calls", .{});
876 }
877 if (inst.kw_args.modifier != .auto) {
878 return self.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier});
879 }
880
881 // TODO handle function calls of generic functions
882
883 const fn_param_types = try self.allocator.alloc(Type, fn_params_len);
884 defer self.allocator.free(fn_param_types);
885 func.ty.fnParamTypes(fn_param_types);
886
887 const casted_args = try self.arena.allocator.alloc(*Inst, fn_params_len);
888 for (inst.positionals.args) |src_arg, i| {
889 const uncasted_arg = try self.resolveInst(scope, src_arg);
890 casted_args[i] = try self.coerce(scope, fn_param_types[i], uncasted_arg);
891 }
892
893 const b = try self.requireRuntimeBlock(scope, inst.base.src);
894 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, Inst.Args(Inst.Call){
895 .func = func,
896 .args = casted_args,
897 });
898 }
899
900 fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *text.Inst.Fn) InnerError!*Inst {
901 const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type);
561902
562903 var new_func: Fn = .{
563904 .fn_index = self.fns.items.len,
564905 .inner_block = .{
565906 .func = undefined,
566 .instructions = std.ArrayList(*Inst).init(self.allocator),
907 .instructions = .{},
567908 },
568 .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
909 .inst_table = std.AutoHashMap(*text.Inst, ?*Inst).init(self.allocator),
569910 };
570911 new_func.inner_block.func = &new_func;
571912 defer new_func.inner_block.instructions.deinit();
572913 defer new_func.inst_table.deinit();
573914 // Don't hang on to a reference to this when analyzing body instructions, since the memory
574915 // could become invalid.
575 (try self.fns.addOne()).* = .{
916 (try self.fns.addOne(self.allocator)).* = .{
576917 .analysis_status = .in_progress,
577918 .fn_type = fn_type,
578919 .body = undefined,
......@@ -593,8 +934,15 @@ const Analyze = struct {
593934 });
594935 }
595936
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);
937 fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *text.Inst.FnType) InnerError!*Inst {
938 const return_type = try self.resolveType(scope, fntype.positionals.return_type);
939
940 if (return_type.zigTypeTag() == .NoReturn and
941 fntype.positionals.param_types.len == 0 and
942 fntype.kw_args.cc == .Unspecified)
943 {
944 return self.constType(fntype.base.src, Type.initTag(.fn_noreturn_no_args));
945 }
598946
599947 if (return_type.zigTypeTag() == .NoReturn and
600948 fntype.positionals.param_types.len == 0 and
......@@ -610,37 +958,37 @@ const Analyze = struct {
610958 return self.constType(fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
611959 }
612960
613 return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{});
961 return self.fail(scope, fntype.base.src, "TODO implement fntype instruction more", .{});
614962 }
615963
616 fn analyzeInstPrimitive(self: *Analyze, primitive: *text.Inst.Primitive) InnerError!*Inst {
964 fn analyzeInstPrimitive(self: *Module, primitive: *text.Inst.Primitive) InnerError!*Inst {
617965 return self.constType(primitive.base.src, primitive.positionals.tag.toType());
618966 }
619967
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);
968 fn analyzeInstAs(self: *Module, scope: *Scope, as: *text.Inst.As) InnerError!*Inst {
969 const dest_type = try self.resolveType(scope, as.positionals.dest_type);
970 const new_inst = try self.resolveInst(scope, as.positionals.value);
971 return self.coerce(scope, dest_type, new_inst);
624972 }
625973
626 fn analyzeInstPtrToInt(self: *Analyze, block: ?*Block, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
627 const ptr = try self.resolveInst(block, ptrtoint.positionals.ptr);
974 fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
975 const ptr = try self.resolveInst(scope, ptrtoint.positionals.ptr);
628976 if (ptr.ty.zigTypeTag() != .Pointer) {
629 return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
977 return self.fail(scope, ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
630978 }
631979 // TODO handle known-pointer-address
632 const b = try self.requireRuntimeBlock(block, ptrtoint.base.src);
980 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);
633981 const ty = Type.initTag(.usize);
634982 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });
635983 }
636984
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);
985 fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst {
986 const object_ptr = try self.resolveInst(scope, fieldptr.positionals.object_ptr);
987 const field_name = try self.resolveConstString(scope, fieldptr.positionals.field_name);
640988
641989 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
642990 .Pointer => object_ptr.ty.elemType(),
643 else => return self.fail(fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
991 else => return self.fail(scope, fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
644992 };
645993 switch (elem_ty.zigTypeTag()) {
646994 .Array => {
......@@ -657,24 +1005,26 @@ const Analyze = struct {
6571005 });
6581006 } else {
6591007 return self.fail(
1008 scope,
6601009 fieldptr.positionals.field_name.src,
6611010 "no member named '{}' in '{}'",
6621011 .{ field_name, elem_ty },
6631012 );
6641013 }
6651014 },
666 else => return self.fail(fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}),
1015 else => return self.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}),
6671016 }
6681017 }
6691018
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);
1019 fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *text.Inst.IntCast) InnerError!*Inst {
1020 const dest_type = try self.resolveType(scope, intcast.positionals.dest_type);
1021 const new_inst = try self.resolveInst(scope, intcast.positionals.value);
6731022
6741023 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
6751024 .ComptimeInt => true,
6761025 .Int => false,
6771026 else => return self.fail(
1027 scope,
6781028 intcast.positionals.dest_type.src,
6791029 "expected integer type, found '{}'",
6801030 .{
......@@ -686,6 +1036,7 @@ const Analyze = struct {
6861036 switch (new_inst.ty.zigTypeTag()) {
6871037 .ComptimeInt, .Int => {},
6881038 else => return self.fail(
1039 scope,
6891040 intcast.positionals.value.src,
6901041 "expected integer type, found '{}'",
6911042 .{new_inst.ty},
......@@ -693,22 +1044,22 @@ const Analyze = struct {
6931044 }
6941045
6951046 if (dest_is_comptime_int or new_inst.value() != null) {
696 return self.coerce(block, dest_type, new_inst);
1047 return self.coerce(scope, dest_type, new_inst);
6971048 }
6981049
699 return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});
1050 return self.fail(scope, intcast.base.src, "TODO implement analyze widen or shorten int", .{});
7001051 }
7011052
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);
1053 fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *text.Inst.BitCast) InnerError!*Inst {
1054 const dest_type = try self.resolveType(scope, inst.positionals.dest_type);
1055 const operand = try self.resolveInst(scope, inst.positionals.operand);
1056 return self.bitcast(scope, dest_type, operand);
7061057 }
7071058
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);
1059 fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *text.Inst.ElemPtr) InnerError!*Inst {
1060 const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr);
1061 const uncasted_index = try self.resolveInst(scope, inst.positionals.index);
1062 const elem_index = try self.coerce(scope, Type.initTag(.usize), uncasted_index);
7121063
7131064 if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {
7141065 if (array_ptr.value()) |array_ptr_val| {
......@@ -717,28 +1068,25 @@ const Analyze = struct {
7171068 const index_u64 = index_val.toUnsignedInt();
7181069 // @intCast here because it would have been impossible to construct a value that
7191070 // 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 };
1071 const elem_ptr = try array_ptr_val.elemPtr(&self.arena.allocator, @intCast(usize, index_u64));
7241072
7251073 const type_payload = try self.arena.allocator.create(Type.Payload.SingleConstPointer);
7261074 type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() };
7271075
7281076 return self.constInst(inst.base.src, .{
7291077 .ty = Type.initPayload(&type_payload.base),
730 .val = Value.initPayload(&ref_payload.base),
1078 .val = elem_ptr,
7311079 });
7321080 }
7331081 }
7341082 }
7351083
736 return self.fail(inst.base.src, "TODO implement more analyze elemptr", .{});
1084 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
7371085 }
7381086
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);
1087 fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *text.Inst.Add) InnerError!*Inst {
1088 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
1089 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
7421090
7431091 if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) {
7441092 if (lhs.value()) |lhs_val| {
......@@ -758,7 +1106,7 @@ const Analyze = struct {
7581106 const result_limbs = result_bigint.limbs[0..result_bigint.len];
7591107
7601108 if (!lhs.ty.eql(rhs.ty)) {
761 return self.fail(inst.base.src, "TODO implement peer type resolution", .{});
1109 return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{});
7621110 }
7631111
7641112 const val_payload = if (result_bigint.positive) blk: {
......@@ -779,14 +1127,14 @@ const Analyze = struct {
7791127 }
7801128 }
7811129
782 return self.fail(inst.base.src, "TODO implement more analyze add", .{});
1130 return self.fail(scope, inst.base.src, "TODO implement more analyze add", .{});
7831131 }
7841132
785 fn analyzeInstDeref(self: *Analyze, block: ?*Block, deref: *text.Inst.Deref) InnerError!*Inst {
786 const ptr = try self.resolveInst(block, deref.positionals.ptr);
1133 fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *text.Inst.Deref) InnerError!*Inst {
1134 const ptr = try self.resolveInst(scope, deref.positionals.ptr);
7871135 const elem_ty = switch (ptr.ty.zigTypeTag()) {
7881136 .Pointer => ptr.ty.elemType(),
789 else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}),
1137 else => return self.fail(scope, deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}),
7901138 };
7911139 if (ptr.value()) |val| {
7921140 return self.constInst(deref.base.src, .{
......@@ -795,30 +1143,30 @@ const Analyze = struct {
7951143 });
7961144 }
7971145
798 return self.fail(deref.base.src, "TODO implement runtime deref", .{});
1146 return self.fail(scope, deref.base.src, "TODO implement runtime deref", .{});
7991147 }
8001148
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;
1149 fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *text.Inst.Asm) InnerError!*Inst {
1150 const return_type = try self.resolveType(scope, assembly.positionals.return_type);
1151 const asm_source = try self.resolveConstString(scope, assembly.positionals.asm_source);
1152 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(scope, o) else null;
8051153
8061154 const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len);
8071155 const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len);
8081156 const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len);
8091157
8101158 for (inputs) |*elem, i| {
811 elem.* = try self.resolveConstString(block, assembly.kw_args.inputs[i]);
1159 elem.* = try self.resolveConstString(scope, assembly.kw_args.inputs[i]);
8121160 }
8131161 for (clobbers) |*elem, i| {
814 elem.* = try self.resolveConstString(block, assembly.kw_args.clobbers[i]);
1162 elem.* = try self.resolveConstString(scope, assembly.kw_args.clobbers[i]);
8151163 }
8161164 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);
1165 const arg = try self.resolveInst(scope, assembly.kw_args.args[i]);
1166 elem.* = try self.coerce(scope, Type.initTag(.usize), arg);
8191167 }
8201168
821 const b = try self.requireRuntimeBlock(block, assembly.base.src);
1169 const b = try self.requireRuntimeBlock(scope, assembly.base.src);
8221170 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
8231171 .asm_source = asm_source,
8241172 .is_volatile = assembly.kw_args.@"volatile",
......@@ -829,9 +1177,9 @@ const Analyze = struct {
8291177 });
8301178 }
8311179
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);
1180 fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *text.Inst.Cmp) InnerError!*Inst {
1181 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
1182 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
8351183 const op = inst.positionals.op;
8361184
8371185 const is_equality_cmp = switch (op) {
......@@ -853,7 +1201,7 @@ const Analyze = struct {
8531201 const is_null = opt_val.isNull();
8541202 return self.constBool(inst.base.src, if (op == .eq) is_null else !is_null);
8551203 }
856 const b = try self.requireRuntimeBlock(block, inst.base.src);
1204 const b = try self.requireRuntimeBlock(scope, inst.base.src);
8571205 switch (op) {
8581206 .eq => return self.addNewInstArgs(
8591207 b,
......@@ -874,64 +1222,64 @@ const Analyze = struct {
8741222 } else if (is_equality_cmp and
8751223 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
8761224 {
877 return self.fail(inst.base.src, "TODO implement C pointer cmp", .{});
1225 return self.fail(scope, inst.base.src, "TODO implement C pointer cmp", .{});
8781226 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
8791227 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});
1228 return self.fail(scope, inst.base.src, "comparison of '{}' with null", .{non_null_type});
8811229 } else if (is_equality_cmp and
8821230 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
8831231 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
8841232 {
885 return self.fail(inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
1233 return self.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
8861234 } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
8871235 if (!is_equality_cmp) {
888 return self.fail(inst.base.src, "{} operator not allowed for errors", .{@tagName(op)});
1236 return self.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)});
8891237 }
890 return self.fail(inst.base.src, "TODO implement equality comparison between errors", .{});
1238 return self.fail(scope, inst.base.src, "TODO implement equality comparison between errors", .{});
8911239 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
8921240 // This operation allows any combination of integer and float types, regardless of the
8931241 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
8941242 // numeric types.
895 return self.cmpNumeric(block, inst.base.src, lhs, rhs, op);
1243 return self.cmpNumeric(scope, inst.base.src, lhs, rhs, op);
8961244 }
897 return self.fail(inst.base.src, "TODO implement more cmp analysis", .{});
1245 return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
8981246 }
8991247
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);
1248 fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *text.Inst.IsNull) InnerError!*Inst {
1249 const operand = try self.resolveInst(scope, inst.positionals.operand);
1250 return self.analyzeIsNull(scope, inst.base.src, operand, true);
9031251 }
9041252
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);
1253 fn analyzeInstIsNonNull(self: *Module, scope: *Scope, inst: *text.Inst.IsNonNull) InnerError!*Inst {
1254 const operand = try self.resolveInst(scope, inst.positionals.operand);
1255 return self.analyzeIsNull(scope, inst.base.src, operand, false);
9081256 }
9091257
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);
1258 fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *text.Inst.CondBr) InnerError!*Inst {
1259 const uncasted_cond = try self.resolveInst(scope, inst.positionals.condition);
1260 const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond);
9131261
9141262 if (try self.resolveDefinedValue(cond)) |cond_val| {
9151263 const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body;
916 try self.analyzeBody(block, body.*);
1264 try self.analyzeBody(scope, body.*);
9171265 return self.constVoid(inst.base.src);
9181266 }
9191267
920 const parent_block = try self.requireRuntimeBlock(block, inst.base.src);
1268 const parent_block = try self.requireRuntimeBlock(scope, inst.base.src);
9211269
922 var true_block: Block = .{
1270 var true_block: Scope.Block = .{
9231271 .func = parent_block.func,
924 .instructions = std.ArrayList(*Inst).init(self.allocator),
1272 .instructions = .{},
9251273 };
9261274 defer true_block.instructions.deinit();
927 try self.analyzeBody(&true_block, inst.positionals.true_body);
1275 try self.analyzeBody(&true_block.base, inst.positionals.true_body);
9281276
929 var false_block: Block = .{
1277 var false_block: Scope.Block = .{
9301278 .func = parent_block.func,
931 .instructions = std.ArrayList(*Inst).init(self.allocator),
1279 .instructions = .{},
9321280 };
9331281 defer false_block.instructions.deinit();
934 try self.analyzeBody(&false_block, inst.positionals.false_body);
1282 try self.analyzeBody(&false_block.base, inst.positionals.false_body);
9351283
9361284 // Copy the instruction pointers to the arena memory
9371285 const true_instructions = try self.arena.allocator.alloc(*Inst, true_block.instructions.items.len);
......@@ -947,7 +1295,7 @@ const Analyze = struct {
9471295 });
9481296 }
9491297
950 fn wantSafety(self: *Analyze, block: ?*Block) bool {
1298 fn wantSafety(self: *Module, scope: *Scope) bool {
9511299 return switch (self.optimize_mode) {
9521300 .Debug => true,
9531301 .ReleaseSafe => true,
......@@ -956,47 +1304,47 @@ const Analyze = struct {
9561304 };
9571305 }
9581306
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)) {
1307 fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *text.Inst.Unreachable) InnerError!*Inst {
1308 const b = try self.requireRuntimeBlock(scope, unreach.base.src);
1309 if (self.wantSafety(scope)) {
9621310 // TODO Once we have a panic function to call, call it here instead of this.
9631311 _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {});
9641312 }
9651313 return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
9661314 }
9671315
968 fn analyzeInstRet(self: *Analyze, block: ?*Block, inst: *text.Inst.Return) InnerError!*Inst {
969 const b = try self.requireRuntimeBlock(block, inst.base.src);
1316 fn analyzeInstRet(self: *Module, scope: *Scope, inst: *text.Inst.Return) InnerError!*Inst {
1317 const b = try self.requireRuntimeBlock(scope, inst.base.src);
9701318 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {});
9711319 }
9721320
973 fn analyzeBody(self: *Analyze, block: ?*Block, body: text.Module.Body) !void {
1321 fn analyzeBody(self: *Module, scope: *Scope, body: text.Module.Body) !void {
9741322 for (body.instructions) |src_inst| {
975 const new_inst = self.analyzeInst(block, src_inst) catch |err| {
976 if (block) |b| {
1323 const new_inst = self.analyzeInst(scope, src_inst) catch |err| {
1324 if (scope.cast(Scope.Block)) |b| {
9771325 self.fns.items[b.func.fn_index].analysis_status = .failure;
9781326 try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = null });
9791327 }
9801328 return err;
9811329 };
982 if (block) |b| try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
1330 if (scope.cast(Scope.Block)) |b| try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
9831331 }
9841332 }
9851333
9861334 fn analyzeIsNull(
987 self: *Analyze,
988 block: ?*Block,
1335 self: *Module,
1336 scope: *Scope,
9891337 src: usize,
9901338 operand: *Inst,
9911339 invert_logic: bool,
9921340 ) InnerError!*Inst {
993 return self.fail(src, "TODO implement analysis of isnull and isnotnull", .{});
1341 return self.fail(scope, src, "TODO implement analysis of isnull and isnotnull", .{});
9941342 }
9951343
9961344 /// Asserts that lhs and rhs types are both numeric.
9971345 fn cmpNumeric(
998 self: *Analyze,
999 block: ?*Block,
1346 self: *Module,
1347 scope: *Scope,
10001348 src: usize,
10011349 lhs: *Inst,
10021350 rhs: *Inst,
......@@ -1010,14 +1358,14 @@ const Analyze = struct {
10101358
10111359 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
10121360 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
1013 return self.fail(src, "vector length mismatch: {} and {}", .{
1361 return self.fail(scope, src, "vector length mismatch: {} and {}", .{
10141362 lhs.ty.arrayLen(),
10151363 rhs.ty.arrayLen(),
10161364 });
10171365 }
1018 return self.fail(src, "TODO implement support for vectors in cmpNumeric", .{});
1366 return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{});
10191367 } 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 '{}'", .{
1368 return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
10211369 lhs.ty,
10221370 rhs.ty,
10231371 });
......@@ -1036,7 +1384,7 @@ const Analyze = struct {
10361384 // of this function if we don't need to.
10371385
10381386 // It must be a runtime comparison.
1039 const b = try self.requireRuntimeBlock(block, src);
1387 const b = try self.requireRuntimeBlock(scope, src);
10401388 // For floats, emit a float comparison instruction.
10411389 const lhs_is_float = switch (lhs_ty_tag) {
10421390 .Float, .ComptimeFloat => true,
......@@ -1054,14 +1402,14 @@ const Analyze = struct {
10541402 } else if (rhs_ty_tag == .ComptimeFloat) {
10551403 break :x lhs.ty;
10561404 }
1057 if (lhs.ty.floatBits(self.target) >= rhs.ty.floatBits(self.target)) {
1405 if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) {
10581406 break :x lhs.ty;
10591407 } else {
10601408 break :x rhs.ty;
10611409 }
10621410 };
1063 const casted_lhs = try self.coerce(block, dest_type, lhs);
1064 const casted_rhs = try self.coerce(block, dest_type, rhs);
1411 const casted_lhs = try self.coerce(scope, dest_type, lhs);
1412 const casted_rhs = try self.coerce(scope, dest_type, rhs);
10651413 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
10661414 .lhs = casted_lhs,
10671415 .rhs = casted_rhs,
......@@ -1117,7 +1465,7 @@ const Analyze = struct {
11171465 } else if (lhs_is_float) {
11181466 dest_float_type = lhs.ty;
11191467 } else {
1120 const int_info = lhs.ty.intInfo(self.target);
1468 const int_info = lhs.ty.intInfo(self.target());
11211469 lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
11221470 }
11231471
......@@ -1152,19 +1500,19 @@ const Analyze = struct {
11521500 } else if (rhs_is_float) {
11531501 dest_float_type = rhs.ty;
11541502 } else {
1155 const int_info = rhs.ty.intInfo(self.target);
1503 const int_info = rhs.ty.intInfo(self.target());
11561504 rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
11571505 }
11581506
11591507 const dest_type = if (dest_float_type) |ft| ft else blk: {
11601508 const max_bits = std.math.max(lhs_bits, rhs_bits);
11611509 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}),
1510 error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}),
11631511 };
11641512 break :blk try self.makeIntType(dest_int_is_signed, casted_bits);
11651513 };
1166 const casted_lhs = try self.coerce(block, dest_type, lhs);
1167 const casted_rhs = try self.coerce(block, dest_type, lhs);
1514 const casted_lhs = try self.coerce(scope, dest_type, lhs);
1515 const casted_rhs = try self.coerce(scope, dest_type, lhs);
11681516
11691517 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
11701518 .lhs = casted_lhs,
......@@ -1173,7 +1521,7 @@ const Analyze = struct {
11731521 });
11741522 }
11751523
1176 fn makeIntType(self: *Analyze, signed: bool, bits: u16) !Type {
1524 fn makeIntType(self: *Module, signed: bool, bits: u16) !Type {
11771525 if (signed) {
11781526 const int_payload = try self.arena.allocator.create(Type.Payload.IntSigned);
11791527 int_payload.* = .{ .bits = bits };
......@@ -1185,14 +1533,14 @@ const Analyze = struct {
11851533 }
11861534 }
11871535
1188 fn coerce(self: *Analyze, block: ?*Block, dest_type: Type, inst: *Inst) !*Inst {
1536 fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
11891537 // If the types are the same, we can return the operand.
11901538 if (dest_type.eql(inst.ty))
11911539 return inst;
11921540
11931541 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
11941542 if (in_memory_result == .ok) {
1195 return self.bitcast(block, dest_type, inst);
1543 return self.bitcast(scope, dest_type, inst);
11961544 }
11971545
11981546 // *[N]T to []T
......@@ -1212,55 +1560,61 @@ const Analyze = struct {
12121560 if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) {
12131561 // The representation is already correct; we only need to make sure it fits in the destination type.
12141562 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 });
1563 if (!val.intFitsInType(dest_type, self.target())) {
1564 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
12171565 }
12181566 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
12191567 }
12201568
12211569 // integer widening
12221570 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);
1571 const src_info = inst.ty.intInfo(self.target());
1572 const dst_info = dest_type.intInfo(self.target());
12251573 if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) {
12261574 if (inst.value()) |val| {
12271575 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
12281576 } else {
1229 return self.fail(inst.src, "TODO implement runtime integer widening", .{});
1577 return self.fail(scope, inst.src, "TODO implement runtime integer widening", .{});
12301578 }
12311579 } else {
1232 return self.fail(inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type });
1580 return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type });
12331581 }
12341582 }
12351583
1236 return self.fail(inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
1584 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
12371585 }
12381586
1239 fn bitcast(self: *Analyze, block: ?*Block, dest_type: Type, inst: *Inst) !*Inst {
1587 fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
12401588 if (inst.value()) |val| {
12411589 // Keep the comptime Value representation; take the new type.
12421590 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
12431591 }
12441592 // TODO validate the type size and other compile errors
1245 const b = try self.requireRuntimeBlock(block, inst.src);
1593 const b = try self.requireRuntimeBlock(scope, inst.src);
12461594 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
12471595 }
12481596
1249 fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
1597 fn coerceArrayPtrToSlice(self: *Module, dest_type: Type, inst: *Inst) !*Inst {
12501598 if (inst.value()) |val| {
12511599 // The comptime Value representation is compatible with both types.
12521600 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
12531601 }
1254 return self.fail(inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
1602 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
12551603 }
12561604
1257 fn fail(self: *Analyze, src: usize, comptime format: []const u8, args: var) InnerError {
1605 fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: var) InnerError {
12581606 @setCold(true);
1259 const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args);
1260 (try self.errors.addOne()).* = .{
1607 const err_msg = ErrorMsg{
12611608 .byte_offset = src,
1262 .msg = msg,
1609 .msg = try std.fmt.allocPrint(self.allocator, format, args),
12631610 };
1611 if (scope.cast(Scope.Block)) |block| {
1612 block.func.analysis = .{ .failure = err_msg };
1613 } else if (scope.cast(Scope.Decl)) |scope_decl| {
1614 scope_decl.decl.analysis = .{ .failure = err_msg };
1615 } else {
1616 unreachable;
1617 }
12641618 return error.AnalysisFail;
12651619 }
12661620
......@@ -1279,6 +1633,11 @@ const Analyze = struct {
12791633 }
12801634};
12811635
1636pub const ErrorMsg = struct {
1637 byte_offset: usize,
1638 msg: []const u8,
1639};
1640
12821641pub fn main() anyerror!void {
12831642 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
12841643 defer arena.deinit();
......@@ -1288,63 +1647,68 @@ pub fn main() anyerror!void {
12881647 defer std.process.argsFree(allocator, args);
12891648
12901649 const src_path = args[1];
1650 const bin_path = args[2];
12911651 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 }
1652 const output_zir = true;
13071653
13081654 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
13091655
1310 var analyzed_module = try analyze(allocator, zir_module, .{
1656 var bin_file = try link.openBinFilePath(allocator, std.fs.cwd(), bin_path, .{
13111657 .target = native_info.target,
1312 .output_mode = .Obj,
1658 .output_mode = .Exe,
13131659 .link_mode = .Static,
1314 .optimize_mode = .Debug,
1660 .object_format = options.object_format orelse native_info.target.getObjectFormat(),
13151661 });
1316 defer analyzed_module.deinit(allocator);
1662 defer bin_file.deinit(allocator);
1663
1664 var module = blk: {
1665 const root_pkg = try Package.create(allocator, std.fs.cwd(), ".", src_path);
1666 errdefer root_pkg.destroy();
1667
1668 const root_scope = try allocator.create(Module.Scope.ZIRModule);
1669 errdefer allocator.destroy(root_scope);
1670 root_scope.* = .{
1671 .sub_file_path = root_pkg.root_src_path,
1672 .contents = .unloaded,
1673 };
13171674
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 });
1675 break :blk Module{
1676 .allocator = allocator,
1677 .root_pkg = root_pkg,
1678 .root_scope = root_scope,
1679 .bin_file = &bin_file,
1680 .optimize_mode = .Debug,
1681 .decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(allocator),
1682 };
1683 };
1684 defer module.deinit();
1685
1686 try module.update();
1687
1688 const errors = try module.getAllErrorsAlloc();
1689 defer errors.deinit();
1690
1691 if (errors.list.len != 0) {
1692 for (errors.list) |full_err_msg| {
1693 std.debug.warn("{}:{}:{}: error: {}\n", .{
1694 full_err_msg.src_path,
1695 full_err_msg.line + 1,
1696 full_err_msg.column + 1,
1697 full_err_msg.msg,
1698 });
13221699 }
13231700 if (debug_error_trace) return error.AnalysisFail;
13241701 std.process.exit(1);
13251702 }
13261703
1327 const output_zir = true;
13281704 if (output_zir) {
1329 var new_zir_module = try text.emit_zir(allocator, analyzed_module);
1705 var new_zir_module = try text.emit_zir(allocator, module);
13301706 defer new_zir_module.deinit(allocator);
13311707
13321708 var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream());
13331709 try new_zir_module.writeToStream(allocator, bos.outStream());
13341710 try bos.flush();
13351711 }
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 }
13481712}
13491713
13501714// Performance optimization ideas:
src-self-hosted/ir/text.zig+125-57
......@@ -16,10 +16,16 @@ pub const Inst = struct {
1616 tag: Tag,
1717 /// Byte offset into the source.
1818 src: usize,
19 name: []const u8,
1920
2021 /// These names are used directly as the instruction names in the text format.
2122 pub const Tag = enum {
2223 breakpoint,
24 call,
25 /// Represents a reference to a global decl by name.
26 /// Canonicalized ZIR will not have any of these. The
27 /// syntax `@foo` is equivalent to `declref("foo")`.
28 declref,
2329 str,
2430 int,
2531 ptrtoint,
......@@ -46,6 +52,8 @@ pub const Inst = struct {
4652 pub fn TagToType(tag: Tag) type {
4753 return switch (tag) {
4854 .breakpoint => Breakpoint,
55 .call => Call,
56 .declref => DeclRef,
4957 .str => Str,
5058 .int => Int,
5159 .ptrtoint => PtrToInt,
......@@ -85,6 +93,29 @@ pub const Inst = struct {
8593 kw_args: struct {},
8694 };
8795
96 pub const Call = struct {
97 pub const base_tag = Tag.call;
98 base: Inst,
99
100 positionals: struct {
101 func: *Inst,
102 args: []*Inst,
103 },
104 kw_args: struct {
105 modifier: std.builtin.CallOptions.Modifier = .auto,
106 },
107 };
108
109 pub const DeclRef = struct {
110 pub const base_tag = Tag.declref;
111 base: Inst,
112
113 positionals: struct {
114 name: *Inst,
115 },
116 kw_args: struct {},
117 };
118
88119 pub const Str = struct {
89120 pub const base_tag = Tag.str;
90121 base: Inst,
......@@ -212,55 +243,55 @@ pub const Inst = struct {
212243 kw_args: struct {},
213244
214245 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",
246 isize,
247 usize,
248 c_short,
249 c_ushort,
250 c_int,
251 c_uint,
252 c_long,
253 c_ulong,
254 c_longlong,
255 c_ulonglong,
256 c_longdouble,
257 c_void,
258 f16,
259 f32,
260 f64,
261 f128,
262 bool,
263 void,
264 noreturn,
265 type,
266 anyerror,
267 comptime_int,
268 comptime_float,
238269
239270 fn toType(self: BuiltinType) Type {
240271 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"),
272 .isize => Type.initTag(.isize),
273 .usize => Type.initTag(.usize),
274 .c_short => Type.initTag(.c_short),
275 .c_ushort => Type.initTag(.c_ushort),
276 .c_int => Type.initTag(.c_int),
277 .c_uint => Type.initTag(.c_uint),
278 .c_long => Type.initTag(.c_long),
279 .c_ulong => Type.initTag(.c_ulong),
280 .c_longlong => Type.initTag(.c_longlong),
281 .c_ulonglong => Type.initTag(.c_ulonglong),
282 .c_longdouble => Type.initTag(.c_longdouble),
283 .c_void => Type.initTag(.c_void),
284 .f16 => Type.initTag(.f16),
285 .f32 => Type.initTag(.f32),
286 .f64 => Type.initTag(.f64),
287 .f128 => Type.initTag(.f128),
288 .bool => Type.initTag(.bool),
289 .void => Type.initTag(.void),
290 .noreturn => Type.initTag(.noreturn),
291 .type => Type.initTag(.type),
292 .anyerror => Type.initTag(.anyerror),
293 .comptime_int => Type.initTag(.comptime_int),
294 .comptime_float => Type.initTag(.comptime_float),
264295 };
265296 }
266297 };
......@@ -376,7 +407,7 @@ pub const ErrorMsg = struct {
376407pub const Module = struct {
377408 decls: []*Inst,
378409 errors: []ErrorMsg,
379 arena: std.heap.ArenaAllocator,
410 arena: std.heap.ArenaAllocator.State,
380411
381412 pub const Body = struct {
382413 instructions: []*Inst,
......@@ -385,7 +416,7 @@ pub const Module = struct {
385416 pub fn deinit(self: *Module, allocator: *Allocator) void {
386417 allocator.free(self.decls);
387418 allocator.free(self.errors);
388 self.arena.deinit();
419 self.arena.promote(allocator).deinit();
389420 self.* = undefined;
390421 }
391422
......@@ -431,6 +462,7 @@ pub const Module = struct {
431462 // TODO I tried implementing this with an inline for loop and hit a compiler bug
432463 switch (decl.tag) {
433464 .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),
465 .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table),
434466 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
435467 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
436468 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
......@@ -543,9 +575,9 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module
543575 .arena = std.heap.ArenaAllocator.init(allocator),
544576 .i = 0,
545577 .source = source,
546 .decls = std.ArrayList(*Inst).init(allocator),
547 .errors = std.ArrayList(ErrorMsg).init(allocator),
548578 .global_name_map = &global_name_map,
579 .errors = .{},
580 .decls = .{},
549581 };
550582 errdefer parser.arena.deinit();
551583
......@@ -555,10 +587,11 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module
555587 },
556588 else => |e| return e,
557589 };
590
558591 return Module{
559 .decls = parser.decls.toOwnedSlice(),
560 .errors = parser.errors.toOwnedSlice(),
561 .arena = parser.arena,
592 .decls = parser.decls.toOwnedSlice(allocator),
593 .errors = parser.errors.toOwnedSlice(allocator),
594 .arena = parser.arena.state,
562595 };
563596}
564597
......@@ -567,8 +600,8 @@ const Parser = struct {
567600 arena: std.heap.ArenaAllocator,
568601 i: usize,
569602 source: [:0]const u8,
570 errors: std.ArrayList(ErrorMsg),
571 decls: std.ArrayList(*Inst),
603 errors: std.ArrayListUnmanaged(ErrorMsg),
604 decls: std.ArrayListUnmanaged(*Inst),
572605 global_name_map: *std.StringHashMap(usize),
573606
574607 const Body = struct {
......@@ -893,8 +926,25 @@ const Parser = struct {
893926 const ident = self.source[name_start..self.i];
894927 const kv = map.get(ident) orelse {
895928 const bad_name = self.source[name_start - 1 .. self.i];
896 self.i = name_start - 1;
897 return self.fail("unrecognized identifier: {}", .{bad_name});
929 const src = name_start - 1;
930 if (local_ref) {
931 self.i = src;
932 return self.fail("unrecognized identifier: {}", .{bad_name});
933 } else {
934 const name = try self.arena.allocator.create(Inst.Str);
935 name.* = .{
936 .base = .{ .src = src, .tag = Inst.Str.base_tag },
937 .positionals = .{ .bytes = ident },
938 .kw_args = .{},
939 };
940 const declref = try self.arena.allocator.create(Inst.DeclRef);
941 declref.* = .{
942 .base = .{ .src = src, .tag = Inst.DeclRef.base_tag },
943 .positionals = .{ .name = &name.base },
944 .kw_args = .{},
945 };
946 return &declref.base;
947 }
898948 };
899949 if (local_ref) {
900950 return body_ctx.?.instructions.items[kv.value];
......@@ -1065,6 +1115,24 @@ const EmitZIR = struct {
10651115 for (body.instructions) |inst| {
10661116 const new_inst = switch (inst.tag) {
10671117 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
1118 .call => blk: {
1119 const old_inst = inst.cast(ir.Inst.Call).?;
1120 const new_inst = try self.arena.allocator.create(Inst.Call);
1121
1122 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1123 for (args) |*elem, i| {
1124 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
1125 }
1126 new_inst.* = .{
1127 .base = .{ .src = inst.src, .tag = Inst.Call.base_tag },
1128 .positionals = .{
1129 .func = try self.resolveInst(inst_table, old_inst.args.func),
1130 .args = args,
1131 },
1132 .kw_args = .{},
1133 };
1134 break :blk &new_inst.base;
1135 },
10681136 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
10691137 .ret => try self.emitTrivial(inst.src, Inst.Return),
10701138 .constant => unreachable, // excluded from function bodies
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+356-224
......@@ -9,50 +9,65 @@ const codegen = @import("codegen.zig");
99
1010const default_entry_addr = 0x8000000;
1111
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 }
12pub const Options = struct {
13 target: std.Target,
14 output_mode: std.builtin.OutputMode,
15 link_mode: std.builtin.LinkMode,
16 object_format: std.builtin.ObjectFormat,
17 /// Used for calculating how much space to reserve for symbols in case the binary file
18 /// does not already have a symbol table.
19 symbol_count_hint: u64 = 32,
20 /// Used for calculating how much space to reserve for executable program code in case
21 /// the binary file deos not already have such a section.
22 program_code_size_hint: u64 = 256 * 1024,
2723};
2824
2925/// Attempts incremental linking, if the file already exists.
3026/// If incremental linking fails, falls back to truncating the file and rewriting it.
3127/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
3228/// This operation is not atomic.
33pub fn updateFilePath(
29pub fn openBinFilePath(
3430 allocator: *Allocator,
35 module: ir.Module,
3631 dir: fs.Dir,
3732 sub_path: []const u8,
38) !Result {
39 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(module) });
33 options: Options,
34) !ElfFile {
35 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) });
4036 defer file.close();
4137
42 return updateFile(allocator, module, file);
38 return openBinFile(allocator, file, options);
4339}
4440
4541/// Atomically overwrites the old file, if present.
4642pub fn writeFilePath(
4743 allocator: *Allocator,
48 module: ir.Module,
4944 dir: fs.Dir,
5045 sub_path: []const u8,
51) !Result {
52 const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(module) });
46 module: ir.Module,
47 errors: *std.ArrayList(ir.ErrorMsg),
48) !void {
49 const options: Options = .{
50 .target = module.target,
51 .output_mode = module.output_mode,
52 .link_mode = module.link_mode,
53 .object_format = module.object_format,
54 .symbol_count_hint = module.decls.items.len,
55 };
56 const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(options) });
5357 defer af.deinit();
5458
55 const result = try writeFile(allocator, module, af.file);
59 const elf_file = try createElfFile(allocator, af.file, options);
60 for (module.decls.items) |decl| {
61 try elf_file.updateDecl(module, decl, errors);
62 }
63 try elf_file.flush();
64 if (elf_file.error_flags.no_entry_point_found) {
65 try errors.ensureCapacity(errors.items.len + 1);
66 errors.appendAssumeCapacity(.{
67 .byte_offset = 0,
68 .msg = try std.fmt.allocPrint(errors.allocator, "no entry point found", .{}),
69 });
70 }
5671 try af.finish();
5772 return result;
5873}
......@@ -62,49 +77,65 @@ pub fn writeFilePath(
6277/// Returns an error if `file` is not already open with +read +write +seek abilities.
6378/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
6479/// 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) {
80pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
81 return openBinFileInner(allocator, file, options) catch |err| switch (err) {
6782 error.IncrFailed => {
68 return writeFile(allocator, module, file);
83 return createElfFile(allocator, file, options);
6984 },
7085 else => |e| return e,
7186 };
7287}
7388
74const Update = struct {
89pub const ElfFile = struct {
90 allocator: *Allocator,
7591 file: fs.File,
76 module: *const ir.Module,
92 options: Options,
93 ptr_width: enum { p32, p64 },
7794
7895 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
7996 /// Same order as in the file.
80 sections: std.ArrayList(elf.Elf64_Shdr),
81 shdr_table_offset: ?u64,
97 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},
98 shdr_table_offset: ?u64 = null,
8299
83100 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
84101 /// Same order as in the file.
85 program_headers: std.ArrayList(elf.Elf64_Phdr),
86 phdr_table_offset: ?u64,
102 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},
103 phdr_table_offset: ?u64 = null,
87104 /// 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,
105 phdr_load_re_index: ?u16 = null,
106 entry_addr: ?u64 = null,
90107
91 shstrtab: std.ArrayList(u8),
92 shstrtab_index: ?u16,
108 shstrtab: std.ArrayListUnmanaged(u8) = .{},
109 shstrtab_index: ?u16 = null,
93110
94 text_section_index: ?u16,
95 symtab_section_index: ?u16,
111 text_section_index: ?u16 = null,
112 symtab_section_index: ?u16 = null,
96113
97114 /// The same order as in the file
98 symbols: std.ArrayList(elf.Elf64_Sym),
115 symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
99116
100 errors: std.ArrayList(ErrorMsg),
117 /// Same order as in the file.
118 offset_table: std.ArrayListUnmanaged(aoeu) = .{},
119
120 /// This means the entire read-only executable program code needs to be rewritten.
121 phdr_load_re_dirty: bool = false,
122 phdr_table_dirty: bool = false,
123 shdr_table_dirty: bool = false,
124 shstrtab_dirty: bool = false,
125 symtab_dirty: bool = false,
126
127 error_flags: ErrorFlags = ErrorFlags{},
101128
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();
129 pub const ErrorFlags = struct {
130 no_entry_point_found: bool = false,
131 };
132
133 pub fn deinit(self: *ElfFile) void {
134 self.sections.deinit(self.allocator);
135 self.program_headers.deinit(self.allocator);
136 self.shstrtab.deinit(self.allocator);
137 self.symbols.deinit(self.allocator);
138 self.offset_table.deinit(self.allocator);
108139 }
109140
110141 // `expand_num / expand_den` is the factor of padding when allocation
......@@ -112,8 +143,8 @@ const Update = struct {
112143 const alloc_den = 3;
113144
114145 /// 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;
146 fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 {
147 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
117148 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
118149 if (start < ehdr_size)
119150 return ehdr_size;
......@@ -157,7 +188,7 @@ const Update = struct {
157188 return null;
158189 }
159190
160 fn allocatedSize(self: *Update, start: u64) u64 {
191 fn allocatedSize(self: *ElfFile, start: u64) u64 {
161192 var min_pos: u64 = std.math.maxInt(u64);
162193 if (self.shdr_table_offset) |off| {
163194 if (off > start and off < min_pos) min_pos = off;
......@@ -176,7 +207,7 @@ const Update = struct {
176207 return min_pos - start;
177208 }
178209
179 fn findFreeSpace(self: *Update, object_size: u64, min_alignment: u16) u64 {
210 fn findFreeSpace(self: *ElfFile, object_size: u64, min_alignment: u16) u64 {
180211 var start: u64 = 0;
181212 while (self.detectAllocCollision(start, object_size)) |item_end| {
182213 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
......@@ -184,33 +215,21 @@ const Update = struct {
184215 return start;
185216 }
186217
187 fn makeString(self: *Update, bytes: []const u8) !u32 {
218 fn makeString(self: *ElfFile, bytes: []const u8) !u32 {
188219 const result = self.shstrtab.items.len;
189220 try self.shstrtab.appendSlice(bytes);
190221 try self.shstrtab.append(0);
191222 return @intCast(u32, result);
192223 }
193224
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) {
225 pub fn populateMissingMetadata(self: *ElfFile) !void {
226 const small_ptr = switch (self.ptr_width) {
201227 .p32 => true,
202228 .p64 => false,
203229 };
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
211230 if (self.phdr_load_re_index == null) {
212231 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
213 const file_size = 256 * 1024;
232 const file_size = self.options.program_code_size_hint;
214233 const p_align = 0x1000;
215234 const off = self.findFreeSpace(file_size, p_align);
216235 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
......@@ -225,24 +244,8 @@ const Update = struct {
225244 .p_flags = elf.PF_X | elf.PF_R,
226245 });
227246 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,
244 });
245 shdr_table_dirty = true;
247 self.phdr_load_re_dirty = true;
248 self.phdr_table_dirty = true;
246249 }
247250 if (self.shstrtab_index == null) {
248251 self.shstrtab_index = @intCast(u16, self.sections.items.len);
......@@ -262,8 +265,8 @@ const Update = struct {
262265 .sh_addralign = 1,
263266 .sh_entsize = 0,
264267 });
265 shstrtab_dirty = true;
266 shdr_table_dirty = true;
268 self.shstrtab_dirty = true;
269 self.shdr_table_dirty = true;
267270 }
268271 if (self.text_section_index == null) {
269272 self.text_section_index = @intCast(u16, self.sections.items.len);
......@@ -281,13 +284,13 @@ const Update = struct {
281284 .sh_addralign = phdr.p_align,
282285 .sh_entsize = 0,
283286 });
284 shdr_table_dirty = true;
287 self.shdr_table_dirty = true;
285288 }
286289 if (self.symtab_section_index == null) {
287290 self.symtab_section_index = @intCast(u16, self.sections.items.len);
288291 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
289292 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;
293 const file_size = self.options.symbol_count_hint * each_size;
291294 const off = self.findFreeSpace(file_size, min_align);
292295 //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
293296
......@@ -300,12 +303,12 @@ const Update = struct {
300303 .sh_size = file_size,
301304 // The section header index of the associated string table.
302305 .sh_link = self.shstrtab_index.?,
303 .sh_info = @intCast(u32, self.module.exports.len),
306 .sh_info = @intCast(u32, self.symbols.items.len),
304307 .sh_addralign = min_align,
305308 .sh_entsize = each_size,
306309 });
307 symtab_dirty = true;
308 shdr_table_dirty = true;
310 self.symtab_dirty = true;
311 self.shdr_table_dirty = true;
309312 }
310313 const shsize: u64 = switch (ptr_width) {
311314 .p32 => @sizeOf(elf.Elf32_Shdr),
......@@ -317,7 +320,7 @@ const Update = struct {
317320 };
318321 if (self.shdr_table_offset == null) {
319322 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
320 shdr_table_dirty = true;
323 self.shdr_table_dirty = true;
321324 }
322325 const phsize: u64 = switch (ptr_width) {
323326 .p32 => @sizeOf(elf.Elf32_Phdr),
......@@ -329,13 +332,15 @@ const Update = struct {
329332 };
330333 if (self.phdr_table_offset == null) {
331334 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
332 phdr_table_dirty = true;
335 self.phdr_table_dirty = true;
333336 }
334 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
337 }
335338
336 try self.writeCodeAndSymbols(phdr_table_dirty, shdr_table_dirty);
339 /// Commit pending changes and write headers.
340 pub fn flush(self: *ElfFile) !void {
341 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
337342
338 if (phdr_table_dirty) {
343 if (self.phdr_table_dirty) {
339344 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
340345 const needed_size = self.program_headers.items.len * phsize;
341346
......@@ -345,7 +350,7 @@ const Update = struct {
345350 }
346351
347352 const allocator = self.program_headers.allocator;
348 switch (ptr_width) {
353 switch (self.ptr_width) {
349354 .p32 => {
350355 const buf = try allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
351356 defer allocator.free(buf);
......@@ -371,11 +376,12 @@ const Update = struct {
371376 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
372377 },
373378 }
379 self.phdr_table_offset = false;
374380 }
375381
376382 {
377383 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
378 if (shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
384 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
379385 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
380386 const needed_size = self.shstrtab.items.len;
381387
......@@ -387,13 +393,14 @@ const Update = struct {
387393 //std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
388394
389395 try self.file.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
390 if (!shdr_table_dirty) {
396 if (!self.shdr_table_dirty) {
391397 // Then it won't get written with the others and we need to do it.
392398 try self.writeSectHeader(self.shstrtab_index.?);
393399 }
400 self.shstrtab_dirty = false;
394401 }
395402 }
396 if (shdr_table_dirty) {
403 if (self.shdr_table_dirty) {
397404 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
398405 const needed_size = self.sections.items.len * phsize;
399406
......@@ -403,7 +410,7 @@ const Update = struct {
403410 }
404411
405412 const allocator = self.sections.allocator;
406 switch (ptr_width) {
413 switch (self.ptr_width) {
407414 .p32 => {
408415 const buf = try allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
409416 defer allocator.free(buf);
......@@ -431,38 +438,36 @@ const Update = struct {
431438 },
432439 }
433440 }
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 });
441 if (self.entry_addr == null and self.options.output_mode == .Exe) {
442 self.error_flags.no_entry_point_found = true;
441443 } else {
444 self.error_flags.no_entry_point_found = false;
442445 try self.writeElfHeader();
443446 }
444447 // TODO find end pos and truncate
448
449 // The point of flush() is to commit changes, so nothing should be dirty after this.
450 assert(!self.phdr_load_re_dirty);
451 assert(!self.phdr_table_dirty);
452 assert(!self.shdr_table_dirty);
453 assert(!self.shstrtab_dirty);
454 assert(!self.symtab_dirty);
445455 }
446456
447 fn writeElfHeader(self: *Update) !void {
457 fn writeElfHeader(self: *ElfFile) !void {
448458 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
449459
450460 var index: usize = 0;
451461 hdr_buf[0..4].* = "\x7fELF".*;
452462 index += 4;
453463
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) {
464 hdr_buf[index] = switch (self.ptr_width) {
460465 .p32 => elf.ELFCLASS32,
461466 .p64 => elf.ELFCLASS64,
462467 };
463468 index += 1;
464469
465 const endian = self.module.target.cpu.arch.endian();
470 const endian = self.options.target.cpu.arch.endian();
466471 hdr_buf[index] = switch (endian) {
467472 .Little => elf.ELFDATA2LSB,
468473 .Big => elf.ELFDATA2MSB,
......@@ -480,10 +485,10 @@ const Update = struct {
480485
481486 assert(index == 16);
482487
483 const elf_type = switch (self.module.output_mode) {
488 const elf_type = switch (self.options.output_mode) {
484489 .Exe => elf.ET.EXEC,
485490 .Obj => elf.ET.REL,
486 .Lib => switch (self.module.link_mode) {
491 .Lib => switch (self.options.link_mode) {
487492 .Static => elf.ET.REL,
488493 .Dynamic => elf.ET.DYN,
489494 },
......@@ -491,7 +496,7 @@ const Update = struct {
491496 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
492497 index += 2;
493498
494 const machine = self.module.target.cpu.arch.toElfMachine();
499 const machine = self.options.target.cpu.arch.toElfMachine();
495500 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
496501 index += 2;
497502
......@@ -501,7 +506,7 @@ const Update = struct {
501506
502507 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
503508
504 switch (ptr_width) {
509 switch (self.ptr_width) {
505510 .p32 => {
506511 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
507512 index += 4;
......@@ -533,14 +538,14 @@ const Update = struct {
533538 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
534539 index += 4;
535540
536 const e_ehsize: u16 = switch (ptr_width) {
541 const e_ehsize: u16 = switch (self.ptr_width) {
537542 .p32 => @sizeOf(elf.Elf32_Ehdr),
538543 .p64 => @sizeOf(elf.Elf64_Ehdr),
539544 };
540545 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
541546 index += 2;
542547
543 const e_phentsize: u16 = switch (ptr_width) {
548 const e_phentsize: u16 = switch (self.ptr_width) {
544549 .p32 => @sizeOf(elf.Elf32_Phdr),
545550 .p64 => @sizeOf(elf.Elf64_Phdr),
546551 };
......@@ -551,7 +556,7 @@ const Update = struct {
551556 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
552557 index += 2;
553558
554 const e_shentsize: u16 = switch (ptr_width) {
559 const e_shentsize: u16 = switch (self.ptr_width) {
555560 .p32 => @sizeOf(elf.Elf32_Shdr),
556561 .p64 => @sizeOf(elf.Elf64_Shdr),
557562 };
......@@ -570,81 +575,172 @@ const Update = struct {
570575 try self.file.pwriteAll(hdr_buf[0..index], 0);
571576 }
572577
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] = .{
577 .st_name = 0,
578 .st_info = 0,
579 .st_other = 0,
580 .st_shndx = 0,
581 .st_value = 0,
582 .st_size = 0,
583 };
578 /// TODO Look into making this smaller to save memory.
579 /// Lots of redundant info here with the data stored in symbol structs.
580 const DeclSymbol = struct {
581 symbol_indexes: []usize,
582 vaddr: u64,
583 file_offset: u64,
584 size: u64,
585 };
584586
587 const AllocatedBlock = struct {
588 vaddr: u64,
589 file_offset: u64,
590 size_capacity: u64,
591 };
592
593 fn allocateDeclSymbol(self: *ElfFile, size: u64) AllocatedBlock {
585594 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;
595 todo();
596 //{
597 // // Now that we know the code size, we need to update the program header for executable code
598 // phdr.p_memsz = vaddr - phdr.p_vaddr;
599 // phdr.p_filesz = phdr.p_memsz;
600
601 // const shdr = &self.sections.items[self.text_section_index.?];
602 // shdr.sh_size = phdr.p_filesz;
603
604 // self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
605 // self.shdr_table_dirty = true; // TODO look into making only the one section dirty
606 //}
607
608 //return self.writeSymbols();
609 }
610
611 fn findAllocatedBlock(self: *ElfFile, vaddr: u64) AllocatedBlock {
612 todo();
613 }
588614
589 var code = std.ArrayList(u8).init(self.sections.allocator);
615 pub fn updateDecl(
616 self: *ElfFile,
617 module: ir.Module,
618 typed_value: ir.TypedValue,
619 decl_export_node: ?*std.LinkedList(std.builtin.ExportOptions).Node,
620 hash: ir.Module.Decl.Hash,
621 err_msg_allocator: *Allocator,
622 ) !?ir.ErrorMsg {
623 var code = std.ArrayList(u8).init(self.allocator);
590624 defer code.deinit();
591625
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 });
626 const err_msg = try codegen.generateSymbol(typed_value, module, &code, err_msg_allocator);
627 if (err_msg != null) |em| return em;
628
629 const export_count = blk: {
630 var export_node = decl_export_node;
631 var i: usize = 0;
632 while (export_node) |node| : (export_node = node.next) i += 1;
633 break :blk i;
634 };
635
636 // Find or create a symbol from the decl
637 var valid_sym_index_len: usize = 0;
638 const decl_symbol = blk: {
639 if (self.decl_table.getValue(hash)) |decl_symbol| {
640 valid_sym_index_len = decl_symbol.symbol_indexes.len;
641 decl_symbol.symbol_indexes = try self.allocator.realloc(usize, export_count);
642
643 const existing_block = self.findAllocatedBlock(decl_symbol.vaddr);
644 if (code.items.len > existing_block.size_capacity) {
645 const new_block = self.allocateDeclSymbol(code.items.len);
646 decl_symbol.vaddr = new_block.vaddr;
647 decl_symbol.file_offset = new_block.file_offset;
648 decl_symbol.size = code.items.len;
604649 }
605 continue;
650 break :blk decl_symbol;
651 } else {
652 const new_block = self.allocateDeclSymbol(code.items.len);
653
654 const decl_symbol = try self.allocator.create(DeclSymbol);
655 errdefer self.allocator.destroy(decl_symbol);
656
657 decl_symbol.* = .{
658 .symbol_indexes = try self.allocator.alloc(usize, export_count),
659 .vaddr = new_block.vaddr,
660 .file_offset = new_block.file_offset,
661 .size = code.items.len,
662 };
663 errdefer self.allocator.free(decl_symbol.symbol_indexes);
664
665 try self.decl_table.put(hash, decl_symbol);
666 break :blk decl_symbol;
667 }
668 };
669
670 // Allocate new symbols.
671 {
672 var i: usize = valid_sym_index_len;
673 const old_len = self.symbols.items.len;
674 try self.symbols.resize(old_len + (decl_symbol.symbol_indexes.len - i));
675 while (i < decl_symbol.symbol_indexes) : (i += 1) {
676 decl_symbol.symbol_indexes[i] = old_len + i;
606677 }
607 try self.file.pwriteAll(code.items, file_off);
678 }
608679
609 if (mem.eql(u8, exp.name, "_start")) {
610 self.entry_addr = vaddr;
680 var export_node = decl_export_node;
681 var export_index: usize = 0;
682 while (export_node) |node| : ({
683 export_node = node.next;
684 export_index += 1;
685 }) {
686 if (node.data.section) |section_name| {
687 if (!mem.eql(u8, section_name, ".text")) {
688 try errors.ensureCapacity(errors.items.len + 1);
689 errors.appendAssumeCapacity(.{
690 .byte_offset = 0,
691 .msg = try std.fmt.allocPrint(errors.allocator, "Unimplemented: ExportOptions.section", .{}),
692 });
693 }
611694 }
612 (try self.symbols.addOne()).* = .{
613 .st_name = try self.makeString(exp.name),
614 .st_info = (elf.STB_LOCAL << 4) | elf.STT_FUNC,
695 const stb_bits = switch (node.data.linkage) {
696 .Internal => elf.STB_LOCAL,
697 .Strong => blk: {
698 if (mem.eql(u8, node.data.name, "_start")) {
699 self.entry_addr = decl_symbol.vaddr;
700 }
701 break :blk elf.STB_GLOBAL;
702 },
703 .Weak => elf.STB_WEAK,
704 .LinkOnce => {
705 try errors.ensureCapacity(errors.items.len + 1);
706 errors.appendAssumeCapacity(.{
707 .byte_offset = 0,
708 .msg = try std.fmt.allocPrint(errors.allocator, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
709 });
710 },
711 };
712 const stt_bits = switch (typed_value.ty.zigTypeTag()) {
713 .Fn => elf.STT_FUNC,
714 else => elf.STT_OBJECT,
715 };
716 const sym_index = decl_symbol.symbol_indexes[export_index];
717 const name = blk: {
718 if (i < valid_sym_index_len) {
719 const name_stroff = self.symbols.items[sym_index].st_name;
720 const existing_name = self.getString(name_stroff);
721 if (mem.eql(u8, existing_name, node.data.name)) {
722 break :blk name_stroff;
723 }
724 }
725 break :blk try self.makeString(node.data.name);
726 };
727 self.symbols.items[sym_index] = .{
728 .st_name = name,
729 .st_info = (stb_bits << 4) | stt_bits,
615730 .st_other = 0,
616731 .st_shndx = self.text_section_index.?,
617 .st_value = vaddr,
732 .st_value = decl_symbol.vaddr,
618733 .st_size = code.items.len,
619734 };
620 vaddr += code.items.len;
621735 }
622736
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;
627
628 const shdr = &self.sections.items[self.text_section_index.?];
629 shdr.sh_size = phdr.p_filesz;
630
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.?);
634 }
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.?);
638 }
639 }
640
641 return self.writeSymbols();
737 try self.file.pwriteAll(code.items, decl_symbol.file_offset);
642738 }
643739
644 fn writeProgHeader(self: *Update, index: usize) !void {
645 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
740 fn writeProgHeader(self: *ElfFile, index: usize) !void {
741 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
646742 const offset = self.program_headers.items[index].p_offset;
647 switch (self.module.target.cpu.arch.ptrBitWidth()) {
743 switch (self.options.target.cpu.arch.ptrBitWidth()) {
648744 32 => {
649745 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
650746 if (foreign_endian) {
......@@ -663,10 +759,10 @@ const Update = struct {
663759 }
664760 }
665761
666 fn writeSectHeader(self: *Update, index: usize) !void {
667 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
762 fn writeSectHeader(self: *ElfFile, index: usize) !void {
763 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
668764 const offset = self.sections.items[index].sh_offset;
669 switch (self.module.target.cpu.arch.ptrBitWidth()) {
765 switch (self.options.target.cpu.arch.ptrBitWidth()) {
670766 32 => {
671767 var shdr: [1]elf.Elf32_Shdr = undefined;
672768 shdr[0] = sectHeaderTo32(self.sections.items[index]);
......@@ -686,13 +782,8 @@ const Update = struct {
686782 }
687783 }
688784
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,
694 };
695 const small_ptr = ptr_width == .p32;
785 fn writeSymbols(self: *ElfFile) !void {
786 const small_ptr = self.ptr_width == .p32;
696787 const syms_sect = &self.sections.items[self.symtab_section_index.?];
697788 const sym_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
698789 const sym_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
......@@ -708,8 +799,8 @@ const Update = struct {
708799 syms_sect.sh_size = needed_size;
709800 syms_sect.sh_info = @intCast(u32, self.symbols.items.len);
710801 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) {
802 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
803 switch (self.ptr_width) {
713804 .p32 => {
714805 const buf = try allocator.alloc(elf.Elf32_Sym, self.symbols.items.len);
715806 defer allocator.free(buf);
......@@ -754,13 +845,13 @@ const Update = struct {
754845
755846/// Truncates the existing file contents and overwrites the contents.
756847/// 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) {
848pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
849 switch (options.output_mode) {
759850 .Exe => {},
760851 .Obj => {},
761852 .Lib => return error.TODOImplementWritingLibFiles,
762853 }
763 switch (module.object_format) {
854 switch (options.object_format) {
764855 .unknown => unreachable, // TODO remove this tag from the enum
765856 .coff => return error.TODOImplementWritingCOFF,
766857 .elf => {},
......@@ -768,38 +859,79 @@ pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Resul
768859 .wasm => return error.TODOImplementWritingWasmObjects,
769860 }
770861
771 var update = Update{
862 var self: ElfFile = .{
863 .allocator = allocator,
772864 .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(),
865 .options = options,
866 .ptr_width = switch (self.options.target.cpu.arch.ptrBitWidth()) {
867 32 => .p32,
868 64 => .p64,
869 else => return error.UnsupportedELFArchitecture,
870 },
871 .symtab_dirty = true,
872 .shdr_table_dirty = true,
794873 };
874 errdefer self.deinit();
875
876 // Index 0 is always a null symbol.
877 try self.symbols.append(allocator, .{
878 .st_name = 0,
879 .st_info = 0,
880 .st_other = 0,
881 .st_shndx = 0,
882 .st_value = 0,
883 .st_size = 0,
884 });
885
886 // There must always be a null section in index 0
887 try self.sections.append(allocator, .{
888 .sh_name = 0,
889 .sh_type = elf.SHT_NULL,
890 .sh_flags = 0,
891 .sh_addr = 0,
892 .sh_offset = 0,
893 .sh_size = 0,
894 .sh_link = 0,
895 .sh_info = 0,
896 .sh_addralign = 0,
897 .sh_entsize = 0,
898 });
899
900 try self.populateMissingMetadata();
901
902 return self;
795903}
796904
797905/// 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;
906fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
907 switch (options.output_mode) {
908 .Exe => {},
909 .Obj => {},
910 .Lib => return error.IncrFailed,
911 }
912 switch (options.object_format) {
913 .unknown => unreachable, // TODO remove this tag from the enum
914 .coff => return error.IncrFailed,
915 .elf => {},
916 .macho => return error.IncrFailed,
917 .wasm => return error.IncrFailed,
918 }
919 var self: ElfFile = .{
920 .allocator = allocator,
921 .file = file,
922 .options = options,
923 .ptr_width = switch (self.options.target.cpu.arch.ptrBitWidth()) {
924 32 => .p32,
925 64 => .p64,
926 else => return error.UnsupportedELFArchitecture,
927 },
928 };
929 errdefer self.deinit();
800930
801 // TODO implement incremental linking
931 // TODO implement reading the elf file
802932 return error.IncrFailed;
933 //try self.populateMissingMetadata();
934 //return self;
803935}
804936
805937/// Saturating multiplication
......@@ -840,14 +972,14 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
840972 };
841973}
842974
843fn determineMode(module: ir.Module) fs.File.Mode {
975fn determineMode(options: Options) fs.File.Mode {
844976 // On common systems with a 0o022 umask, 0o777 will still result in a file created
845977 // with 0o755 permissions, but it works appropriately if the system is configured
846978 // more leniently. As another data point, C's fopen seems to open files with the
847979 // 666 mode.
848980 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) {
981 switch (options.output_mode) {
982 .Lib => return switch (options.link_mode) {
851983 .Dynamic => executable_mode,
852984 .Static => fs.File.default_mode,
853985 },
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/test.zig+5-6
......@@ -3,15 +3,14 @@ const link = @import("link.zig");
33const ir = @import("ir.zig");
44const Allocator = std.mem.Allocator;
55
6var global_ctx: TestContext = undefined;
7
86test "self-hosted" {
9 try global_ctx.init();
10 defer global_ctx.deinit();
7 var ctx: TestContext = undefined;
8 try ctx.init();
9 defer ctx.deinit();
1110
12 try @import("stage2_tests").addCases(&global_ctx);
11 try @import("stage2_tests").addCases(&ctx);
1312
14 try global_ctx.run();
13 try ctx.run();
1514}
1615
1716pub const TestContext = struct {
src-self-hosted/type.zig+62-1
......@@ -52,6 +52,7 @@ pub const Type = extern union {
5252 .comptime_float => return .ComptimeFloat,
5353 .noreturn => return .NoReturn,
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
......@@ -184,6 +185,7 @@ pub const Type = extern union {
184185 => return out_stream.writeAll(@tagName(t)),
185186
186187 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
188 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
187189 .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
188190 .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
189191 .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
......@@ -244,6 +246,7 @@ pub const Type = extern union {
244246 .comptime_int => return Value.initTag(.comptime_int_type),
245247 .comptime_float => return Value.initTag(.comptime_float_type),
246248 .noreturn => return Value.initTag(.noreturn_type),
249 .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
247250 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
248251 .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
249252 .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
......@@ -286,6 +289,7 @@ pub const Type = extern union {
286289 .array,
287290 .array_u8_sentinel_0,
288291 .const_slice_u8,
292 .fn_noreturn_no_args,
289293 .fn_naked_noreturn_no_args,
290294 .fn_ccc_void_no_args,
291295 .int_unsigned,
......@@ -329,6 +333,7 @@ pub const Type = extern union {
329333 .array_u8_sentinel_0,
330334 .single_const_pointer,
331335 .single_const_pointer_to_comptime_int,
336 .fn_noreturn_no_args,
332337 .fn_naked_noreturn_no_args,
333338 .fn_ccc_void_no_args,
334339 .int_unsigned,
......@@ -369,6 +374,7 @@ pub const Type = extern union {
369374 .noreturn,
370375 .array,
371376 .array_u8_sentinel_0,
377 .fn_noreturn_no_args,
372378 .fn_naked_noreturn_no_args,
373379 .fn_ccc_void_no_args,
374380 .int_unsigned,
......@@ -410,6 +416,7 @@ pub const Type = extern union {
410416 .comptime_int,
411417 .comptime_float,
412418 .noreturn,
419 .fn_noreturn_no_args,
413420 .fn_naked_noreturn_no_args,
414421 .fn_ccc_void_no_args,
415422 .int_unsigned,
......@@ -451,6 +458,7 @@ pub const Type = extern union {
451458 .comptime_int,
452459 .comptime_float,
453460 .noreturn,
461 .fn_noreturn_no_args,
454462 .fn_naked_noreturn_no_args,
455463 .fn_ccc_void_no_args,
456464 .single_const_pointer,
......@@ -481,6 +489,7 @@ pub const Type = extern union {
481489 .comptime_int,
482490 .comptime_float,
483491 .noreturn,
492 .fn_noreturn_no_args,
484493 .fn_naked_noreturn_no_args,
485494 .fn_ccc_void_no_args,
486495 .array,
......@@ -524,6 +533,7 @@ pub const Type = extern union {
524533 .comptime_int,
525534 .comptime_float,
526535 .noreturn,
536 .fn_noreturn_no_args,
527537 .fn_naked_noreturn_no_args,
528538 .fn_ccc_void_no_args,
529539 .array,
......@@ -579,6 +589,7 @@ pub const Type = extern union {
579589 /// Asserts the type is a function.
580590 pub fn fnParamLen(self: Type) usize {
581591 return switch (self.tag()) {
592 .fn_noreturn_no_args => 0,
582593 .fn_naked_noreturn_no_args => 0,
583594 .fn_ccc_void_no_args => 0,
584595
......@@ -622,6 +633,7 @@ pub const Type = extern union {
622633 /// given by `fnParamLen`.
623634 pub fn fnParamTypes(self: Type, types: []Type) void {
624635 switch (self.tag()) {
636 .fn_noreturn_no_args => return,
625637 .fn_naked_noreturn_no_args => return,
626638 .fn_ccc_void_no_args => return,
627639
......@@ -664,6 +676,7 @@ pub const Type = extern union {
664676 /// Asserts the type is a function.
665677 pub fn fnReturnType(self: Type) Type {
666678 return switch (self.tag()) {
679 .fn_noreturn_no_args => Type.initTag(.noreturn),
667680 .fn_naked_noreturn_no_args => Type.initTag(.noreturn),
668681 .fn_ccc_void_no_args => Type.initTag(.void),
669682
......@@ -706,6 +719,7 @@ pub const Type = extern union {
706719 /// Asserts the type is a function.
707720 pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
708721 return switch (self.tag()) {
722 .fn_noreturn_no_args => .Unspecified,
709723 .fn_naked_noreturn_no_args => .Naked,
710724 .fn_ccc_void_no_args => .C,
711725
......@@ -745,6 +759,49 @@ pub const Type = extern union {
745759 };
746760 }
747761
762 /// Asserts the type is a function.
763 pub fn fnIsVarArgs(self: Type) bool {
764 return switch (self.tag()) {
765 .fn_noreturn_no_args => false,
766 .fn_naked_noreturn_no_args => false,
767 .fn_ccc_void_no_args => false,
768
769 .f16,
770 .f32,
771 .f64,
772 .f128,
773 .c_longdouble,
774 .c_void,
775 .bool,
776 .void,
777 .type,
778 .anyerror,
779 .comptime_int,
780 .comptime_float,
781 .noreturn,
782 .array,
783 .single_const_pointer,
784 .single_const_pointer_to_comptime_int,
785 .array_u8_sentinel_0,
786 .const_slice_u8,
787 .u8,
788 .i8,
789 .usize,
790 .isize,
791 .c_short,
792 .c_ushort,
793 .c_int,
794 .c_uint,
795 .c_long,
796 .c_ulong,
797 .c_longlong,
798 .c_ulonglong,
799 .int_unsigned,
800 .int_signed,
801 => unreachable,
802 };
803 }
804
748805 pub fn isNumeric(self: Type) bool {
749806 return switch (self.tag()) {
750807 .f16,
......@@ -776,6 +833,7 @@ pub const Type = extern union {
776833 .type,
777834 .anyerror,
778835 .noreturn,
836 .fn_noreturn_no_args,
779837 .fn_naked_noreturn_no_args,
780838 .fn_ccc_void_no_args,
781839 .array,
......@@ -812,6 +870,7 @@ pub const Type = extern union {
812870 .bool,
813871 .type,
814872 .anyerror,
873 .fn_noreturn_no_args,
815874 .fn_naked_noreturn_no_args,
816875 .fn_ccc_void_no_args,
817876 .single_const_pointer_to_comptime_int,
......@@ -865,6 +924,7 @@ pub const Type = extern union {
865924 .bool,
866925 .type,
867926 .anyerror,
927 .fn_noreturn_no_args,
868928 .fn_naked_noreturn_no_args,
869929 .fn_ccc_void_no_args,
870930 .single_const_pointer_to_comptime_int,
......@@ -902,11 +962,11 @@ pub const Type = extern union {
902962 c_longlong,
903963 c_ulonglong,
904964 c_longdouble,
905 c_void,
906965 f16,
907966 f32,
908967 f64,
909968 f128,
969 c_void,
910970 bool,
911971 void,
912972 type,
......@@ -914,6 +974,7 @@ pub const Type = extern union {
914974 comptime_int,
915975 comptime_float,
916976 noreturn,
977 fn_noreturn_no_args,
917978 fn_naked_noreturn_no_args,
918979 fn_ccc_void_no_args,
919980 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+96-90
......@@ -6,6 +6,7 @@ const BigIntConst = std.math.big.int.Const;
66const BigIntMutable = std.math.big.int.Mutable;
77const Target = std.Target;
88const Allocator = std.mem.Allocator;
9const ir = @import("ir.zig");
910
1011/// This is the raw data, with no bookkeeping, no memory awareness,
1112/// no de-duplication, and no type system awareness.
......@@ -45,6 +46,7 @@ pub const Value = extern union {
4546 comptime_int_type,
4647 comptime_float_type,
4748 noreturn_type,
49 fn_noreturn_no_args_type,
4850 fn_naked_noreturn_no_args_type,
4951 fn_ccc_void_no_args_type,
5052 single_const_pointer_to_comptime_int_type,
......@@ -64,8 +66,8 @@ pub const Value = extern union {
6466 int_big_positive,
6567 int_big_negative,
6668 function,
67 ref,
68 ref_val,
69 decl_ref,
70 elem_ptr,
6971 bytes,
7072 repeated, // the value is a value repeated some number of times
7173
......@@ -136,6 +138,7 @@ pub const Value = extern union {
136138 .comptime_int_type => return out_stream.writeAll("comptime_int"),
137139 .comptime_float_type => return out_stream.writeAll("comptime_float"),
138140 .noreturn_type => return out_stream.writeAll("noreturn"),
141 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
139142 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
140143 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
141144 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
......@@ -153,11 +156,11 @@ pub const Value = extern union {
153156 .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
154157 .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
155158 .function => return out_stream.writeAll("(function)"),
156 .ref => return out_stream.writeAll("(ref)"),
157 .ref_val => {
158 try out_stream.writeAll("*const ");
159 val = val.cast(Payload.RefVal).?.val;
160 continue;
159 .decl_ref => return out_stream.writeAll("(decl ref)"),
160 .elem_ptr => {
161 const elem_ptr = val.cast(Payload.Int_u64).?;
162 try out_stream.print("&[{}] ", .{elem_ptr.index});
163 val = elem_ptr.array_ptr;
161164 },
162165 .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
163166 .repeated => {
......@@ -181,31 +184,32 @@ pub const Value = extern union {
181184 return switch (self.tag()) {
182185 .ty => self.cast(Payload.Ty).?.ty,
183186
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"),
187 .u8_type => Type.initTag(.u8),
188 .i8_type => Type.initTag(.i8),
189 .isize_type => Type.initTag(.isize),
190 .usize_type => Type.initTag(.usize),
191 .c_short_type => Type.initTag(.c_short),
192 .c_ushort_type => Type.initTag(.c_ushort),
193 .c_int_type => Type.initTag(.c_int),
194 .c_uint_type => Type.initTag(.c_uint),
195 .c_long_type => Type.initTag(.c_long),
196 .c_ulong_type => Type.initTag(.c_ulong),
197 .c_longlong_type => Type.initTag(.c_longlong),
198 .c_ulonglong_type => Type.initTag(.c_ulonglong),
199 .c_longdouble_type => Type.initTag(.c_longdouble),
200 .f16_type => Type.initTag(.f16),
201 .f32_type => Type.initTag(.f32),
202 .f64_type => Type.initTag(.f64),
203 .f128_type => Type.initTag(.f128),
204 .c_void_type => Type.initTag(.c_void),
205 .bool_type => Type.initTag(.bool),
206 .void_type => Type.initTag(.void),
207 .type_type => Type.initTag(.type),
208 .anyerror_type => Type.initTag(.anyerror),
209 .comptime_int_type => Type.initTag(.comptime_int),
210 .comptime_float_type => Type.initTag(.comptime_float),
211 .noreturn_type => Type.initTag(.noreturn),
212 .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
209213 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
210214 .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
211215 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
......@@ -222,8 +226,8 @@ pub const Value = extern union {
222226 .int_big_positive,
223227 .int_big_negative,
224228 .function,
225 .ref,
226 .ref_val,
229 .decl_ref,
230 .elem_ptr,
227231 .bytes,
228232 .repeated,
229233 => unreachable,
......@@ -259,6 +263,7 @@ pub const Value = extern union {
259263 .comptime_int_type,
260264 .comptime_float_type,
261265 .noreturn_type,
266 .fn_noreturn_no_args_type,
262267 .fn_naked_noreturn_no_args_type,
263268 .fn_ccc_void_no_args_type,
264269 .single_const_pointer_to_comptime_int_type,
......@@ -267,8 +272,8 @@ pub const Value = extern union {
267272 .bool_false,
268273 .null_value,
269274 .function,
270 .ref,
271 .ref_val,
275 .decl_ref,
276 .elem_ptr,
272277 .bytes,
273278 .undef,
274279 .repeated,
......@@ -314,6 +319,7 @@ pub const Value = extern union {
314319 .comptime_int_type,
315320 .comptime_float_type,
316321 .noreturn_type,
322 .fn_noreturn_no_args_type,
317323 .fn_naked_noreturn_no_args_type,
318324 .fn_ccc_void_no_args_type,
319325 .single_const_pointer_to_comptime_int_type,
......@@ -322,8 +328,8 @@ pub const Value = extern union {
322328 .bool_false,
323329 .null_value,
324330 .function,
325 .ref,
326 .ref_val,
331 .decl_ref,
332 .elem_ptr,
327333 .bytes,
328334 .undef,
329335 .repeated,
......@@ -370,6 +376,7 @@ pub const Value = extern union {
370376 .comptime_int_type,
371377 .comptime_float_type,
372378 .noreturn_type,
379 .fn_noreturn_no_args_type,
373380 .fn_naked_noreturn_no_args_type,
374381 .fn_ccc_void_no_args_type,
375382 .single_const_pointer_to_comptime_int_type,
......@@ -378,8 +385,8 @@ pub const Value = extern union {
378385 .bool_false,
379386 .null_value,
380387 .function,
381 .ref,
382 .ref_val,
388 .decl_ref,
389 .elem_ptr,
383390 .bytes,
384391 .undef,
385392 .repeated,
......@@ -431,6 +438,7 @@ pub const Value = extern union {
431438 .comptime_int_type,
432439 .comptime_float_type,
433440 .noreturn_type,
441 .fn_noreturn_no_args_type,
434442 .fn_naked_noreturn_no_args_type,
435443 .fn_ccc_void_no_args_type,
436444 .single_const_pointer_to_comptime_int_type,
......@@ -439,8 +447,8 @@ pub const Value = extern union {
439447 .bool_false,
440448 .null_value,
441449 .function,
442 .ref,
443 .ref_val,
450 .decl_ref,
451 .elem_ptr,
444452 .bytes,
445453 .repeated,
446454 => unreachable,
......@@ -521,6 +529,7 @@ pub const Value = extern union {
521529 .comptime_int_type,
522530 .comptime_float_type,
523531 .noreturn_type,
532 .fn_noreturn_no_args_type,
524533 .fn_naked_noreturn_no_args_type,
525534 .fn_ccc_void_no_args_type,
526535 .single_const_pointer_to_comptime_int_type,
......@@ -529,8 +538,8 @@ pub const Value = extern union {
529538 .bool_false,
530539 .null_value,
531540 .function,
532 .ref,
533 .ref_val,
541 .decl_ref,
542 .elem_ptr,
534543 .bytes,
535544 .repeated,
536545 .undef,
......@@ -573,6 +582,7 @@ pub const Value = extern union {
573582 .comptime_int_type,
574583 .comptime_float_type,
575584 .noreturn_type,
585 .fn_noreturn_no_args_type,
576586 .fn_naked_noreturn_no_args_type,
577587 .fn_ccc_void_no_args_type,
578588 .single_const_pointer_to_comptime_int_type,
......@@ -581,8 +591,8 @@ pub const Value = extern union {
581591 .bool_false,
582592 .null_value,
583593 .function,
584 .ref,
585 .ref_val,
594 .decl_ref,
595 .elem_ptr,
586596 .bytes,
587597 .repeated,
588598 .undef,
......@@ -636,7 +646,7 @@ pub const Value = extern union {
636646 }
637647
638648 /// Asserts the value is a pointer and dereferences it.
639 pub fn pointerDeref(self: Value) Value {
649 pub fn pointerDeref(self: Value, module: *ir.Module) !Value {
640650 return switch (self.tag()) {
641651 .ty,
642652 .u8_type,
......@@ -664,6 +674,7 @@ pub const Value = extern union {
664674 .comptime_int_type,
665675 .comptime_float_type,
666676 .noreturn_type,
677 .fn_noreturn_no_args_type,
667678 .fn_naked_noreturn_no_args_type,
668679 .fn_ccc_void_no_args_type,
669680 .single_const_pointer_to_comptime_int_type,
......@@ -683,14 +694,21 @@ pub const Value = extern union {
683694 => unreachable,
684695
685696 .the_one_possible_value => Value.initTag(.the_one_possible_value),
686 .ref => self.cast(Payload.Ref).?.cell.contents,
687 .ref_val => self.cast(Payload.RefVal).?.val,
697 .decl_ref => {
698 const index = self.cast(Payload.DeclRef).?.index;
699 return module.getDeclValue(index);
700 },
701 .elem_ptr => {
702 const elem_ptr = self.cast(ElemPtr).?;
703 const array_val = try elem_ptr.array_ptr.pointerDeref(module);
704 return self.elemValue(array_val, elem_ptr.index);
705 },
688706 };
689707 }
690708
691709 /// Asserts the value is a single-item pointer to an array, or an array,
692710 /// 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 {
711 pub fn elemValue(self: Value, index: usize) Value {
694712 switch (self.tag()) {
695713 .ty,
696714 .u8_type,
......@@ -718,6 +736,7 @@ pub const Value = extern union {
718736 .comptime_int_type,
719737 .comptime_float_type,
720738 .noreturn_type,
739 .fn_noreturn_no_args_type,
721740 .fn_naked_noreturn_no_args_type,
722741 .fn_ccc_void_no_args_type,
723742 .single_const_pointer_to_comptime_int_type,
......@@ -733,13 +752,12 @@ pub const Value = extern union {
733752 .int_big_positive,
734753 .int_big_negative,
735754 .undef,
755 .elem_ptr,
756 .decl_ref,
736757 => unreachable,
737758
738 .ref => @panic("TODO figure out how MemoryCell works"),
739 .ref_val => @panic("TODO figure out how MemoryCell works"),
740
741759 .bytes => {
742 const int_payload = try allocator.create(Value.Payload.Int_u64);
760 const int_payload = try allocator.create(Payload.Int_u64);
743761 int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
744762 return Value.initPayload(&int_payload.base);
745763 },
......@@ -749,6 +767,17 @@ pub const Value = extern union {
749767 }
750768 }
751769
770 /// Returns a pointer to the element value at the index.
771 pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value {
772 const payload = try allocator.create(Payload.ElemPtr);
773 if (self.cast(Payload.ElemPtr)) |elem_ptr| {
774 payload.* = .{ .array_ptr = elem_ptr.array_ptr, .index = elem_ptr.index + index };
775 } else {
776 payload.* = .{ .array_ptr = self, .index = index };
777 }
778 return Value.initPayload(&payload.base);
779 }
780
752781 pub fn isUndef(self: Value) bool {
753782 return self.tag() == .undef;
754783 }
......@@ -783,6 +812,7 @@ pub const Value = extern union {
783812 .comptime_int_type,
784813 .comptime_float_type,
785814 .noreturn_type,
815 .fn_noreturn_no_args_type,
786816 .fn_naked_noreturn_no_args_type,
787817 .fn_ccc_void_no_args_type,
788818 .single_const_pointer_to_comptime_int_type,
......@@ -796,8 +826,8 @@ pub const Value = extern union {
796826 .int_i64,
797827 .int_big_positive,
798828 .int_big_negative,
799 .ref,
800 .ref_val,
829 .decl_ref,
830 .elem_ptr,
801831 .bytes,
802832 .repeated,
803833 => false,
......@@ -841,8 +871,7 @@ pub const Value = extern union {
841871
842872 pub const Function = struct {
843873 base: Payload = Payload{ .tag = .function },
844 /// Index into the `fns` array of the `ir.Module`
845 index: usize,
874 func: *ir.Module.Fn,
846875 };
847876
848877 pub const ArraySentinel0_u8_Type = struct {
......@@ -855,14 +884,17 @@ pub const Value = extern union {
855884 elem_type: *Type,
856885 };
857886
858 pub const Ref = struct {
859 base: Payload = Payload{ .tag = .ref },
860 cell: *MemoryCell,
887 /// Represents a pointer to a decl, not the value of the decl.
888 pub const DeclRef = struct {
889 base: Payload = Payload{ .tag = .decl_ref },
890 /// Index into the Module's decls list
891 index: usize,
861892 };
862893
863 pub const RefVal = struct {
864 base: Payload = Payload{ .tag = .ref_val },
865 val: Value,
894 pub const ElemPtr = struct {
895 base: Payload = Payload{ .tag = .elem_ptr },
896 array_ptr: Value,
897 index: usize,
866898 };
867899
868900 pub const Bytes = struct {
......@@ -890,29 +922,3 @@ pub const Value = extern union {
890922 limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
891923 };
892924};
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};