authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-16 04:20:41-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-01-16 04:20:41-05:00
logd4fe4698d9ff865ed1dc7e0163f2d5fcbe2b45a6
tree160d596e8ab0ab9568dac3f026c2ce42ad1c935e
parent77273103a8f9895ceab28287dffcf4d4c6fcb91b
parenteda8b6e137a10f398cd292b533e924960f7fc409
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #22220 from ziglang/wasm-linker

wasm linker: aggressive rewrite towards Data-Oriented Design

104 files changed, 13672 insertions(+), 12204 deletions(-)

CMakeLists.txt+1-2
...@@ -643,9 +643,8 @@ set(ZIG_STAGE2_SOURCES...@@ -643,9 +643,8 @@ set(ZIG_STAGE2_SOURCES
643 src/link/StringTable.zig643 src/link/StringTable.zig
644 src/link/Wasm.zig644 src/link/Wasm.zig
645 src/link/Wasm/Archive.zig645 src/link/Wasm/Archive.zig
646 src/link/Wasm/Flush.zig
646 src/link/Wasm/Object.zig647 src/link/Wasm/Object.zig
647 src/link/Wasm/Symbol.zig
648 src/link/Wasm/ZigObject.zig
649 src/link/aarch64.zig648 src/link/aarch64.zig
650 src/link/riscv.zig649 src/link/riscv.zig
651 src/link/table_section.zig650 src/link/table_section.zig
build.zig+5
...@@ -447,6 +447,7 @@ pub fn build(b: *std.Build) !void {...@@ -447,6 +447,7 @@ pub fn build(b: *std.Build) !void {
447 .skip_single_threaded = skip_single_threaded,447 .skip_single_threaded = skip_single_threaded,
448 .skip_non_native = skip_non_native,448 .skip_non_native = skip_non_native,
449 .skip_libc = skip_libc,449 .skip_libc = skip_libc,
450 .use_llvm = use_llvm,
450 .max_rss = 1 * 1024 * 1024 * 1024,451 .max_rss = 1 * 1024 * 1024 * 1024,
451 }));452 }));
452453
...@@ -462,6 +463,7 @@ pub fn build(b: *std.Build) !void {...@@ -462,6 +463,7 @@ pub fn build(b: *std.Build) !void {
462 .skip_single_threaded = true,463 .skip_single_threaded = true,
463 .skip_non_native = skip_non_native,464 .skip_non_native = skip_non_native,
464 .skip_libc = skip_libc,465 .skip_libc = skip_libc,
466 .use_llvm = use_llvm,
465 }));467 }));
466468
467 test_modules_step.dependOn(tests.addModuleTests(b, .{469 test_modules_step.dependOn(tests.addModuleTests(b, .{
...@@ -476,6 +478,7 @@ pub fn build(b: *std.Build) !void {...@@ -476,6 +478,7 @@ pub fn build(b: *std.Build) !void {
476 .skip_single_threaded = true,478 .skip_single_threaded = true,
477 .skip_non_native = skip_non_native,479 .skip_non_native = skip_non_native,
478 .skip_libc = true,480 .skip_libc = true,
481 .use_llvm = use_llvm,
479 .no_builtin = true,482 .no_builtin = true,
480 }));483 }));
481484
...@@ -491,6 +494,7 @@ pub fn build(b: *std.Build) !void {...@@ -491,6 +494,7 @@ pub fn build(b: *std.Build) !void {
491 .skip_single_threaded = true,494 .skip_single_threaded = true,
492 .skip_non_native = skip_non_native,495 .skip_non_native = skip_non_native,
493 .skip_libc = true,496 .skip_libc = true,
497 .use_llvm = use_llvm,
494 .no_builtin = true,498 .no_builtin = true,
495 }));499 }));
496500
...@@ -506,6 +510,7 @@ pub fn build(b: *std.Build) !void {...@@ -506,6 +510,7 @@ pub fn build(b: *std.Build) !void {
506 .skip_single_threaded = skip_single_threaded,510 .skip_single_threaded = skip_single_threaded,
507 .skip_non_native = skip_non_native,511 .skip_non_native = skip_non_native,
508 .skip_libc = skip_libc,512 .skip_libc = skip_libc,
513 .use_llvm = use_llvm,
509 // I observed a value of 4572626944 on the M2 CI.514 // I observed a value of 4572626944 on the M2 CI.
510 .max_rss = 5029889638,515 .max_rss = 5029889638,
511 }));516 }));
lib/std/Build/Step/CheckObject.zig+17-4
...@@ -2424,7 +2424,22 @@ const WasmDumper = struct {...@@ -2424,7 +2424,22 @@ const WasmDumper = struct {
2424 }2424 }
24252425
2426 var output = std.ArrayList(u8).init(gpa);2426 var output = std.ArrayList(u8).init(gpa);
2427 errdefer output.deinit();2427 defer output.deinit();
2428 parseAndDumpInner(step, check, bytes, &fbs, &output) catch |err| switch (err) {
2429 error.EndOfStream => try output.appendSlice("\n<UnexpectedEndOfStream>"),
2430 else => |e| return e,
2431 };
2432 return output.toOwnedSlice();
2433 }
2434
2435 fn parseAndDumpInner(
2436 step: *Step,
2437 check: Check,
2438 bytes: []const u8,
2439 fbs: *std.io.FixedBufferStream([]const u8),
2440 output: *std.ArrayList(u8),
2441 ) !void {
2442 const reader = fbs.reader();
2428 const writer = output.writer();2443 const writer = output.writer();
24292444
2430 switch (check.kind) {2445 switch (check.kind) {
...@@ -2442,8 +2457,6 @@ const WasmDumper = struct {...@@ -2442,8 +2457,6 @@ const WasmDumper = struct {
24422457
2443 else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}),2458 else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}),
2444 }2459 }
2445
2446 return output.toOwnedSlice();
2447 }2460 }
24482461
2449 fn parseAndDumpSection(2462 fn parseAndDumpSection(
...@@ -2682,7 +2695,7 @@ const WasmDumper = struct {...@@ -2682,7 +2695,7 @@ const WasmDumper = struct {
2682 else => unreachable,2695 else => unreachable,
2683 }2696 }
2684 const end_opcode = try std.leb.readUleb128(u8, reader);2697 const end_opcode = try std.leb.readUleb128(u8, reader);
2685 if (end_opcode != std.wasm.opcode(.end)) {2698 if (end_opcode != @intFromEnum(std.wasm.Opcode.end)) {
2686 return step.fail("expected 'end' opcode in init expression", .{});2699 return step.fail("expected 'end' opcode in init expression", .{});
2687 }2700 }
2688 }2701 }
lib/std/Target.zig+6
...@@ -1219,6 +1219,12 @@ pub const Cpu = struct {...@@ -1219,6 +1219,12 @@ pub const Cpu = struct {
1219 } else true;1219 } else true;
1220 }1220 }
12211221
1222 pub fn count(set: Set) std.math.IntFittingRange(0, needed_bit_count) {
1223 var sum: usize = 0;
1224 for (set.ints) |x| sum += @popCount(x);
1225 return @intCast(sum);
1226 }
1227
1222 pub fn isEnabled(set: Set, arch_feature_index: Index) bool {1228 pub fn isEnabled(set: Set, arch_feature_index: Index) bool {
1223 const usize_index = arch_feature_index / @bitSizeOf(usize);1229 const usize_index = arch_feature_index / @bitSizeOf(usize);
1224 const bit_index: ShiftInt = @intCast(arch_feature_index % @bitSizeOf(usize));1230 const bit_index: ShiftInt = @intCast(arch_feature_index % @bitSizeOf(usize));
lib/std/Thread.zig+8-5
...@@ -1018,12 +1018,15 @@ const WasiThreadImpl = struct {...@@ -1018,12 +1018,15 @@ const WasiThreadImpl = struct {
1018 return .{ .thread = &instance.thread };1018 return .{ .thread = &instance.thread };
1019 }1019 }
10201020
1021 /// Bootstrap procedure, called by the host environment after thread creation.1021 comptime {
1022 export fn wasi_thread_start(tid: i32, arg: *Instance) void {1022 if (!builtin.single_threaded) {
1023 if (builtin.single_threaded) {1023 @export(wasi_thread_start, .{ .name = "wasi_thread_start" });
1024 // ensure function is not analyzed in single-threaded mode
1025 return;
1026 }1024 }
1025 }
1026
1027 /// Called by the host environment after thread creation.
1028 fn wasi_thread_start(tid: i32, arg: *Instance) callconv(.c) void {
1029 comptime assert(!builtin.single_threaded);
1027 __set_stack_pointer(arg.thread.memory.ptr + arg.stack_offset);1030 __set_stack_pointer(arg.thread.memory.ptr + arg.stack_offset);
1028 __wasm_init_tls(arg.thread.memory.ptr + arg.tls_offset);1031 __wasm_init_tls(arg.thread.memory.ptr + arg.tls_offset);
1029 @atomicStore(u32, &WasiThreadImpl.tls_thread_id, @intCast(tid), .seq_cst);1032 @atomicStore(u32, &WasiThreadImpl.tls_thread_id, @intCast(tid), .seq_cst);
lib/std/array_hash_map.zig+4-1
...@@ -641,10 +641,13 @@ pub fn ArrayHashMapUnmanaged(...@@ -641,10 +641,13 @@ pub fn ArrayHashMapUnmanaged(
641 return self;641 return self;
642 }642 }
643643
644 /// An empty `value_list` may be passed, in which case the values array becomes `undefined`.
644 pub fn reinit(self: *Self, gpa: Allocator, key_list: []const K, value_list: []const V) Oom!void {645 pub fn reinit(self: *Self, gpa: Allocator, key_list: []const K, value_list: []const V) Oom!void {
645 try self.entries.resize(gpa, key_list.len);646 try self.entries.resize(gpa, key_list.len);
646 @memcpy(self.keys(), key_list);647 @memcpy(self.keys(), key_list);
647 if (@sizeOf(V) != 0) {648 if (value_list.len == 0) {
649 @memset(self.values(), undefined);
650 } else {
648 assert(key_list.len == value_list.len);651 assert(key_list.len == value_list.len);
649 @memcpy(self.values(), value_list);652 @memcpy(self.values(), value_list);
650 }653 }
lib/std/array_list.zig+2-4
...@@ -267,8 +267,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -267,8 +267,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
267 /// Never invalidates element pointers.267 /// Never invalidates element pointers.
268 /// Asserts that the list can hold one additional item.268 /// Asserts that the list can hold one additional item.
269 pub fn appendAssumeCapacity(self: *Self, item: T) void {269 pub fn appendAssumeCapacity(self: *Self, item: T) void {
270 const new_item_ptr = self.addOneAssumeCapacity();270 self.addOneAssumeCapacity().* = item;
271 new_item_ptr.* = item;
272 }271 }
273272
274 /// Remove the element at index `i`, shift elements after index273 /// Remove the element at index `i`, shift elements after index
...@@ -879,8 +878,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -879,8 +878,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
879 /// Never invalidates element pointers.878 /// Never invalidates element pointers.
880 /// Asserts that the list can hold one additional item.879 /// Asserts that the list can hold one additional item.
881 pub fn appendAssumeCapacity(self: *Self, item: T) void {880 pub fn appendAssumeCapacity(self: *Self, item: T) void {
882 const new_item_ptr = self.addOneAssumeCapacity();881 self.addOneAssumeCapacity().* = item;
883 new_item_ptr.* = item;
884 }882 }
885883
886 /// Remove the element at index `i` from the list and return its value.884 /// Remove the element at index `i` from the list and return its value.
lib/std/io.zig-12
...@@ -16,10 +16,6 @@ const Allocator = std.mem.Allocator;...@@ -16,10 +16,6 @@ const Allocator = std.mem.Allocator;
1616
17fn getStdOutHandle() posix.fd_t {17fn getStdOutHandle() posix.fd_t {
18 if (is_windows) {18 if (is_windows) {
19 if (builtin.zig_backend == .stage2_aarch64) {
20 // TODO: this is just a temporary workaround until we advance aarch64 backend further along.
21 return windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) catch windows.INVALID_HANDLE_VALUE;
22 }
23 return windows.peb().ProcessParameters.hStdOutput;19 return windows.peb().ProcessParameters.hStdOutput;
24 }20 }
2521
...@@ -36,10 +32,6 @@ pub fn getStdOut() File {...@@ -36,10 +32,6 @@ pub fn getStdOut() File {
3632
37fn getStdErrHandle() posix.fd_t {33fn getStdErrHandle() posix.fd_t {
38 if (is_windows) {34 if (is_windows) {
39 if (builtin.zig_backend == .stage2_aarch64) {
40 // TODO: this is just a temporary workaround until we advance aarch64 backend further along.
41 return windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch windows.INVALID_HANDLE_VALUE;
42 }
43 return windows.peb().ProcessParameters.hStdError;35 return windows.peb().ProcessParameters.hStdError;
44 }36 }
4537
...@@ -56,10 +48,6 @@ pub fn getStdErr() File {...@@ -56,10 +48,6 @@ pub fn getStdErr() File {
5648
57fn getStdInHandle() posix.fd_t {49fn getStdInHandle() posix.fd_t {
58 if (is_windows) {50 if (is_windows) {
59 if (builtin.zig_backend == .stage2_aarch64) {
60 // TODO: this is just a temporary workaround until we advance aarch64 backend further along.
61 return windows.GetStdHandle(windows.STD_INPUT_HANDLE) catch windows.INVALID_HANDLE_VALUE;
62 }
63 return windows.peb().ProcessParameters.hStdInput;51 return windows.peb().ProcessParameters.hStdInput;
64 }52 }
6553
lib/std/wasm.zig+17-180
...@@ -4,8 +4,6 @@...@@ -4,8 +4,6 @@
4const std = @import("std.zig");4const std = @import("std.zig");
5const testing = std.testing;5const testing = std.testing;
66
7// TODO: Add support for multi-byte ops (e.g. table operations)
8
9/// Wasm instruction opcodes7/// Wasm instruction opcodes
10///8///
11/// All instructions are defined as per spec:9/// All instructions are defined as per spec:
...@@ -195,27 +193,6 @@ pub const Opcode = enum(u8) {...@@ -195,27 +193,6 @@ pub const Opcode = enum(u8) {
195 _,193 _,
196};194};
197195
198/// Returns the integer value of an `Opcode`. Used by the Zig compiler
199/// to write instructions to the wasm binary file
200pub fn opcode(op: Opcode) u8 {
201 return @intFromEnum(op);
202}
203
204test "opcodes" {
205 // Ensure our opcodes values remain intact as certain values are skipped due to them being reserved
206 const i32_const = opcode(.i32_const);
207 const end = opcode(.end);
208 const drop = opcode(.drop);
209 const local_get = opcode(.local_get);
210 const i64_extend32_s = opcode(.i64_extend32_s);
211
212 try testing.expectEqual(@as(u16, 0x41), i32_const);
213 try testing.expectEqual(@as(u16, 0x0B), end);
214 try testing.expectEqual(@as(u16, 0x1A), drop);
215 try testing.expectEqual(@as(u16, 0x20), local_get);
216 try testing.expectEqual(@as(u16, 0xC4), i64_extend32_s);
217}
218
219/// Opcodes that require a prefix `0xFC`.196/// Opcodes that require a prefix `0xFC`.
220/// Each opcode represents a varuint32, meaning197/// Each opcode represents a varuint32, meaning
221/// they are encoded as leb128 in binary.198/// they are encoded as leb128 in binary.
...@@ -241,12 +218,6 @@ pub const MiscOpcode = enum(u32) {...@@ -241,12 +218,6 @@ pub const MiscOpcode = enum(u32) {
241 _,218 _,
242};219};
243220
244/// Returns the integer value of an `MiscOpcode`. Used by the Zig compiler
245/// to write instructions to the wasm binary file
246pub fn miscOpcode(op: MiscOpcode) u32 {
247 return @intFromEnum(op);
248}
249
250/// Simd opcodes that require a prefix `0xFD`.221/// Simd opcodes that require a prefix `0xFD`.
251/// Each opcode represents a varuint32, meaning222/// Each opcode represents a varuint32, meaning
252/// they are encoded as leb128 in binary.223/// they are encoded as leb128 in binary.
...@@ -512,12 +483,6 @@ pub const SimdOpcode = enum(u32) {...@@ -512,12 +483,6 @@ pub const SimdOpcode = enum(u32) {
512 f32x4_relaxed_dot_bf16x8_add_f32x4 = 0x114,483 f32x4_relaxed_dot_bf16x8_add_f32x4 = 0x114,
513};484};
514485
515/// Returns the integer value of an `SimdOpcode`. Used by the Zig compiler
516/// to write instructions to the wasm binary file
517pub fn simdOpcode(op: SimdOpcode) u32 {
518 return @intFromEnum(op);
519}
520
521/// Atomic opcodes that require a prefix `0xFE`.486/// Atomic opcodes that require a prefix `0xFE`.
522/// Each opcode represents a varuint32, meaning487/// Each opcode represents a varuint32, meaning
523/// they are encoded as leb128 in binary.488/// they are encoded as leb128 in binary.
...@@ -592,12 +557,6 @@ pub const AtomicsOpcode = enum(u32) {...@@ -592,12 +557,6 @@ pub const AtomicsOpcode = enum(u32) {
592 i64_atomic_rmw32_cmpxchg_u = 0x4E,557 i64_atomic_rmw32_cmpxchg_u = 0x4E,
593};558};
594559
595/// Returns the integer value of an `AtomicsOpcode`. Used by the Zig compiler
596/// to write instructions to the wasm binary file
597pub fn atomicsOpcode(op: AtomicsOpcode) u32 {
598 return @intFromEnum(op);
599}
600
601/// Enum representing all Wasm value types as per spec:560/// Enum representing all Wasm value types as per spec:
602/// https://webassembly.github.io/spec/core/binary/types.html561/// https://webassembly.github.io/spec/core/binary/types.html
603pub const Valtype = enum(u8) {562pub const Valtype = enum(u8) {
...@@ -608,11 +567,6 @@ pub const Valtype = enum(u8) {...@@ -608,11 +567,6 @@ pub const Valtype = enum(u8) {
608 v128 = 0x7B,567 v128 = 0x7B,
609};568};
610569
611/// Returns the integer value of a `Valtype`
612pub fn valtype(value: Valtype) u8 {
613 return @intFromEnum(value);
614}
615
616/// Reference types, where the funcref references to a function regardless of its type570/// Reference types, where the funcref references to a function regardless of its type
617/// and ref references an object from the embedder.571/// and ref references an object from the embedder.
618pub const RefType = enum(u8) {572pub const RefType = enum(u8) {
...@@ -620,41 +574,17 @@ pub const RefType = enum(u8) {...@@ -620,41 +574,17 @@ pub const RefType = enum(u8) {
620 externref = 0x6F,574 externref = 0x6F,
621};575};
622576
623/// Returns the integer value of a `Reftype`
624pub fn reftype(value: RefType) u8 {
625 return @intFromEnum(value);
626}
627
628test "valtypes" {
629 const _i32 = valtype(.i32);
630 const _i64 = valtype(.i64);
631 const _f32 = valtype(.f32);
632 const _f64 = valtype(.f64);
633
634 try testing.expectEqual(@as(u8, 0x7F), _i32);
635 try testing.expectEqual(@as(u8, 0x7E), _i64);
636 try testing.expectEqual(@as(u8, 0x7D), _f32);
637 try testing.expectEqual(@as(u8, 0x7C), _f64);
638}
639
640/// Limits classify the size range of resizeable storage associated with memory types and table types.577/// Limits classify the size range of resizeable storage associated with memory types and table types.
641pub const Limits = struct {578pub const Limits = struct {
642 flags: u8,579 flags: Flags,
643 min: u32,580 min: u32,
644 max: u32,581 max: u32,
645582
646 pub const Flags = enum(u8) {583 pub const Flags = packed struct(u8) {
647 WASM_LIMITS_FLAG_HAS_MAX = 0x1,584 has_max: bool,
648 WASM_LIMITS_FLAG_IS_SHARED = 0x2,585 is_shared: bool,
586 reserved: u6 = 0,
649 };587 };
650
651 pub fn hasFlag(limits: Limits, flag: Flags) bool {
652 return limits.flags & @intFromEnum(flag) != 0;
653 }
654
655 pub fn setFlag(limits: *Limits, flag: Flags) void {
656 limits.flags |= @intFromEnum(flag);
657 }
658};588};
659589
660/// Initialization expressions are used to set the initial value on an object590/// Initialization expressions are used to set the initial value on an object
...@@ -667,18 +597,6 @@ pub const InitExpression = union(enum) {...@@ -667,18 +597,6 @@ pub const InitExpression = union(enum) {
667 global_get: u32,597 global_get: u32,
668};598};
669599
670/// Represents a function entry, holding the index to its type
671pub const Func = struct {
672 type_index: u32,
673};
674
675/// Tables are used to hold pointers to opaque objects.
676/// This can either by any function, or an object from the host.
677pub const Table = struct {
678 limits: Limits,
679 reftype: RefType,
680};
681
682/// Describes the layout of the memory where `min` represents600/// Describes the layout of the memory where `min` represents
683/// the minimal amount of pages, and the optional `max` represents601/// the minimal amount of pages, and the optional `max` represents
684/// the max pages. When `null` will allow the host to determine the602/// the max pages. When `null` will allow the host to determine the
...@@ -687,88 +605,6 @@ pub const Memory = struct {...@@ -687,88 +605,6 @@ pub const Memory = struct {
687 limits: Limits,605 limits: Limits,
688};606};
689607
690/// Represents the type of a `Global` or an imported global.
691pub const GlobalType = struct {
692 valtype: Valtype,
693 mutable: bool,
694};
695
696pub const Global = struct {
697 global_type: GlobalType,
698 init: InitExpression,
699};
700
701/// Notates an object to be exported from wasm
702/// to the host.
703pub const Export = struct {
704 name: []const u8,
705 kind: ExternalKind,
706 index: u32,
707};
708
709/// Element describes the layout of the table that can
710/// be found at `table_index`
711pub const Element = struct {
712 table_index: u32,
713 offset: InitExpression,
714 func_indexes: []const u32,
715};
716
717/// Imports are used to import objects from the host
718pub const Import = struct {
719 module_name: []const u8,
720 name: []const u8,
721 kind: Kind,
722
723 pub const Kind = union(ExternalKind) {
724 function: u32,
725 table: Table,
726 memory: Limits,
727 global: GlobalType,
728 };
729};
730
731/// `Type` represents a function signature type containing both
732/// a slice of parameters as well as a slice of return values.
733pub const Type = struct {
734 params: []const Valtype,
735 returns: []const Valtype,
736
737 pub fn format(self: Type, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
738 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
739 _ = opt;
740 try writer.writeByte('(');
741 for (self.params, 0..) |param, i| {
742 try writer.print("{s}", .{@tagName(param)});
743 if (i + 1 != self.params.len) {
744 try writer.writeAll(", ");
745 }
746 }
747 try writer.writeAll(") -> ");
748 if (self.returns.len == 0) {
749 try writer.writeAll("nil");
750 } else {
751 for (self.returns, 0..) |return_ty, i| {
752 try writer.print("{s}", .{@tagName(return_ty)});
753 if (i + 1 != self.returns.len) {
754 try writer.writeAll(", ");
755 }
756 }
757 }
758 }
759
760 pub fn eql(self: Type, other: Type) bool {
761 return std.mem.eql(Valtype, self.params, other.params) and
762 std.mem.eql(Valtype, self.returns, other.returns);
763 }
764
765 pub fn deinit(self: *Type, gpa: std.mem.Allocator) void {
766 gpa.free(self.params);
767 gpa.free(self.returns);
768 self.* = undefined;
769 }
770};
771
772/// Wasm module sections as per spec:608/// Wasm module sections as per spec:
773/// https://webassembly.github.io/spec/core/binary/modules.html609/// https://webassembly.github.io/spec/core/binary/modules.html
774pub const Section = enum(u8) {610pub const Section = enum(u8) {
...@@ -788,11 +624,6 @@ pub const Section = enum(u8) {...@@ -788,11 +624,6 @@ pub const Section = enum(u8) {
788 _,624 _,
789};625};
790626
791/// Returns the integer value of a given `Section`
792pub fn section(val: Section) u8 {
793 return @intFromEnum(val);
794}
795
796/// The kind of the type when importing or exporting to/from the host environment.627/// The kind of the type when importing or exporting to/from the host environment.
797/// https://webassembly.github.io/spec/core/syntax/modules.html628/// https://webassembly.github.io/spec/core/syntax/modules.html
798pub const ExternalKind = enum(u8) {629pub const ExternalKind = enum(u8) {
...@@ -802,11 +633,6 @@ pub const ExternalKind = enum(u8) {...@@ -802,11 +633,6 @@ pub const ExternalKind = enum(u8) {
802 global,633 global,
803};634};
804635
805/// Returns the integer value of a given `ExternalKind`
806pub fn externalKind(val: ExternalKind) u8 {
807 return @intFromEnum(val);
808}
809
810/// Defines the enum values for each subsection id for the "Names" custom section636/// Defines the enum values for each subsection id for the "Names" custom section
811/// as described by:637/// as described by:
812/// https://webassembly.github.io/spec/core/appendix/custom.html?highlight=name#name-section638/// https://webassembly.github.io/spec/core/appendix/custom.html?highlight=name#name-section
...@@ -829,7 +655,18 @@ pub const function_type: u8 = 0x60;...@@ -829,7 +655,18 @@ pub const function_type: u8 = 0x60;
829pub const result_type: u8 = 0x40;655pub const result_type: u8 = 0x40;
830656
831/// Represents a block which will not return a value657/// Represents a block which will not return a value
832pub const block_empty: u8 = 0x40;658pub const BlockType = enum(u8) {
659 empty = 0x40,
660 i32 = 0x7F,
661 i64 = 0x7E,
662 f32 = 0x7D,
663 f64 = 0x7C,
664 v128 = 0x7B,
665
666 pub fn fromValtype(valtype: Valtype) BlockType {
667 return @enumFromInt(@intFromEnum(valtype));
668 }
669};
833670
834// binary constants671// binary constants
835pub const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm672pub const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm
lib/std/zig/ErrorBundle.zig+15-13
...@@ -11,6 +11,11 @@ string_bytes: []const u8,...@@ -11,6 +11,11 @@ string_bytes: []const u8,
11/// The first thing in this array is an `ErrorMessageList`.11/// The first thing in this array is an `ErrorMessageList`.
12extra: []const u32,12extra: []const u32,
1313
14/// Index into `string_bytes`.
15pub const String = u32;
16/// Index into `string_bytes`, or null.
17pub const OptionalString = u32;
18
14/// Special encoding when there are no errors.19/// Special encoding when there are no errors.
15pub const empty: ErrorBundle = .{20pub const empty: ErrorBundle = .{
16 .string_bytes = &.{},21 .string_bytes = &.{},
...@@ -33,14 +38,13 @@ pub const ErrorMessageList = struct {...@@ -33,14 +38,13 @@ pub const ErrorMessageList = struct {
33 len: u32,38 len: u32,
34 start: u32,39 start: u32,
35 /// null-terminated string index. 0 means no compile log text.40 /// null-terminated string index. 0 means no compile log text.
36 compile_log_text: u32,41 compile_log_text: OptionalString,
37};42};
3843
39/// Trailing:44/// Trailing:
40/// * ReferenceTrace for each reference_trace_len45/// * ReferenceTrace for each reference_trace_len
41pub const SourceLocation = struct {46pub const SourceLocation = struct {
42 /// null terminated string index47 src_path: String,
43 src_path: u32,
44 line: u32,48 line: u32,
45 column: u32,49 column: u32,
46 /// byte offset of starting token50 /// byte offset of starting token
...@@ -49,17 +53,15 @@ pub const SourceLocation = struct {...@@ -49,17 +53,15 @@ pub const SourceLocation = struct {
49 span_main: u32,53 span_main: u32,
50 /// byte offset of end of last token54 /// byte offset of end of last token
51 span_end: u32,55 span_end: u32,
52 /// null terminated string index, possibly null.
53 /// Does not include the trailing newline.56 /// Does not include the trailing newline.
54 source_line: u32 = 0,57 source_line: OptionalString = 0,
55 reference_trace_len: u32 = 0,58 reference_trace_len: u32 = 0,
56};59};
5760
58/// Trailing:61/// Trailing:
59/// * MessageIndex for each notes_len.62/// * MessageIndex for each notes_len.
60pub const ErrorMessage = struct {63pub const ErrorMessage = struct {
61 /// null terminated string index64 msg: String,
62 msg: u32,
63 /// Usually one, but incremented for redundant messages.65 /// Usually one, but incremented for redundant messages.
64 count: u32 = 1,66 count: u32 = 1,
65 src_loc: SourceLocationIndex = .none,67 src_loc: SourceLocationIndex = .none,
...@@ -71,7 +73,7 @@ pub const ReferenceTrace = struct {...@@ -71,7 +73,7 @@ pub const ReferenceTrace = struct {
71 /// Except for the sentinel ReferenceTrace element, in which case:73 /// Except for the sentinel ReferenceTrace element, in which case:
72 /// * 0 means remaining references hidden74 /// * 0 means remaining references hidden
73 /// * >0 means N references hidden75 /// * >0 means N references hidden
74 decl_name: u32,76 decl_name: String,
75 /// Index into extra of a SourceLocation77 /// Index into extra of a SourceLocation
76 /// If this is 0, this is the sentinel ReferenceTrace element.78 /// If this is 0, this is the sentinel ReferenceTrace element.
77 src_loc: SourceLocationIndex,79 src_loc: SourceLocationIndex,
...@@ -138,7 +140,7 @@ fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T,...@@ -138,7 +140,7 @@ fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T,
138}140}
139141
140/// Given an index into `string_bytes` returns the null-terminated string found there.142/// Given an index into `string_bytes` returns the null-terminated string found there.
141pub fn nullTerminatedString(eb: ErrorBundle, index: usize) [:0]const u8 {143pub fn nullTerminatedString(eb: ErrorBundle, index: String) [:0]const u8 {
142 const string_bytes = eb.string_bytes;144 const string_bytes = eb.string_bytes;
143 var end: usize = index;145 var end: usize = index;
144 while (string_bytes[end] != 0) {146 while (string_bytes[end] != 0) {
...@@ -384,18 +386,18 @@ pub const Wip = struct {...@@ -384,18 +386,18 @@ pub const Wip = struct {
384 };386 };
385 }387 }
386388
387 pub fn addString(wip: *Wip, s: []const u8) Allocator.Error!u32 {389 pub fn addString(wip: *Wip, s: []const u8) Allocator.Error!String {
388 const gpa = wip.gpa;390 const gpa = wip.gpa;
389 const index: u32 = @intCast(wip.string_bytes.items.len);391 const index: String = @intCast(wip.string_bytes.items.len);
390 try wip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1);392 try wip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1);
391 wip.string_bytes.appendSliceAssumeCapacity(s);393 wip.string_bytes.appendSliceAssumeCapacity(s);
392 wip.string_bytes.appendAssumeCapacity(0);394 wip.string_bytes.appendAssumeCapacity(0);
393 return index;395 return index;
394 }396 }
395397
396 pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) Allocator.Error!u32 {398 pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) Allocator.Error!String {
397 const gpa = wip.gpa;399 const gpa = wip.gpa;
398 const index: u32 = @intCast(wip.string_bytes.items.len);400 const index: String = @intCast(wip.string_bytes.items.len);
399 try wip.string_bytes.writer(gpa).print(fmt, args);401 try wip.string_bytes.writer(gpa).print(fmt, args);
400 try wip.string_bytes.append(gpa, 0);402 try wip.string_bytes.append(gpa, 0);
401 return index;403 return index;
src/Compilation.zig+164-8
...@@ -113,6 +113,14 @@ link_diags: link.Diags,...@@ -113,6 +113,14 @@ link_diags: link.Diags,
113link_task_queue: ThreadSafeQueue(link.Task) = .empty,113link_task_queue: ThreadSafeQueue(link.Task) = .empty,
114/// Ensure only 1 simultaneous call to `flushTaskQueue`.114/// Ensure only 1 simultaneous call to `flushTaskQueue`.
115link_task_queue_safety: std.debug.SafetyLock = .{},115link_task_queue_safety: std.debug.SafetyLock = .{},
116/// If any tasks are queued up that depend on prelink being finished, they are moved
117/// here until prelink finishes.
118link_task_queue_postponed: std.ArrayListUnmanaged(link.Task) = .empty,
119/// Initialized with how many link input tasks are expected. After this reaches zero
120/// the linker will begin the prelink phase.
121/// Initialized in the Compilation main thread before the pipeline; modified only in
122/// the linker task thread.
123remaining_prelink_tasks: u32,
116124
117work_queues: [125work_queues: [
118 len: {126 len: {
...@@ -1515,6 +1523,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1515,6 +1523,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1515 .file_system_inputs = options.file_system_inputs,1523 .file_system_inputs = options.file_system_inputs,
1516 .parent_whole_cache = options.parent_whole_cache,1524 .parent_whole_cache = options.parent_whole_cache,
1517 .link_diags = .init(gpa),1525 .link_diags = .init(gpa),
1526 .remaining_prelink_tasks = 0,
1518 };1527 };
15191528
1520 // Prevent some footguns by making the "any" fields of config reflect1529 // Prevent some footguns by making the "any" fields of config reflect
...@@ -1587,6 +1596,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1587,6 +1596,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1587 .pdb_source_path = options.pdb_source_path,1596 .pdb_source_path = options.pdb_source_path,
1588 .pdb_out_path = options.pdb_out_path,1597 .pdb_out_path = options.pdb_out_path,
1589 .entry_addr = null, // CLI does not expose this option (yet?)1598 .entry_addr = null, // CLI does not expose this option (yet?)
1599 .object_host_name = "env",
1590 };1600 };
15911601
1592 switch (options.cache_mode) {1602 switch (options.cache_mode) {
...@@ -1715,6 +1725,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1715,6 +1725,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1715 };1725 };
1716 comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});1726 comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});
1717 }1727 }
1728 comp.remaining_prelink_tasks += @intCast(comp.c_object_table.count());
17181729
1719 // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`.1730 // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`.
1720 const win32_resource_count =1731 const win32_resource_count =
...@@ -1722,6 +1733,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1722,6 +1733,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1722 if (win32_resource_count > 0) {1733 if (win32_resource_count > 0) {
1723 dev.check(.win32_resource);1734 dev.check(.win32_resource);
1724 try comp.win32_resource_table.ensureTotalCapacity(gpa, win32_resource_count);1735 try comp.win32_resource_table.ensureTotalCapacity(gpa, win32_resource_count);
1736 // Add this after adding logic to updateWin32Resource to pass the
1737 // result into link.loadInput. loadInput integration is not implemented
1738 // for Windows linking logic yet.
1739 //comp.remaining_prelink_tasks += @intCast(win32_resource_count);
1725 for (options.rc_source_files) |rc_source_file| {1740 for (options.rc_source_files) |rc_source_file| {
1726 const win32_resource = try gpa.create(Win32Resource);1741 const win32_resource = try gpa.create(Win32Resource);
1727 errdefer gpa.destroy(win32_resource);1742 errdefer gpa.destroy(win32_resource);
...@@ -1732,6 +1747,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1732,6 +1747,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1732 };1747 };
1733 comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {});1748 comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {});
1734 }1749 }
1750
1735 if (options.manifest_file) |manifest_path| {1751 if (options.manifest_file) |manifest_path| {
1736 const win32_resource = try gpa.create(Win32Resource);1752 const win32_resource = try gpa.create(Win32Resource);
1737 errdefer gpa.destroy(win32_resource);1753 errdefer gpa.destroy(win32_resource);
...@@ -1779,10 +1795,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1779,10 +1795,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1779 inline for (fields) |field| {1795 inline for (fields) |field| {
1780 if (@field(paths, field.name)) |path| {1796 if (@field(paths, field.name)) |path| {
1781 comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path });1797 comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path });
1798 comp.remaining_prelink_tasks += 1;
1782 }1799 }
1783 }1800 }
1784 // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`.1801 // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`.
1785 comp.link_task_queue.shared.appendAssumeCapacity(.load_host_libc);1802 comp.link_task_queue.shared.appendAssumeCapacity(.load_host_libc);
1803 comp.remaining_prelink_tasks += 1;
1786 } else if (target.isMusl() and !target.isWasm()) {1804 } else if (target.isMusl() and !target.isWasm()) {
1787 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;1805 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
17881806
...@@ -1791,14 +1809,17 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1791,14 +1809,17 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1791 .{ .musl_crt_file = .crti_o },1809 .{ .musl_crt_file = .crti_o },
1792 .{ .musl_crt_file = .crtn_o },1810 .{ .musl_crt_file = .crtn_o },
1793 });1811 });
1812 comp.remaining_prelink_tasks += 2;
1794 }1813 }
1795 if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| {1814 if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| {
1796 try comp.queueJobs(&.{.{ .musl_crt_file = f }});1815 try comp.queueJobs(&.{.{ .musl_crt_file = f }});
1816 comp.remaining_prelink_tasks += 1;
1797 }1817 }
1798 try comp.queueJobs(&.{.{ .musl_crt_file = switch (comp.config.link_mode) {1818 try comp.queueJobs(&.{.{ .musl_crt_file = switch (comp.config.link_mode) {
1799 .static => .libc_a,1819 .static => .libc_a,
1800 .dynamic => .libc_so,1820 .dynamic => .libc_so,
1801 } }});1821 } }});
1822 comp.remaining_prelink_tasks += 1;
1802 } else if (target.isGnuLibC()) {1823 } else if (target.isGnuLibC()) {
1803 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;1824 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
18041825
...@@ -1807,14 +1828,18 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1807,14 +1828,18 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1807 .{ .glibc_crt_file = .crti_o },1828 .{ .glibc_crt_file = .crti_o },
1808 .{ .glibc_crt_file = .crtn_o },1829 .{ .glibc_crt_file = .crtn_o },
1809 });1830 });
1831 comp.remaining_prelink_tasks += 2;
1810 }1832 }
1811 if (glibc.needsCrt0(comp.config.output_mode)) |f| {1833 if (glibc.needsCrt0(comp.config.output_mode)) |f| {
1812 try comp.queueJobs(&.{.{ .glibc_crt_file = f }});1834 try comp.queueJobs(&.{.{ .glibc_crt_file = f }});
1835 comp.remaining_prelink_tasks += 1;
1813 }1836 }
1814 try comp.queueJobs(&[_]Job{1837 try comp.queueJobs(&[_]Job{
1815 .{ .glibc_shared_objects = {} },1838 .{ .glibc_shared_objects = {} },
1816 .{ .glibc_crt_file = .libc_nonshared_a },1839 .{ .glibc_crt_file = .libc_nonshared_a },
1817 });1840 });
1841 comp.remaining_prelink_tasks += 1;
1842 comp.remaining_prelink_tasks += glibc.sharedObjectsCount(&target);
1818 } else if (target.isWasm() and target.os.tag == .wasi) {1843 } else if (target.isWasm() and target.os.tag == .wasi) {
1819 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;1844 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
18201845
...@@ -1822,11 +1847,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1822,11 +1847,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1822 try comp.queueJob(.{1847 try comp.queueJob(.{
1823 .wasi_libc_crt_file = crt_file,1848 .wasi_libc_crt_file = crt_file,
1824 });1849 });
1850 comp.remaining_prelink_tasks += 1;
1825 }1851 }
1826 try comp.queueJobs(&[_]Job{1852 try comp.queueJobs(&[_]Job{
1827 .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) },1853 .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) },
1828 .{ .wasi_libc_crt_file = .libc_a },1854 .{ .wasi_libc_crt_file = .libc_a },
1829 });1855 });
1856 comp.remaining_prelink_tasks += 2;
1830 } else if (target.isMinGW()) {1857 } else if (target.isMinGW()) {
1831 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;1858 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
18321859
...@@ -1835,6 +1862,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1835,6 +1862,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1835 .{ .mingw_crt_file = .mingw32_lib },1862 .{ .mingw_crt_file = .mingw32_lib },
1836 crt_job,1863 crt_job,
1837 });1864 });
1865 comp.remaining_prelink_tasks += 2;
18381866
1839 // When linking mingw-w64 there are some import libs we always need.1867 // When linking mingw-w64 there are some import libs we always need.
1840 try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);1868 try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);
...@@ -1846,6 +1874,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1846,6 +1874,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1846 }1874 }
1847 } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) {1875 } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) {
1848 try comp.queueJob(.{ .zig_libc = {} });1876 try comp.queueJob(.{ .zig_libc = {} });
1877 comp.remaining_prelink_tasks += 1;
1849 } else {1878 } else {
1850 return error.LibCUnavailable;1879 return error.LibCUnavailable;
1851 }1880 }
...@@ -1860,13 +1889,16 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1860,13 +1889,16 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1860 }1889 }
1861 if (comp.wantBuildLibUnwindFromSource()) {1890 if (comp.wantBuildLibUnwindFromSource()) {
1862 try comp.queueJob(.{ .libunwind = {} });1891 try comp.queueJob(.{ .libunwind = {} });
1892 comp.remaining_prelink_tasks += 1;
1863 }1893 }
1864 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {1894 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
1865 try comp.queueJob(.libcxx);1895 try comp.queueJob(.libcxx);
1866 try comp.queueJob(.libcxxabi);1896 try comp.queueJob(.libcxxabi);
1897 comp.remaining_prelink_tasks += 2;
1867 }1898 }
1868 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) {1899 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) {
1869 try comp.queueJob(.libtsan);1900 try comp.queueJob(.libtsan);
1901 comp.remaining_prelink_tasks += 1;
1870 }1902 }
18711903
1872 if (target.isMinGW() and comp.config.any_non_single_threaded) {1904 if (target.isMinGW() and comp.config.any_non_single_threaded) {
...@@ -1885,22 +1917,27 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1885,22 +1917,27 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1885 if (is_exe_or_dyn_lib) {1917 if (is_exe_or_dyn_lib) {
1886 log.debug("queuing a job to build compiler_rt_lib", .{});1918 log.debug("queuing a job to build compiler_rt_lib", .{});
1887 comp.job_queued_compiler_rt_lib = true;1919 comp.job_queued_compiler_rt_lib = true;
1920 comp.remaining_prelink_tasks += 1;
1888 } else if (output_mode != .Obj) {1921 } else if (output_mode != .Obj) {
1889 log.debug("queuing a job to build compiler_rt_obj", .{});1922 log.debug("queuing a job to build compiler_rt_obj", .{});
1890 // In this case we are making a static library, so we ask1923 // In this case we are making a static library, so we ask
1891 // for a compiler-rt object to put in it.1924 // for a compiler-rt object to put in it.
1892 comp.job_queued_compiler_rt_obj = true;1925 comp.job_queued_compiler_rt_obj = true;
1926 comp.remaining_prelink_tasks += 1;
1893 }1927 }
1894 }1928 }
18951929
1896 if (is_exe_or_dyn_lib and comp.config.any_fuzz and capable_of_building_compiler_rt) {1930 if (is_exe_or_dyn_lib and comp.config.any_fuzz and capable_of_building_compiler_rt) {
1897 log.debug("queuing a job to build libfuzzer", .{});1931 log.debug("queuing a job to build libfuzzer", .{});
1898 comp.job_queued_fuzzer_lib = true;1932 comp.job_queued_fuzzer_lib = true;
1933 comp.remaining_prelink_tasks += 1;
1899 }1934 }
1900 }1935 }
19011936
1902 try comp.link_task_queue.shared.append(gpa, .load_explicitly_provided);1937 try comp.link_task_queue.shared.append(gpa, .load_explicitly_provided);
1938 comp.remaining_prelink_tasks += 1;
1903 }1939 }
1940 log.debug("total prelink tasks: {d}", .{comp.remaining_prelink_tasks});
19041941
1905 return comp;1942 return comp;
1906}1943}
...@@ -1976,6 +2013,7 @@ pub fn destroy(comp: *Compilation) void {...@@ -1976,6 +2013,7 @@ pub fn destroy(comp: *Compilation) void {
19762013
1977 comp.link_diags.deinit();2014 comp.link_diags.deinit();
1978 comp.link_task_queue.deinit(gpa);2015 comp.link_task_queue.deinit(gpa);
2016 comp.link_task_queue_postponed.deinit(gpa);
19792017
1980 comp.clearMiscFailures();2018 comp.clearMiscFailures();
19812019
...@@ -2438,9 +2476,8 @@ fn flush(...@@ -2438,9 +2476,8 @@ fn flush(
2438 if (comp.bin_file) |lf| {2476 if (comp.bin_file) |lf| {
2439 // This is needed before reading the error flags.2477 // This is needed before reading the error flags.
2440 lf.flush(arena, tid, prog_node) catch |err| switch (err) {2478 lf.flush(arena, tid, prog_node) catch |err| switch (err) {
2441 error.FlushFailure, error.LinkFailure => {}, // error reported through link_diags.flags2479 error.LinkFailure => {}, // Already reported.
2442 error.LLDReportedFailure => {}, // error reported via lockAndParseLldStderr2480 error.OutOfMemory => return error.OutOfMemory,
2443 else => |e| return e,
2444 };2481 };
2445 }2482 }
24462483
...@@ -3025,8 +3062,120 @@ pub fn saveState(comp: *Compilation) !void {...@@ -3025,8 +3062,120 @@ pub fn saveState(comp: *Compilation) !void {
3025 //// TODO: compilation errors3062 //// TODO: compilation errors
3026 //// TODO: namespaces3063 //// TODO: namespaces
3027 //// TODO: decls3064 //// TODO: decls
3028 //// TODO: linker state
3029 }3065 }
3066
3067 // linker state
3068 switch (lf.tag) {
3069 .wasm => {
3070 const wasm = lf.cast(.wasm).?;
3071 const is_obj = comp.config.output_mode == .Obj;
3072 try bufs.ensureUnusedCapacity(85);
3073 addBuf(&bufs, wasm.string_bytes.items);
3074 // TODO make it well-defined memory layout
3075 //addBuf(&bufs, mem.sliceAsBytes(wasm.objects.items));
3076 addBuf(&bufs, mem.sliceAsBytes(wasm.func_types.keys()));
3077 addBuf(&bufs, mem.sliceAsBytes(wasm.object_function_imports.keys()));
3078 addBuf(&bufs, mem.sliceAsBytes(wasm.object_function_imports.values()));
3079 addBuf(&bufs, mem.sliceAsBytes(wasm.object_functions.items));
3080 addBuf(&bufs, mem.sliceAsBytes(wasm.object_global_imports.keys()));
3081 addBuf(&bufs, mem.sliceAsBytes(wasm.object_global_imports.values()));
3082 addBuf(&bufs, mem.sliceAsBytes(wasm.object_globals.items));
3083 addBuf(&bufs, mem.sliceAsBytes(wasm.object_table_imports.keys()));
3084 addBuf(&bufs, mem.sliceAsBytes(wasm.object_table_imports.values()));
3085 addBuf(&bufs, mem.sliceAsBytes(wasm.object_tables.items));
3086 addBuf(&bufs, mem.sliceAsBytes(wasm.object_memory_imports.keys()));
3087 addBuf(&bufs, mem.sliceAsBytes(wasm.object_memory_imports.values()));
3088 addBuf(&bufs, mem.sliceAsBytes(wasm.object_memories.items));
3089 addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.tag)));
3090 addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.offset)));
3091 // TODO handle the union safety field
3092 //addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.pointee)));
3093 addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.addend)));
3094 addBuf(&bufs, mem.sliceAsBytes(wasm.object_init_funcs.items));
3095 addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_segments.items));
3096 addBuf(&bufs, mem.sliceAsBytes(wasm.object_datas.items));
3097 addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_imports.keys()));
3098 addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_imports.values()));
3099 addBuf(&bufs, mem.sliceAsBytes(wasm.object_custom_segments.keys()));
3100 addBuf(&bufs, mem.sliceAsBytes(wasm.object_custom_segments.values()));
3101 // TODO make it well-defined memory layout
3102 // addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdats.items));
3103 addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations_table.keys()));
3104 addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations_table.values()));
3105 addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdat_symbols.items(.kind)));
3106 addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdat_symbols.items(.index)));
3107 addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.tag)));
3108 addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.offset)));
3109 // TODO handle the union safety field
3110 //addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.pointee)));
3111 addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.addend)));
3112 addBuf(&bufs, mem.sliceAsBytes(wasm.uav_fixups.items));
3113 addBuf(&bufs, mem.sliceAsBytes(wasm.nav_fixups.items));
3114 addBuf(&bufs, mem.sliceAsBytes(wasm.func_table_fixups.items));
3115 if (is_obj) {
3116 addBuf(&bufs, mem.sliceAsBytes(wasm.navs_obj.keys()));
3117 addBuf(&bufs, mem.sliceAsBytes(wasm.navs_obj.values()));
3118 addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_obj.keys()));
3119 addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_obj.values()));
3120 } else {
3121 addBuf(&bufs, mem.sliceAsBytes(wasm.navs_exe.keys()));
3122 addBuf(&bufs, mem.sliceAsBytes(wasm.navs_exe.values()));
3123 addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_exe.keys()));
3124 addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_exe.values()));
3125 }
3126 addBuf(&bufs, mem.sliceAsBytes(wasm.overaligned_uavs.keys()));
3127 addBuf(&bufs, mem.sliceAsBytes(wasm.overaligned_uavs.values()));
3128 addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_funcs.keys()));
3129 // TODO handle the union safety field
3130 // addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_funcs.values()));
3131 addBuf(&bufs, mem.sliceAsBytes(wasm.nav_exports.keys()));
3132 addBuf(&bufs, mem.sliceAsBytes(wasm.nav_exports.values()));
3133 addBuf(&bufs, mem.sliceAsBytes(wasm.uav_exports.keys()));
3134 addBuf(&bufs, mem.sliceAsBytes(wasm.uav_exports.values()));
3135 addBuf(&bufs, mem.sliceAsBytes(wasm.imports.keys()));
3136 addBuf(&bufs, mem.sliceAsBytes(wasm.missing_exports.keys()));
3137 addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.keys()));
3138 addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.values()));
3139 addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.keys()));
3140 addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.values()));
3141 addBuf(&bufs, mem.sliceAsBytes(wasm.global_exports.items));
3142 addBuf(&bufs, mem.sliceAsBytes(wasm.functions.keys()));
3143 addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.keys()));
3144 addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.values()));
3145 addBuf(&bufs, mem.sliceAsBytes(wasm.data_imports.keys()));
3146 addBuf(&bufs, mem.sliceAsBytes(wasm.data_imports.values()));
3147 addBuf(&bufs, mem.sliceAsBytes(wasm.data_segments.keys()));
3148 addBuf(&bufs, mem.sliceAsBytes(wasm.globals.keys()));
3149 addBuf(&bufs, mem.sliceAsBytes(wasm.global_imports.keys()));
3150 addBuf(&bufs, mem.sliceAsBytes(wasm.global_imports.values()));
3151 addBuf(&bufs, mem.sliceAsBytes(wasm.tables.keys()));
3152 addBuf(&bufs, mem.sliceAsBytes(wasm.table_imports.keys()));
3153 addBuf(&bufs, mem.sliceAsBytes(wasm.table_imports.values()));
3154 addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_indirect_function_set.keys()));
3155 addBuf(&bufs, mem.sliceAsBytes(wasm.object_indirect_function_import_set.keys()));
3156 addBuf(&bufs, mem.sliceAsBytes(wasm.object_indirect_function_set.keys()));
3157 addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.tag)));
3158 // TODO handle the union safety field
3159 //addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.data)));
3160 addBuf(&bufs, mem.sliceAsBytes(wasm.mir_extra.items));
3161 addBuf(&bufs, mem.sliceAsBytes(wasm.all_zcu_locals.items));
3162 addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_bytes.items));
3163 addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_offs.items));
3164
3165 // TODO add as header fields
3166 // entry_resolution: FunctionImport.Resolution
3167 // function_exports_len: u32
3168 // global_exports_len: u32
3169 // functions_end_prelink: u32
3170 // globals_end_prelink: u32
3171 // error_name_table_ref_count: u32
3172 // tag_name_table_ref_count: u32
3173 // any_tls_relocs: bool
3174 // any_passive_inits: bool
3175 },
3176 else => log.err("TODO implement saving linker state for {s}", .{@tagName(lf.tag)}),
3177 }
3178
3030 var basename_buf: [255]u8 = undefined;3179 var basename_buf: [255]u8 = undefined;
3031 const basename = std.fmt.bufPrint(&basename_buf, "{s}.zcs", .{3180 const basename = std.fmt.bufPrint(&basename_buf, "{s}.zcs", .{
3032 comp.root_name,3181 comp.root_name,
...@@ -3209,6 +3358,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3209,6 +3358,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3209 if (!zcu.navFileScope(nav).okToReportErrors()) continue;3358 if (!zcu.navFileScope(nav).okToReportErrors()) continue;
3210 try addModuleErrorMsg(zcu, &bundle, error_msg.*);3359 try addModuleErrorMsg(zcu, &bundle, error_msg.*);
3211 }3360 }
3361 for (zcu.failed_types.keys(), zcu.failed_types.values()) |ty_index, error_msg| {
3362 if (!zcu.typeFileScope(ty_index).okToReportErrors()) continue;
3363 try addModuleErrorMsg(zcu, &bundle, error_msg.*);
3364 }
3212 for (zcu.failed_exports.values()) |value| {3365 for (zcu.failed_exports.values()) |value| {
3213 try addModuleErrorMsg(zcu, &bundle, value.*);3366 try addModuleErrorMsg(zcu, &bundle, value.*);
3214 }3367 }
...@@ -3252,7 +3405,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3252,7 +3405,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3252 }));3405 }));
3253 }3406 }
32543407
3255 try comp.link_diags.addMessagesToBundle(&bundle);3408 try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file);
32563409
3257 if (comp.zcu) |zcu| {3410 if (comp.zcu) |zcu| {
3258 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {3411 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
...@@ -3524,9 +3677,9 @@ pub fn performAllTheWork(...@@ -3524,9 +3677,9 @@ pub fn performAllTheWork(
35243677
3525 defer if (comp.zcu) |zcu| {3678 defer if (comp.zcu) |zcu| {
3526 zcu.sema_prog_node.end();3679 zcu.sema_prog_node.end();
3527 zcu.sema_prog_node = std.Progress.Node.none;3680 zcu.sema_prog_node = .none;
3528 zcu.codegen_prog_node.end();3681 zcu.codegen_prog_node.end();
3529 zcu.codegen_prog_node = std.Progress.Node.none;3682 zcu.codegen_prog_node = .none;
35303683
3531 zcu.generation += 1;3684 zcu.generation += 1;
3532 };3685 };
...@@ -3659,7 +3812,7 @@ fn performAllTheWorkInner(...@@ -3659,7 +3812,7 @@ fn performAllTheWorkInner(
3659 try zcu.flushRetryableFailures();3812 try zcu.flushRetryableFailures();
36603813
3661 zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);3814 zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
3662 zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);3815 zcu.codegen_prog_node = if (comp.bin_file != null) main_progress_node.start("Code Generation", 0) else .none;
3663 }3816 }
36643817
3665 if (!comp.separateCodegenThreadOk()) {3818 if (!comp.separateCodegenThreadOk()) {
...@@ -3689,6 +3842,8 @@ fn performAllTheWorkInner(...@@ -3689,6 +3842,8 @@ fn performAllTheWorkInner(
3689 });3842 });
3690 continue;3843 continue;
3691 }3844 }
3845 zcu.sema_prog_node.end();
3846 zcu.sema_prog_node = .none;
3692 }3847 }
3693 break;3848 break;
3694 }3849 }
...@@ -3962,6 +4117,7 @@ fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) voi...@@ -3962,6 +4117,7 @@ fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) voi
3962 if (comp.separateCodegenThreadOk()) {4117 if (comp.separateCodegenThreadOk()) {
3963 comp.queueLinkTasks(&.{link_task});4118 comp.queueLinkTasks(&.{link_task});
3964 } else {4119 } else {
4120 assert(comp.remaining_prelink_tasks == 0);
3965 link.doTask(comp, tid, link_task);4121 link.doTask(comp, tid, link_task);
3966 }4122 }
3967}4123}
src/InternPool.zig+41-1
...@@ -552,6 +552,15 @@ pub const Nav = struct {...@@ -552,6 +552,15 @@ pub const Nav = struct {
552 };552 };
553 }553 }
554554
555 /// This function is intended to be used by code generation, since semantic
556 /// analysis will ensure that any `Nav` which is potentially `extern` is
557 /// fully resolved.
558 /// Asserts that `status == .fully_resolved`.
559 pub fn getResolvedExtern(nav: Nav, ip: *const InternPool) ?Key.Extern {
560 assert(nav.status == .fully_resolved);
561 return nav.getExtern(ip);
562 }
563
555 /// Always returns `null` for `status == .type_resolved`. This function is inteded564 /// Always returns `null` for `status == .type_resolved`. This function is inteded
556 /// to be used by code generation, since semantic analysis will ensure that any `Nav`565 /// to be used by code generation, since semantic analysis will ensure that any `Nav`
557 /// which is potentially `extern` is fully resolved.566 /// which is potentially `extern` is fully resolved.
...@@ -585,6 +594,15 @@ pub const Nav = struct {...@@ -585,6 +594,15 @@ pub const Nav = struct {
585 };594 };
586 }595 }
587596
597 /// Asserts that `status != .unresolved`.
598 pub fn getLinkSection(nav: Nav) OptionalNullTerminatedString {
599 return switch (nav.status) {
600 .unresolved => unreachable,
601 .type_resolved => |r| r.@"linksection",
602 .fully_resolved => |r| r.@"linksection",
603 };
604 }
605
588 /// Asserts that `status != .unresolved`.606 /// Asserts that `status != .unresolved`.
589 pub fn isThreadlocal(nav: Nav, ip: *const InternPool) bool {607 pub fn isThreadlocal(nav: Nav, ip: *const InternPool) bool {
590 return switch (nav.status) {608 return switch (nav.status) {
...@@ -598,6 +616,20 @@ pub const Nav = struct {...@@ -598,6 +616,20 @@ pub const Nav = struct {
598 };616 };
599 }617 }
600618
619 pub fn isFn(nav: Nav, ip: *const InternPool) bool {
620 return switch (nav.status) {
621 .unresolved => unreachable,
622 .type_resolved => |r| {
623 const tag = ip.zigTypeTagOrPoison(r.type) catch unreachable;
624 return tag == .@"fn";
625 },
626 .fully_resolved => |r| {
627 const tag = ip.zigTypeTagOrPoison(ip.typeOf(r.val)) catch unreachable;
628 return tag == .@"fn";
629 },
630 };
631 }
632
601 /// If this returns `true`, then a pointer to this `Nav` might actually be encoded as a pointer633 /// If this returns `true`, then a pointer to this `Nav` might actually be encoded as a pointer
602 /// to some other `Nav` due to an extern definition or extern alias (see #21027).634 /// to some other `Nav` due to an extern definition or extern alias (see #21027).
603 /// This query is valid on `Nav`s for whom only the type is resolved.635 /// This query is valid on `Nav`s for whom only the type is resolved.
...@@ -3360,6 +3392,10 @@ pub const LoadedUnionType = struct {...@@ -3360,6 +3392,10 @@ pub const LoadedUnionType = struct {
3360 return flags.status == .field_types_wip;3392 return flags.status == .field_types_wip;
3361 }3393 }
33623394
3395 pub fn requiresComptime(u: LoadedUnionType, ip: *const InternPool) RequiresComptime {
3396 return u.flagsUnordered(ip).requires_comptime;
3397 }
3398
3363 pub fn setRequiresComptimeWip(u: LoadedUnionType, ip: *InternPool) RequiresComptime {3399 pub fn setRequiresComptimeWip(u: LoadedUnionType, ip: *InternPool) RequiresComptime {
3364 const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;3400 const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
3365 extra_mutex.lock();3401 extra_mutex.lock();
...@@ -4014,7 +4050,7 @@ pub const LoadedStructType = struct {...@@ -4014,7 +4050,7 @@ pub const LoadedStructType = struct {
4014 }4050 }
4015 }4051 }
40164052
4017 pub fn haveLayout(s: LoadedStructType, ip: *InternPool) bool {4053 pub fn haveLayout(s: LoadedStructType, ip: *const InternPool) bool {
4018 return switch (s.layout) {4054 return switch (s.layout) {
4019 .@"packed" => s.backingIntTypeUnordered(ip) != .none,4055 .@"packed" => s.backingIntTypeUnordered(ip) != .none,
4020 .auto, .@"extern" => s.flagsUnordered(ip).layout_resolved,4056 .auto, .@"extern" => s.flagsUnordered(ip).layout_resolved,
...@@ -11797,6 +11833,10 @@ pub fn toEnum(ip: *const InternPool, comptime E: type, i: Index) E {...@@ -11797,6 +11833,10 @@ pub fn toEnum(ip: *const InternPool, comptime E: type, i: Index) E {
11797 return @enumFromInt(ip.indexToKey(int).int.storage.u64);11833 return @enumFromInt(ip.indexToKey(int).int.storage.u64);
11798}11834}
1179911835
11836pub fn toFunc(ip: *const InternPool, i: Index) Key.Func {
11837 return ip.indexToKey(i).func;
11838}
11839
11800pub fn aggregateTypeLen(ip: *const InternPool, ty: Index) u64 {11840pub fn aggregateTypeLen(ip: *const InternPool, ty: Index) u64 {
11801 return switch (ip.indexToKey(ty)) {11841 return switch (ip.indexToKey(ty)) {
11802 .struct_type => ip.loadStructType(ty).field_types.len,11842 .struct_type => ip.loadStructType(ty).field_types.len,
src/Sema.zig+5-5
...@@ -38298,7 +38298,7 @@ pub fn flushExports(sema: *Sema) !void {...@@ -38298,7 +38298,7 @@ pub fn flushExports(sema: *Sema) !void {
38298 // So, pick up and delete any existing exports. This strategy performs38298 // So, pick up and delete any existing exports. This strategy performs
38299 // redundant work, but that's okay, because this case is exceedingly rare.38299 // redundant work, but that's okay, because this case is exceedingly rare.
38300 if (zcu.single_exports.get(sema.owner)) |export_idx| {38300 if (zcu.single_exports.get(sema.owner)) |export_idx| {
38301 try sema.exports.append(gpa, zcu.all_exports.items[export_idx]);38301 try sema.exports.append(gpa, export_idx.ptr(zcu).*);
38302 } else if (zcu.multi_exports.get(sema.owner)) |info| {38302 } else if (zcu.multi_exports.get(sema.owner)) |info| {
38303 try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]);38303 try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]);
38304 }38304 }
...@@ -38307,12 +38307,12 @@ pub fn flushExports(sema: *Sema) !void {...@@ -38307,12 +38307,12 @@ pub fn flushExports(sema: *Sema) !void {
38307 // `sema.exports` is completed; store the data into the `Zcu`.38307 // `sema.exports` is completed; store the data into the `Zcu`.
38308 if (sema.exports.items.len == 1) {38308 if (sema.exports.items.len == 1) {
38309 try zcu.single_exports.ensureUnusedCapacity(gpa, 1);38309 try zcu.single_exports.ensureUnusedCapacity(gpa, 1);
38310 const export_idx = zcu.free_exports.popOrNull() orelse idx: {38310 const export_idx: Zcu.Export.Index = zcu.free_exports.popOrNull() orelse idx: {
38311 _ = try zcu.all_exports.addOne(gpa);38311 _ = try zcu.all_exports.addOne(gpa);
38312 break :idx zcu.all_exports.items.len - 1;38312 break :idx @enumFromInt(zcu.all_exports.items.len - 1);
38313 };38313 };
38314 zcu.all_exports.items[export_idx] = sema.exports.items[0];38314 export_idx.ptr(zcu).* = sema.exports.items[0];
38315 zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, @intCast(export_idx));38315 zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, export_idx);
38316 } else {38316 } else {
38317 try zcu.multi_exports.ensureUnusedCapacity(gpa, 1);38317 try zcu.multi_exports.ensureUnusedCapacity(gpa, 1);
38318 const exports_base = zcu.all_exports.items.len;38318 const exports_base = zcu.all_exports.items.len;
src/Type.zig+108-85
...@@ -441,7 +441,7 @@ pub fn toValue(self: Type) Value {...@@ -441,7 +441,7 @@ pub fn toValue(self: Type) Value {
441441
442const RuntimeBitsError = SemaError || error{NeedLazy};442const RuntimeBitsError = SemaError || error{NeedLazy};
443443
444pub fn hasRuntimeBits(ty: Type, zcu: *Zcu) bool {444pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool {
445 return hasRuntimeBitsInner(ty, false, .eager, zcu, {}) catch unreachable;445 return hasRuntimeBitsInner(ty, false, .eager, zcu, {}) catch unreachable;
446}446}
447447
...@@ -452,7 +452,7 @@ pub fn hasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool {...@@ -452,7 +452,7 @@ pub fn hasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool {
452 };452 };
453}453}
454454
455pub fn hasRuntimeBitsIgnoreComptime(ty: Type, zcu: *Zcu) bool {455pub fn hasRuntimeBitsIgnoreComptime(ty: Type, zcu: *const Zcu) bool {
456 return hasRuntimeBitsInner(ty, true, .eager, zcu, {}) catch unreachable;456 return hasRuntimeBitsInner(ty, true, .eager, zcu, {}) catch unreachable;
457}457}
458458
...@@ -471,7 +471,7 @@ pub fn hasRuntimeBitsInner(...@@ -471,7 +471,7 @@ pub fn hasRuntimeBitsInner(
471 ty: Type,471 ty: Type,
472 ignore_comptime_only: bool,472 ignore_comptime_only: bool,
473 comptime strat: ResolveStratLazy,473 comptime strat: ResolveStratLazy,
474 zcu: *Zcu,474 zcu: strat.ZcuPtr(),
475 tid: strat.Tid(),475 tid: strat.Tid(),
476) RuntimeBitsError!bool {476) RuntimeBitsError!bool {
477 const ip = &zcu.intern_pool;477 const ip = &zcu.intern_pool;
...@@ -560,7 +560,7 @@ pub fn hasRuntimeBitsInner(...@@ -560,7 +560,7 @@ pub fn hasRuntimeBitsInner(
560 },560 },
561 .struct_type => {561 .struct_type => {
562 const struct_type = ip.loadStructType(ty.toIntern());562 const struct_type = ip.loadStructType(ty.toIntern());
563 if (struct_type.assumeRuntimeBitsIfFieldTypesWip(ip)) {563 if (strat != .eager and struct_type.assumeRuntimeBitsIfFieldTypesWip(ip)) {
564 // In this case, we guess that hasRuntimeBits() for this type is true,564 // In this case, we guess that hasRuntimeBits() for this type is true,
565 // and then later if our guess was incorrect, we emit a compile error.565 // and then later if our guess was incorrect, we emit a compile error.
566 return true;566 return true;
...@@ -596,7 +596,7 @@ pub fn hasRuntimeBitsInner(...@@ -596,7 +596,7 @@ pub fn hasRuntimeBitsInner(
596 const union_type = ip.loadUnionType(ty.toIntern());596 const union_type = ip.loadUnionType(ty.toIntern());
597 const union_flags = union_type.flagsUnordered(ip);597 const union_flags = union_type.flagsUnordered(ip);
598 switch (union_flags.runtime_tag) {598 switch (union_flags.runtime_tag) {
599 .none => {599 .none => if (strat != .eager) {
600 // In this case, we guess that hasRuntimeBits() for this type is true,600 // In this case, we guess that hasRuntimeBits() for this type is true,
601 // and then later if our guess was incorrect, we emit a compile error.601 // and then later if our guess was incorrect, we emit a compile error.
602 if (union_type.assumeRuntimeBitsIfFieldTypesWip(ip)) return true;602 if (union_type.assumeRuntimeBitsIfFieldTypesWip(ip)) return true;
...@@ -774,7 +774,7 @@ pub fn fnHasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool {...@@ -774,7 +774,7 @@ pub fn fnHasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool {
774pub fn fnHasRuntimeBitsInner(774pub fn fnHasRuntimeBitsInner(
775 ty: Type,775 ty: Type,
776 comptime strat: ResolveStrat,776 comptime strat: ResolveStrat,
777 zcu: *Zcu,777 zcu: strat.ZcuPtr(),
778 tid: strat.Tid(),778 tid: strat.Tid(),
779) SemaError!bool {779) SemaError!bool {
780 const fn_info = zcu.typeToFunc(ty).?;780 const fn_info = zcu.typeToFunc(ty).?;
...@@ -815,7 +815,7 @@ pub fn ptrAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment {...@@ -815,7 +815,7 @@ pub fn ptrAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment {
815pub fn ptrAlignmentInner(815pub fn ptrAlignmentInner(
816 ty: Type,816 ty: Type,
817 comptime strat: ResolveStrat,817 comptime strat: ResolveStrat,
818 zcu: *Zcu,818 zcu: strat.ZcuPtr(),
819 tid: strat.Tid(),819 tid: strat.Tid(),
820) !Alignment {820) !Alignment {
821 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {821 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
...@@ -868,14 +868,25 @@ pub const ResolveStratLazy = enum {...@@ -868,14 +868,25 @@ pub const ResolveStratLazy = enum {
868 /// This should typically be used from semantic analysis.868 /// This should typically be used from semantic analysis.
869 sema,869 sema,
870870
871 pub fn Tid(comptime strat: ResolveStratLazy) type {871 pub fn Tid(strat: ResolveStratLazy) type {
872 return switch (strat) {872 return switch (strat) {
873 .lazy, .sema => Zcu.PerThread.Id,873 .lazy, .sema => Zcu.PerThread.Id,
874 .eager => void,874 .eager => void,
875 };875 };
876 }876 }
877877
878 pub fn pt(comptime strat: ResolveStratLazy, zcu: *Zcu, tid: strat.Tid()) switch (strat) {878 pub fn ZcuPtr(strat: ResolveStratLazy) type {
879 return switch (strat) {
880 .eager => *const Zcu,
881 .sema, .lazy => *Zcu,
882 };
883 }
884
885 pub fn pt(
886 comptime strat: ResolveStratLazy,
887 zcu: strat.ZcuPtr(),
888 tid: strat.Tid(),
889 ) switch (strat) {
879 .lazy, .sema => Zcu.PerThread,890 .lazy, .sema => Zcu.PerThread,
880 .eager => void,891 .eager => void,
881 } {892 } {
...@@ -896,14 +907,21 @@ pub const ResolveStrat = enum {...@@ -896,14 +907,21 @@ pub const ResolveStrat = enum {
896 /// This should typically be used from semantic analysis.907 /// This should typically be used from semantic analysis.
897 sema,908 sema,
898909
899 pub fn Tid(comptime strat: ResolveStrat) type {910 pub fn Tid(strat: ResolveStrat) type {
900 return switch (strat) {911 return switch (strat) {
901 .sema => Zcu.PerThread.Id,912 .sema => Zcu.PerThread.Id,
902 .normal => void,913 .normal => void,
903 };914 };
904 }915 }
905916
906 pub fn pt(comptime strat: ResolveStrat, zcu: *Zcu, tid: strat.Tid()) switch (strat) {917 pub fn ZcuPtr(strat: ResolveStrat) type {
918 return switch (strat) {
919 .normal => *const Zcu,
920 .sema => *Zcu,
921 };
922 }
923
924 pub fn pt(comptime strat: ResolveStrat, zcu: strat.ZcuPtr(), tid: strat.Tid()) switch (strat) {
907 .sema => Zcu.PerThread,925 .sema => Zcu.PerThread,
908 .normal => void,926 .normal => void,
909 } {927 } {
...@@ -922,7 +940,7 @@ pub const ResolveStrat = enum {...@@ -922,7 +940,7 @@ pub const ResolveStrat = enum {
922};940};
923941
924/// Never returns `none`. Asserts that all necessary type resolution is already done.942/// Never returns `none`. Asserts that all necessary type resolution is already done.
925pub fn abiAlignment(ty: Type, zcu: *Zcu) Alignment {943pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {
926 return (ty.abiAlignmentInner(.eager, zcu, {}) catch unreachable).scalar;944 return (ty.abiAlignmentInner(.eager, zcu, {}) catch unreachable).scalar;
927}945}
928946
...@@ -939,7 +957,7 @@ pub fn abiAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment {...@@ -939,7 +957,7 @@ pub fn abiAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment {
939pub fn abiAlignmentInner(957pub fn abiAlignmentInner(
940 ty: Type,958 ty: Type,
941 comptime strat: ResolveStratLazy,959 comptime strat: ResolveStratLazy,
942 zcu: *Zcu,960 zcu: strat.ZcuPtr(),
943 tid: strat.Tid(),961 tid: strat.Tid(),
944) SemaError!AbiAlignmentInner {962) SemaError!AbiAlignmentInner {
945 const pt = strat.pt(zcu, tid);963 const pt = strat.pt(zcu, tid);
...@@ -1156,7 +1174,7 @@ pub fn abiAlignmentInner(...@@ -1156,7 +1174,7 @@ pub fn abiAlignmentInner(
1156fn abiAlignmentInnerErrorUnion(1174fn abiAlignmentInnerErrorUnion(
1157 ty: Type,1175 ty: Type,
1158 comptime strat: ResolveStratLazy,1176 comptime strat: ResolveStratLazy,
1159 zcu: *Zcu,1177 zcu: strat.ZcuPtr(),
1160 tid: strat.Tid(),1178 tid: strat.Tid(),
1161 payload_ty: Type,1179 payload_ty: Type,
1162) SemaError!AbiAlignmentInner {1180) SemaError!AbiAlignmentInner {
...@@ -1198,7 +1216,7 @@ fn abiAlignmentInnerErrorUnion(...@@ -1198,7 +1216,7 @@ fn abiAlignmentInnerErrorUnion(
1198fn abiAlignmentInnerOptional(1216fn abiAlignmentInnerOptional(
1199 ty: Type,1217 ty: Type,
1200 comptime strat: ResolveStratLazy,1218 comptime strat: ResolveStratLazy,
1201 zcu: *Zcu,1219 zcu: strat.ZcuPtr(),
1202 tid: strat.Tid(),1220 tid: strat.Tid(),
1203) SemaError!AbiAlignmentInner {1221) SemaError!AbiAlignmentInner {
1204 const pt = strat.pt(zcu, tid);1222 const pt = strat.pt(zcu, tid);
...@@ -1244,7 +1262,7 @@ const AbiSizeInner = union(enum) {...@@ -1244,7 +1262,7 @@ const AbiSizeInner = union(enum) {
12441262
1245/// Asserts the type has the ABI size already resolved.1263/// Asserts the type has the ABI size already resolved.
1246/// Types that return false for hasRuntimeBits() return 0.1264/// Types that return false for hasRuntimeBits() return 0.
1247pub fn abiSize(ty: Type, zcu: *Zcu) u64 {1265pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {
1248 return (abiSizeInner(ty, .eager, zcu, {}) catch unreachable).scalar;1266 return (abiSizeInner(ty, .eager, zcu, {}) catch unreachable).scalar;
1249}1267}
12501268
...@@ -1269,7 +1287,7 @@ pub fn abiSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 {...@@ -1269,7 +1287,7 @@ pub fn abiSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 {
1269pub fn abiSizeInner(1287pub fn abiSizeInner(
1270 ty: Type,1288 ty: Type,
1271 comptime strat: ResolveStratLazy,1289 comptime strat: ResolveStratLazy,
1272 zcu: *Zcu,1290 zcu: strat.ZcuPtr(),
1273 tid: strat.Tid(),1291 tid: strat.Tid(),
1274) SemaError!AbiSizeInner {1292) SemaError!AbiSizeInner {
1275 const target = zcu.getTarget();1293 const target = zcu.getTarget();
...@@ -1542,7 +1560,7 @@ pub fn abiSizeInner(...@@ -1542,7 +1560,7 @@ pub fn abiSizeInner(
1542fn abiSizeInnerOptional(1560fn abiSizeInnerOptional(
1543 ty: Type,1561 ty: Type,
1544 comptime strat: ResolveStratLazy,1562 comptime strat: ResolveStratLazy,
1545 zcu: *Zcu,1563 zcu: strat.ZcuPtr(),
1546 tid: strat.Tid(),1564 tid: strat.Tid(),
1547) SemaError!AbiSizeInner {1565) SemaError!AbiSizeInner {
1548 const child_ty = ty.optionalChild(zcu);1566 const child_ty = ty.optionalChild(zcu);
...@@ -1701,7 +1719,7 @@ pub fn maxIntAlignment(target: std.Target, use_llvm: bool) u16 {...@@ -1701,7 +1719,7 @@ pub fn maxIntAlignment(target: std.Target, use_llvm: bool) u16 {
1701 };1719 };
1702}1720}
17031721
1704pub fn bitSize(ty: Type, zcu: *Zcu) u64 {1722pub fn bitSize(ty: Type, zcu: *const Zcu) u64 {
1705 return bitSizeInner(ty, .normal, zcu, {}) catch unreachable;1723 return bitSizeInner(ty, .normal, zcu, {}) catch unreachable;
1706}1724}
17071725
...@@ -1712,7 +1730,7 @@ pub fn bitSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 {...@@ -1712,7 +1730,7 @@ pub fn bitSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 {
1712pub fn bitSizeInner(1730pub fn bitSizeInner(
1713 ty: Type,1731 ty: Type,
1714 comptime strat: ResolveStrat,1732 comptime strat: ResolveStrat,
1715 zcu: *Zcu,1733 zcu: strat.ZcuPtr(),
1716 tid: strat.Tid(),1734 tid: strat.Tid(),
1717) SemaError!u64 {1735) SemaError!u64 {
1718 const target = zcu.getTarget();1736 const target = zcu.getTarget();
...@@ -2148,7 +2166,7 @@ pub fn unionBackingType(ty: Type, pt: Zcu.PerThread) !Type {...@@ -2148,7 +2166,7 @@ pub fn unionBackingType(ty: Type, pt: Zcu.PerThread) !Type {
2148 };2166 };
2149}2167}
21502168
2151pub fn unionGetLayout(ty: Type, zcu: *Zcu) Zcu.UnionLayout {2169pub fn unionGetLayout(ty: Type, zcu: *const Zcu) Zcu.UnionLayout {
2152 const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern());2170 const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern());
2153 return Type.getUnionLayout(union_obj, zcu);2171 return Type.getUnionLayout(union_obj, zcu);
2154}2172}
...@@ -2746,7 +2764,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {...@@ -2746,7 +2764,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
27462764
2747/// During semantic analysis, instead call `ty.comptimeOnlySema` which2765/// During semantic analysis, instead call `ty.comptimeOnlySema` which
2748/// resolves field types rather than asserting they are already resolved.2766/// resolves field types rather than asserting they are already resolved.
2749pub fn comptimeOnly(ty: Type, zcu: *Zcu) bool {2767pub fn comptimeOnly(ty: Type, zcu: *const Zcu) bool {
2750 return ty.comptimeOnlyInner(.normal, zcu, {}) catch unreachable;2768 return ty.comptimeOnlyInner(.normal, zcu, {}) catch unreachable;
2751}2769}
27522770
...@@ -2759,7 +2777,7 @@ pub fn comptimeOnlySema(ty: Type, pt: Zcu.PerThread) SemaError!bool {...@@ -2759,7 +2777,7 @@ pub fn comptimeOnlySema(ty: Type, pt: Zcu.PerThread) SemaError!bool {
2759pub fn comptimeOnlyInner(2777pub fn comptimeOnlyInner(
2760 ty: Type,2778 ty: Type,
2761 comptime strat: ResolveStrat,2779 comptime strat: ResolveStrat,
2762 zcu: *Zcu,2780 zcu: strat.ZcuPtr(),
2763 tid: strat.Tid(),2781 tid: strat.Tid(),
2764) SemaError!bool {2782) SemaError!bool {
2765 const ip = &zcu.intern_pool;2783 const ip = &zcu.intern_pool;
...@@ -2834,40 +2852,44 @@ pub fn comptimeOnlyInner(...@@ -2834,40 +2852,44 @@ pub fn comptimeOnlyInner(
2834 if (struct_type.layout == .@"packed")2852 if (struct_type.layout == .@"packed")
2835 return false;2853 return false;
28362854
2837 // A struct with no fields is not comptime-only.2855 return switch (strat) {
2838 return switch (struct_type.setRequiresComptimeWip(ip)) {2856 .normal => switch (struct_type.requiresComptime(ip)) {
2839 .no, .wip => false,2857 .wip => unreachable,
2840 .yes => true,2858 .no => false,
2841 .unknown => {2859 .yes => true,
2842 // Inlined `assert` so that the resolution calls below are not statically reachable.2860 .unknown => unreachable,
2843 if (strat != .sema) unreachable;2861 },
28442862 .sema => switch (struct_type.setRequiresComptimeWip(ip)) {
2845 if (struct_type.flagsUnordered(ip).field_types_wip) {2863 .no, .wip => false,
2846 struct_type.setRequiresComptime(ip, .unknown);2864 .yes => true,
2847 return false;2865 .unknown => {
2848 }2866 if (struct_type.flagsUnordered(ip).field_types_wip) {
2867 struct_type.setRequiresComptime(ip, .unknown);
2868 return false;
2869 }
28492870
2850 errdefer struct_type.setRequiresComptime(ip, .unknown);2871 errdefer struct_type.setRequiresComptime(ip, .unknown);
28512872
2852 const pt = strat.pt(zcu, tid);2873 const pt = strat.pt(zcu, tid);
2853 try ty.resolveFields(pt);2874 try ty.resolveFields(pt);
28542875
2855 for (0..struct_type.field_types.len) |i_usize| {2876 for (0..struct_type.field_types.len) |i_usize| {
2856 const i: u32 = @intCast(i_usize);2877 const i: u32 = @intCast(i_usize);
2857 if (struct_type.fieldIsComptime(ip, i)) continue;2878 if (struct_type.fieldIsComptime(ip, i)) continue;
2858 const field_ty = struct_type.field_types.get(ip)[i];2879 const field_ty = struct_type.field_types.get(ip)[i];
2859 if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) {2880 if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) {
2860 // Note that this does not cause the layout to2881 // Note that this does not cause the layout to
2861 // be considered resolved. Comptime-only types2882 // be considered resolved. Comptime-only types
2862 // still maintain a layout of their2883 // still maintain a layout of their
2863 // runtime-known fields.2884 // runtime-known fields.
2864 struct_type.setRequiresComptime(ip, .yes);2885 struct_type.setRequiresComptime(ip, .yes);
2865 return true;2886 return true;
2887 }
2866 }2888 }
2867 }
28682889
2869 struct_type.setRequiresComptime(ip, .no);2890 struct_type.setRequiresComptime(ip, .no);
2870 return false;2891 return false;
2892 },
2871 },2893 },
2872 };2894 };
2873 },2895 },
...@@ -2882,35 +2904,40 @@ pub fn comptimeOnlyInner(...@@ -2882,35 +2904,40 @@ pub fn comptimeOnlyInner(
28822904
2883 .union_type => {2905 .union_type => {
2884 const union_type = ip.loadUnionType(ty.toIntern());2906 const union_type = ip.loadUnionType(ty.toIntern());
2885 switch (union_type.setRequiresComptimeWip(ip)) {2907 return switch (strat) {
2886 .no, .wip => return false,2908 .normal => switch (union_type.requiresComptime(ip)) {
2887 .yes => return true,2909 .wip => unreachable,
2888 .unknown => {2910 .no => false,
2889 // Inlined `assert` so that the resolution calls below are not statically reachable.2911 .yes => true,
2890 if (strat != .sema) unreachable;2912 .unknown => unreachable,
28912913 },
2892 if (union_type.flagsUnordered(ip).status == .field_types_wip) {2914 .sema => switch (union_type.setRequiresComptimeWip(ip)) {
2893 union_type.setRequiresComptime(ip, .unknown);2915 .no, .wip => return false,
2894 return false;2916 .yes => return true,
2895 }2917 .unknown => {
2918 if (union_type.flagsUnordered(ip).status == .field_types_wip) {
2919 union_type.setRequiresComptime(ip, .unknown);
2920 return false;
2921 }
28962922
2897 errdefer union_type.setRequiresComptime(ip, .unknown);2923 errdefer union_type.setRequiresComptime(ip, .unknown);
28982924
2899 const pt = strat.pt(zcu, tid);2925 const pt = strat.pt(zcu, tid);
2900 try ty.resolveFields(pt);2926 try ty.resolveFields(pt);
29012927
2902 for (0..union_type.field_types.len) |field_idx| {2928 for (0..union_type.field_types.len) |field_idx| {
2903 const field_ty = union_type.field_types.get(ip)[field_idx];2929 const field_ty = union_type.field_types.get(ip)[field_idx];
2904 if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) {2930 if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) {
2905 union_type.setRequiresComptime(ip, .yes);2931 union_type.setRequiresComptime(ip, .yes);
2906 return true;2932 return true;
2933 }
2907 }2934 }
2908 }
29092935
2910 union_type.setRequiresComptime(ip, .no);2936 union_type.setRequiresComptime(ip, .no);
2911 return false;2937 return false;
2938 },
2912 },2939 },
2913 }2940 };
2914 },2941 },
29152942
2916 .opaque_type => false,2943 .opaque_type => false,
...@@ -3207,7 +3234,7 @@ pub fn fieldAlignmentInner(...@@ -3207,7 +3234,7 @@ pub fn fieldAlignmentInner(
3207 ty: Type,3234 ty: Type,
3208 index: usize,3235 index: usize,
3209 comptime strat: ResolveStrat,3236 comptime strat: ResolveStrat,
3210 zcu: *Zcu,3237 zcu: strat.ZcuPtr(),
3211 tid: strat.Tid(),3238 tid: strat.Tid(),
3212) SemaError!Alignment {3239) SemaError!Alignment {
3213 const ip = &zcu.intern_pool;3240 const ip = &zcu.intern_pool;
...@@ -3281,7 +3308,7 @@ pub fn structFieldAlignmentInner(...@@ -3281,7 +3308,7 @@ pub fn structFieldAlignmentInner(
3281 explicit_alignment: Alignment,3308 explicit_alignment: Alignment,
3282 layout: std.builtin.Type.ContainerLayout,3309 layout: std.builtin.Type.ContainerLayout,
3283 comptime strat: Type.ResolveStrat,3310 comptime strat: Type.ResolveStrat,
3284 zcu: *Zcu,3311 zcu: strat.ZcuPtr(),
3285 tid: strat.Tid(),3312 tid: strat.Tid(),
3286) SemaError!Alignment {3313) SemaError!Alignment {
3287 assert(layout != .@"packed");3314 assert(layout != .@"packed");
...@@ -3323,7 +3350,7 @@ pub fn unionFieldAlignmentInner(...@@ -3323,7 +3350,7 @@ pub fn unionFieldAlignmentInner(
3323 explicit_alignment: Alignment,3350 explicit_alignment: Alignment,
3324 layout: std.builtin.Type.ContainerLayout,3351 layout: std.builtin.Type.ContainerLayout,
3325 comptime strat: Type.ResolveStrat,3352 comptime strat: Type.ResolveStrat,
3326 zcu: *Zcu,3353 zcu: strat.ZcuPtr(),
3327 tid: strat.Tid(),3354 tid: strat.Tid(),
3328) SemaError!Alignment {3355) SemaError!Alignment {
3329 assert(layout != .@"packed");3356 assert(layout != .@"packed");
...@@ -3392,11 +3419,7 @@ pub const FieldOffset = struct {...@@ -3392,11 +3419,7 @@ pub const FieldOffset = struct {
3392};3419};
33933420
3394/// Supports structs and unions.3421/// Supports structs and unions.
3395pub fn structFieldOffset(3422pub fn structFieldOffset(ty: Type, index: usize, zcu: *const Zcu) u64 {
3396 ty: Type,
3397 index: usize,
3398 zcu: *Zcu,
3399) u64 {
3400 const ip = &zcu.intern_pool;3423 const ip = &zcu.intern_pool;
3401 switch (ip.indexToKey(ty.toIntern())) {3424 switch (ip.indexToKey(ty.toIntern())) {
3402 .struct_type => {3425 .struct_type => {
...@@ -3944,7 +3967,7 @@ fn resolveUnionInner(...@@ -3944,7 +3967,7 @@ fn resolveUnionInner(
3944 };3967 };
3945}3968}
39463969
3947pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *Zcu) Zcu.UnionLayout {3970pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout {
3948 const ip = &zcu.intern_pool;3971 const ip = &zcu.intern_pool;
3949 assert(loaded_union.haveLayout(ip));3972 assert(loaded_union.haveLayout(ip));
3950 var most_aligned_field: u32 = undefined;3973 var most_aligned_field: u32 = undefined;
src/Value.zig+4-4
...@@ -241,12 +241,12 @@ pub fn getVariable(val: Value, mod: *Zcu) ?InternPool.Key.Variable {...@@ -241,12 +241,12 @@ pub fn getVariable(val: Value, mod: *Zcu) ?InternPool.Key.Variable {
241241
242/// If the value fits in a u64, return it, otherwise null.242/// If the value fits in a u64, return it, otherwise null.
243/// Asserts not undefined.243/// Asserts not undefined.
244pub fn getUnsignedInt(val: Value, zcu: *Zcu) ?u64 {244pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 {
245 return getUnsignedIntInner(val, .normal, zcu, {}) catch unreachable;245 return getUnsignedIntInner(val, .normal, zcu, {}) catch unreachable;
246}246}
247247
248/// Asserts the value is an integer and it fits in a u64248/// Asserts the value is an integer and it fits in a u64
249pub fn toUnsignedInt(val: Value, zcu: *Zcu) u64 {249pub fn toUnsignedInt(val: Value, zcu: *const Zcu) u64 {
250 return getUnsignedInt(val, zcu).?;250 return getUnsignedInt(val, zcu).?;
251}251}
252252
...@@ -259,7 +259,7 @@ pub fn getUnsignedIntSema(val: Value, pt: Zcu.PerThread) !?u64 {...@@ -259,7 +259,7 @@ pub fn getUnsignedIntSema(val: Value, pt: Zcu.PerThread) !?u64 {
259pub fn getUnsignedIntInner(259pub fn getUnsignedIntInner(
260 val: Value,260 val: Value,
261 comptime strat: ResolveStrat,261 comptime strat: ResolveStrat,
262 zcu: *Zcu,262 zcu: strat.ZcuPtr(),
263 tid: strat.Tid(),263 tid: strat.Tid(),
264) !?u64 {264) !?u64 {
265 return switch (val.toIntern()) {265 return switch (val.toIntern()) {
...@@ -304,7 +304,7 @@ pub fn toUnsignedIntSema(val: Value, pt: Zcu.PerThread) !u64 {...@@ -304,7 +304,7 @@ pub fn toUnsignedIntSema(val: Value, pt: Zcu.PerThread) !u64 {
304}304}
305305
306/// Asserts the value is an integer and it fits in a i64306/// Asserts the value is an integer and it fits in a i64
307pub fn toSignedInt(val: Value, zcu: *Zcu) i64 {307pub fn toSignedInt(val: Value, zcu: *const Zcu) i64 {
308 return switch (val.toIntern()) {308 return switch (val.toIntern()) {
309 .bool_false => 0,309 .bool_false => 0,
310 .bool_true => 1,310 .bool_true => 1,
src/Zcu.zig+93-24
...@@ -19,8 +19,8 @@ const Ast = std.zig.Ast;...@@ -19,8 +19,8 @@ const Ast = std.zig.Ast;
19const Zcu = @This();19const Zcu = @This();
20const Compilation = @import("Compilation.zig");20const Compilation = @import("Compilation.zig");
21const Cache = std.Build.Cache;21const Cache = std.Build.Cache;
22const Value = @import("Value.zig");22pub const Value = @import("Value.zig");
23const Type = @import("Type.zig");23pub const Type = @import("Type.zig");
24const Package = @import("Package.zig");24const Package = @import("Package.zig");
25const link = @import("link.zig");25const link = @import("link.zig");
26const Air = @import("Air.zig");26const Air = @import("Air.zig");
...@@ -79,11 +79,11 @@ local_zir_cache: Compilation.Directory,...@@ -79,11 +79,11 @@ local_zir_cache: Compilation.Directory,
79all_exports: std.ArrayListUnmanaged(Export) = .empty,79all_exports: std.ArrayListUnmanaged(Export) = .empty,
80/// This is a list of free indices in `all_exports`. These indices may be reused by exports from80/// This is a list of free indices in `all_exports`. These indices may be reused by exports from
81/// future semantic analysis.81/// future semantic analysis.
82free_exports: std.ArrayListUnmanaged(u32) = .empty,82free_exports: std.ArrayListUnmanaged(Export.Index) = .empty,
83/// Maps from an `AnalUnit` which performs a single export, to the index into `all_exports` of83/// Maps from an `AnalUnit` which performs a single export, to the index into `all_exports` of
84/// the export it performs. Note that the key is not the `Decl` being exported, but the `AnalUnit`84/// the export it performs. Note that the key is not the `Decl` being exported, but the `AnalUnit`
85/// whose analysis triggered the export.85/// whose analysis triggered the export.
86single_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .empty,86single_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, Export.Index) = .empty,
87/// Like `single_exports`, but for `AnalUnit`s which perform multiple exports.87/// Like `single_exports`, but for `AnalUnit`s which perform multiple exports.
88/// The exports are `all_exports.items[index..][0..len]`.88/// The exports are `all_exports.items[index..][0..len]`.
89multi_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct {89multi_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct {
...@@ -127,6 +127,7 @@ transitive_failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .emp...@@ -127,6 +127,7 @@ transitive_failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .emp
127/// This may be a simple "value" `Nav`, or it may be a function.127/// This may be a simple "value" `Nav`, or it may be a function.
128/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator.128/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator.
129failed_codegen: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, *ErrorMsg) = .empty,129failed_codegen: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, *ErrorMsg) = .empty,
130failed_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, *ErrorMsg) = .empty,
130/// Keep track of one `@compileLog` callsite per `AnalUnit`.131/// Keep track of one `@compileLog` callsite per `AnalUnit`.
131/// The value is the source location of the `@compileLog` call, convertible to a `LazySrcLoc`.132/// The value is the source location of the `@compileLog` call, convertible to a `LazySrcLoc`.
132compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct {133compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct {
...@@ -144,8 +145,7 @@ compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct {...@@ -144,8 +145,7 @@ compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct {
144failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .empty,145failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .empty,
145/// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator.146/// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator.
146failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .empty,147failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .empty,
147/// Key is index into `all_exports`.148failed_exports: std.AutoArrayHashMapUnmanaged(Export.Index, *ErrorMsg) = .empty,
148failed_exports: std.AutoArrayHashMapUnmanaged(u32, *ErrorMsg) = .empty,
149/// If analysis failed due to a cimport error, the corresponding Clang errors149/// If analysis failed due to a cimport error, the corresponding Clang errors
150/// are stored here.150/// are stored here.
151cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = .empty,151cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = .empty,
...@@ -524,6 +524,15 @@ pub const Export = struct {...@@ -524,6 +524,15 @@ pub const Export = struct {
524 section: InternPool.OptionalNullTerminatedString = .none,524 section: InternPool.OptionalNullTerminatedString = .none,
525 visibility: std.builtin.SymbolVisibility = .default,525 visibility: std.builtin.SymbolVisibility = .default,
526 };526 };
527
528 /// Index into `all_exports`.
529 pub const Index = enum(u32) {
530 _,
531
532 pub fn ptr(i: Index, zcu: *const Zcu) *Export {
533 return &zcu.all_exports.items[@intFromEnum(i)];
534 }
535 };
527};536};
528537
529pub const Reference = struct {538pub const Reference = struct {
...@@ -2439,16 +2448,14 @@ pub fn deinit(zcu: *Zcu) void {...@@ -2439,16 +2448,14 @@ pub fn deinit(zcu: *Zcu) void {
2439 zcu.local_zir_cache.handle.close();2448 zcu.local_zir_cache.handle.close();
2440 zcu.global_zir_cache.handle.close();2449 zcu.global_zir_cache.handle.close();
24412450
2442 for (zcu.failed_analysis.values()) |value| {2451 for (zcu.failed_analysis.values()) |value| value.destroy(gpa);
2443 value.destroy(gpa);2452 for (zcu.failed_codegen.values()) |value| value.destroy(gpa);
2444 }2453 for (zcu.failed_types.values()) |value| value.destroy(gpa);
2445 for (zcu.failed_codegen.values()) |value| {
2446 value.destroy(gpa);
2447 }
2448 zcu.analysis_in_progress.deinit(gpa);2454 zcu.analysis_in_progress.deinit(gpa);
2449 zcu.failed_analysis.deinit(gpa);2455 zcu.failed_analysis.deinit(gpa);
2450 zcu.transitive_failed_analysis.deinit(gpa);2456 zcu.transitive_failed_analysis.deinit(gpa);
2451 zcu.failed_codegen.deinit(gpa);2457 zcu.failed_codegen.deinit(gpa);
2458 zcu.failed_types.deinit(gpa);
24522459
2453 for (zcu.failed_files.values()) |value| {2460 for (zcu.failed_files.values()) |value| {
2454 if (value) |msg| msg.destroy(gpa);2461 if (value) |msg| msg.destroy(gpa);
...@@ -3093,7 +3100,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {...@@ -3093,7 +3100,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
3093 const gpa = zcu.gpa;3100 const gpa = zcu.gpa;
30943101
3095 const exports_base, const exports_len = if (zcu.single_exports.fetchSwapRemove(anal_unit)) |kv|3102 const exports_base, const exports_len = if (zcu.single_exports.fetchSwapRemove(anal_unit)) |kv|
3096 .{ kv.value, 1 }3103 .{ @intFromEnum(kv.value), 1 }
3097 else if (zcu.multi_exports.fetchSwapRemove(anal_unit)) |info|3104 else if (zcu.multi_exports.fetchSwapRemove(anal_unit)) |info|
3098 .{ info.value.index, info.value.len }3105 .{ info.value.index, info.value.len }
3099 else3106 else
...@@ -3107,11 +3114,12 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {...@@ -3107,11 +3114,12 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
3107 // This case is needed because in some rare edge cases, `Sema` wants to add and delete exports3114 // This case is needed because in some rare edge cases, `Sema` wants to add and delete exports
3108 // within a single update.3115 // within a single update.
3109 if (dev.env.supports(.incremental)) {3116 if (dev.env.supports(.incremental)) {
3110 for (exports, exports_base..) |exp, export_idx| {3117 for (exports, exports_base..) |exp, export_index_usize| {
3118 const export_idx: Export.Index = @enumFromInt(export_index_usize);
3111 if (zcu.comp.bin_file) |lf| {3119 if (zcu.comp.bin_file) |lf| {
3112 lf.deleteExport(exp.exported, exp.opts.name);3120 lf.deleteExport(exp.exported, exp.opts.name);
3113 }3121 }
3114 if (zcu.failed_exports.fetchSwapRemove(@intCast(export_idx))) |failed_kv| {3122 if (zcu.failed_exports.fetchSwapRemove(export_idx)) |failed_kv| {
3115 failed_kv.value.destroy(gpa);3123 failed_kv.value.destroy(gpa);
3116 }3124 }
3117 }3125 }
...@@ -3123,7 +3131,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {...@@ -3123,7 +3131,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
3123 return;3131 return;
3124 };3132 };
3125 for (exports_base..exports_base + exports_len) |export_idx| {3133 for (exports_base..exports_base + exports_len) |export_idx| {
3126 zcu.free_exports.appendAssumeCapacity(@intCast(export_idx));3134 zcu.free_exports.appendAssumeCapacity(@enumFromInt(export_idx));
3127 }3135 }
3128}3136}
31293137
...@@ -3269,7 +3277,7 @@ fn lockAndClearFileCompileError(zcu: *Zcu, file: *File) void {...@@ -3269,7 +3277,7 @@ fn lockAndClearFileCompileError(zcu: *Zcu, file: *File) void {
32693277
3270pub fn handleUpdateExports(3278pub fn handleUpdateExports(
3271 zcu: *Zcu,3279 zcu: *Zcu,
3272 export_indices: []const u32,3280 export_indices: []const Export.Index,
3273 result: link.File.UpdateExportsError!void,3281 result: link.File.UpdateExportsError!void,
3274) Allocator.Error!void {3282) Allocator.Error!void {
3275 const gpa = zcu.gpa;3283 const gpa = zcu.gpa;
...@@ -3277,12 +3285,10 @@ pub fn handleUpdateExports(...@@ -3277,12 +3285,10 @@ pub fn handleUpdateExports(
3277 error.OutOfMemory => return error.OutOfMemory,3285 error.OutOfMemory => return error.OutOfMemory,
3278 error.AnalysisFail => {3286 error.AnalysisFail => {
3279 const export_idx = export_indices[0];3287 const export_idx = export_indices[0];
3280 const new_export = &zcu.all_exports.items[export_idx];3288 const new_export = export_idx.ptr(zcu);
3281 new_export.status = .failed_retryable;3289 new_export.status = .failed_retryable;
3282 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);3290 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
3283 const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{3291 const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{@errorName(err)});
3284 @errorName(err),
3285 });
3286 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg);3292 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg);
3287 },3293 },
3288 };3294 };
...@@ -3443,7 +3449,7 @@ pub fn atomicPtrAlignment(...@@ -3443,7 +3449,7 @@ pub fn atomicPtrAlignment(
3443/// * `@TypeOf(.{})`3449/// * `@TypeOf(.{})`
3444/// * A struct which has no fields (`struct {}`).3450/// * A struct which has no fields (`struct {}`).
3445/// * Not a struct.3451/// * Not a struct.
3446pub fn typeToStruct(zcu: *Zcu, ty: Type) ?InternPool.LoadedStructType {3452pub fn typeToStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType {
3447 if (ty.ip_index == .none) return null;3453 if (ty.ip_index == .none) return null;
3448 const ip = &zcu.intern_pool;3454 const ip = &zcu.intern_pool;
3449 return switch (ip.indexToKey(ty.ip_index)) {3455 return switch (ip.indexToKey(ty.ip_index)) {
...@@ -3452,7 +3458,7 @@ pub fn typeToStruct(zcu: *Zcu, ty: Type) ?InternPool.LoadedStructType {...@@ -3452,7 +3458,7 @@ pub fn typeToStruct(zcu: *Zcu, ty: Type) ?InternPool.LoadedStructType {
3452 };3458 };
3453}3459}
34543460
3455pub fn typeToPackedStruct(zcu: *Zcu, ty: Type) ?InternPool.LoadedStructType {3461pub fn typeToPackedStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType {
3456 const s = zcu.typeToStruct(ty) orelse return null;3462 const s = zcu.typeToStruct(ty) orelse return null;
3457 if (s.layout != .@"packed") return null;3463 if (s.layout != .@"packed") return null;
3458 return s;3464 return s;
...@@ -3477,7 +3483,7 @@ pub fn iesFuncIndex(zcu: *const Zcu, ies_index: InternPool.Index) InternPool.Ind...@@ -3477,7 +3483,7 @@ pub fn iesFuncIndex(zcu: *const Zcu, ies_index: InternPool.Index) InternPool.Ind
3477}3483}
34783484
3479pub fn funcInfo(zcu: *const Zcu, func_index: InternPool.Index) InternPool.Key.Func {3485pub fn funcInfo(zcu: *const Zcu, func_index: InternPool.Index) InternPool.Key.Func {
3480 return zcu.intern_pool.indexToKey(func_index).func;3486 return zcu.intern_pool.toFunc(func_index);
3481}3487}
34823488
3483pub fn toEnum(zcu: *const Zcu, comptime E: type, val: Value) E {3489pub fn toEnum(zcu: *const Zcu, comptime E: type, val: Value) E {
...@@ -3791,6 +3797,18 @@ pub fn navSrcLoc(zcu: *const Zcu, nav_index: InternPool.Nav.Index) LazySrcLoc {...@@ -3791,6 +3797,18 @@ pub fn navSrcLoc(zcu: *const Zcu, nav_index: InternPool.Nav.Index) LazySrcLoc {
3791 };3797 };
3792}3798}
37933799
3800pub fn typeSrcLoc(zcu: *const Zcu, ty_index: InternPool.Index) LazySrcLoc {
3801 _ = zcu;
3802 _ = ty_index;
3803 @panic("TODO");
3804}
3805
3806pub fn typeFileScope(zcu: *Zcu, ty_index: InternPool.Index) *File {
3807 _ = zcu;
3808 _ = ty_index;
3809 @panic("TODO");
3810}
3811
3794pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 {3812pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 {
3795 const ip = &zcu.intern_pool;3813 const ip = &zcu.intern_pool;
3796 const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?;3814 const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?;
...@@ -4051,3 +4069,54 @@ pub fn navValIsConst(zcu: *const Zcu, val: InternPool.Index) bool {...@@ -4051,3 +4069,54 @@ pub fn navValIsConst(zcu: *const Zcu, val: InternPool.Index) bool {
4051 else => true,4069 else => true,
4052 };4070 };
4053}4071}
4072
4073pub const CodegenFailError = error{
4074 /// Indicates the error message has been already stored at `Zcu.failed_codegen`.
4075 CodegenFail,
4076 OutOfMemory,
4077};
4078
4079pub fn codegenFail(
4080 zcu: *Zcu,
4081 nav_index: InternPool.Nav.Index,
4082 comptime format: []const u8,
4083 args: anytype,
4084) CodegenFailError {
4085 const gpa = zcu.gpa;
4086 try zcu.failed_codegen.ensureUnusedCapacity(gpa, 1);
4087 const msg = try Zcu.ErrorMsg.create(gpa, zcu.navSrcLoc(nav_index), format, args);
4088 zcu.failed_codegen.putAssumeCapacityNoClobber(nav_index, msg);
4089 return error.CodegenFail;
4090}
4091
4092pub fn codegenFailMsg(zcu: *Zcu, nav_index: InternPool.Nav.Index, msg: *ErrorMsg) CodegenFailError {
4093 const gpa = zcu.gpa;
4094 {
4095 errdefer msg.deinit(gpa);
4096 try zcu.failed_codegen.putNoClobber(gpa, nav_index, msg);
4097 }
4098 return error.CodegenFail;
4099}
4100
4101pub fn codegenFailType(
4102 zcu: *Zcu,
4103 ty_index: InternPool.Index,
4104 comptime format: []const u8,
4105 args: anytype,
4106) CodegenFailError {
4107 const gpa = zcu.gpa;
4108 try zcu.failed_types.ensureUnusedCapacity(gpa, 1);
4109 const msg = try Zcu.ErrorMsg.create(gpa, zcu.typeSrcLoc(ty_index), format, args);
4110 zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg);
4111 return error.CodegenFail;
4112}
4113
4114pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg) CodegenFailError {
4115 const gpa = zcu.gpa;
4116 {
4117 errdefer msg.deinit(gpa);
4118 try zcu.failed_types.ensureUnusedCapacity(gpa, 1);
4119 }
4120 zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg);
4121 return error.CodegenFail;
4122}
src/Zcu/PerThread.zig+26-38
...@@ -1722,22 +1722,18 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai...@@ -1722,22 +1722,18 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai
1722 // Correcting this failure will involve changing a type this function1722 // Correcting this failure will involve changing a type this function
1723 // depends on, hence triggering re-analysis of this function, so this1723 // depends on, hence triggering re-analysis of this function, so this
1724 // interacts correctly with incremental compilation.1724 // interacts correctly with incremental compilation.
1725 // TODO: do we need to mark this failure anywhere? I don't think so, since compilation
1726 // will fail due to the type error anyway.
1727 } else if (comp.bin_file) |lf| {1725 } else if (comp.bin_file) |lf| {
1728 lf.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) {1726 lf.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) {
1729 error.OutOfMemory => return error.OutOfMemory,1727 error.OutOfMemory => return error.OutOfMemory,
1730 error.AnalysisFail => {1728 error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)),
1731 assert(zcu.failed_codegen.contains(nav_index));1729 error.Overflow => {
1732 },
1733 else => {
1734 try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(1730 try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
1735 gpa,1731 gpa,
1736 zcu.navSrcLoc(nav_index),1732 zcu.navSrcLoc(nav_index),
1737 "unable to codegen: {s}",1733 "unable to codegen: {s}",
1738 .{@errorName(err)},1734 .{@errorName(err)},
1739 ));1735 ));
1740 try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index }));1736 // Not a retryable failure.
1741 },1737 },
1742 };1738 };
1743 } else if (zcu.llvm_object) |llvm_object| {1739 } else if (zcu.llvm_object) |llvm_object| {
...@@ -2819,8 +2815,8 @@ pub fn processExports(pt: Zcu.PerThread) !void {...@@ -2819,8 +2815,8 @@ pub fn processExports(pt: Zcu.PerThread) !void {
2819 const gpa = zcu.gpa;2815 const gpa = zcu.gpa;
28202816
2821 // First, construct a mapping of every exported value and Nav to the indices of all its different exports.2817 // First, construct a mapping of every exported value and Nav to the indices of all its different exports.
2822 var nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, std.ArrayListUnmanaged(u32)) = .empty;2818 var nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, std.ArrayListUnmanaged(Zcu.Export.Index)) = .empty;
2823 var uav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, std.ArrayListUnmanaged(u32)) = .empty;2819 var uav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, std.ArrayListUnmanaged(Zcu.Export.Index)) = .empty;
2824 defer {2820 defer {
2825 for (nav_exports.values()) |*exports| {2821 for (nav_exports.values()) |*exports| {
2826 exports.deinit(gpa);2822 exports.deinit(gpa);
...@@ -2839,7 +2835,7 @@ pub fn processExports(pt: Zcu.PerThread) !void {...@@ -2839,7 +2835,7 @@ pub fn processExports(pt: Zcu.PerThread) !void {
2839 try nav_exports.ensureTotalCapacity(gpa, zcu.single_exports.count() + zcu.multi_exports.count());2835 try nav_exports.ensureTotalCapacity(gpa, zcu.single_exports.count() + zcu.multi_exports.count());
28402836
2841 for (zcu.single_exports.values()) |export_idx| {2837 for (zcu.single_exports.values()) |export_idx| {
2842 const exp = zcu.all_exports.items[export_idx];2838 const exp = export_idx.ptr(zcu);
2843 const value_ptr, const found_existing = switch (exp.exported) {2839 const value_ptr, const found_existing = switch (exp.exported) {
2844 .nav => |nav| gop: {2840 .nav => |nav| gop: {
2845 const gop = try nav_exports.getOrPut(gpa, nav);2841 const gop = try nav_exports.getOrPut(gpa, nav);
...@@ -2867,7 +2863,7 @@ pub fn processExports(pt: Zcu.PerThread) !void {...@@ -2867,7 +2863,7 @@ pub fn processExports(pt: Zcu.PerThread) !void {
2867 },2863 },
2868 };2864 };
2869 if (!found_existing) value_ptr.* = .{};2865 if (!found_existing) value_ptr.* = .{};
2870 try value_ptr.append(gpa, @intCast(export_idx));2866 try value_ptr.append(gpa, @enumFromInt(export_idx));
2871 }2867 }
2872 }2868 }
28732869
...@@ -2886,20 +2882,20 @@ pub fn processExports(pt: Zcu.PerThread) !void {...@@ -2886,20 +2882,20 @@ pub fn processExports(pt: Zcu.PerThread) !void {
2886 }2882 }
2887}2883}
28882884
2889const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, u32);2885const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, Zcu.Export.Index);
28902886
2891fn processExportsInner(2887fn processExportsInner(
2892 pt: Zcu.PerThread,2888 pt: Zcu.PerThread,
2893 symbol_exports: *SymbolExports,2889 symbol_exports: *SymbolExports,
2894 exported: Zcu.Exported,2890 exported: Zcu.Exported,
2895 export_indices: []const u32,2891 export_indices: []const Zcu.Export.Index,
2896) error{OutOfMemory}!void {2892) error{OutOfMemory}!void {
2897 const zcu = pt.zcu;2893 const zcu = pt.zcu;
2898 const gpa = zcu.gpa;2894 const gpa = zcu.gpa;
2899 const ip = &zcu.intern_pool;2895 const ip = &zcu.intern_pool;
29002896
2901 for (export_indices) |export_idx| {2897 for (export_indices) |export_idx| {
2902 const new_export = &zcu.all_exports.items[export_idx];2898 const new_export = export_idx.ptr(zcu);
2903 const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name);2899 const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name);
2904 if (gop.found_existing) {2900 if (gop.found_existing) {
2905 new_export.status = .failed_retryable;2901 new_export.status = .failed_retryable;
...@@ -2908,7 +2904,7 @@ fn processExportsInner(...@@ -2908,7 +2904,7 @@ fn processExportsInner(
2908 new_export.opts.name.fmt(ip),2904 new_export.opts.name.fmt(ip),
2909 });2905 });
2910 errdefer msg.destroy(gpa);2906 errdefer msg.destroy(gpa);
2911 const other_export = zcu.all_exports.items[gop.value_ptr.*];2907 const other_export = gop.value_ptr.ptr(zcu);
2912 try zcu.errNote(other_export.src, msg, "other symbol here", .{});2908 try zcu.errNote(other_export.src, msg, "other symbol here", .{});
2913 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg);2909 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg);
2914 new_export.status = .failed;2910 new_export.status = .failed;
...@@ -3100,6 +3096,7 @@ pub fn populateTestFunctions(...@@ -3100,6 +3096,7 @@ pub fn populateTestFunctions(
3100pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{OutOfMemory}!void {3096pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{OutOfMemory}!void {
3101 const zcu = pt.zcu;3097 const zcu = pt.zcu;
3102 const comp = zcu.comp;3098 const comp = zcu.comp;
3099 const gpa = zcu.gpa;
3103 const ip = &zcu.intern_pool;3100 const ip = &zcu.intern_pool;
31043101
3105 const nav = zcu.intern_pool.getNav(nav_index);3102 const nav = zcu.intern_pool.getNav(nav_index);
...@@ -3113,26 +3110,15 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error...@@ -3113,26 +3110,15 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error
3113 } else if (comp.bin_file) |lf| {3110 } else if (comp.bin_file) |lf| {
3114 lf.updateNav(pt, nav_index) catch |err| switch (err) {3111 lf.updateNav(pt, nav_index) catch |err| switch (err) {
3115 error.OutOfMemory => return error.OutOfMemory,3112 error.OutOfMemory => return error.OutOfMemory,
3116 error.AnalysisFail => {3113 error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)),
3117 assert(zcu.failed_codegen.contains(nav_index));3114 error.Overflow => {
3118 },3115 try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
3119 else => {
3120 const gpa = zcu.gpa;
3121 try zcu.failed_codegen.ensureUnusedCapacity(gpa, 1);
3122 zcu.failed_codegen.putAssumeCapacityNoClobber(nav_index, try Zcu.ErrorMsg.create(
3123 gpa,3116 gpa,
3124 zcu.navSrcLoc(nav_index),3117 zcu.navSrcLoc(nav_index),
3125 "unable to codegen: {s}",3118 "unable to codegen: {s}",
3126 .{@errorName(err)},3119 .{@errorName(err)},
3127 ));3120 ));
3128 if (nav.analysis != null) {3121 // Not a retryable failure.
3129 try zcu.retryable_failures.append(zcu.gpa, .wrap(.{ .nav_val = nav_index }));
3130 } else {
3131 // TODO: we don't have a way to indicate that this failure is retryable!
3132 // Since these are really rare, we could as a cop-out retry the whole build next update.
3133 // But perhaps we can do better...
3134 @panic("TODO: retryable failure codegenning non-declaration Nav");
3135 }
3136 },3122 },
3137 };3123 };
3138 } else if (zcu.llvm_object) |llvm_object| {3124 } else if (zcu.llvm_object) |llvm_object| {
...@@ -3142,24 +3128,26 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error...@@ -3142,24 +3128,26 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error
3142 }3128 }
3143}3129}
31443130
3145pub fn linkerUpdateContainerType(pt: Zcu.PerThread, ty: InternPool.Index) !void {3131pub fn linkerUpdateContainerType(pt: Zcu.PerThread, ty: InternPool.Index) error{OutOfMemory}!void {
3146 const zcu = pt.zcu;3132 const zcu = pt.zcu;
3133 const gpa = zcu.gpa;
3147 const comp = zcu.comp;3134 const comp = zcu.comp;
3148 const ip = &zcu.intern_pool;3135 const ip = &zcu.intern_pool;
31493136
3150 const codegen_prog_node = zcu.codegen_prog_node.start(Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), 0);3137 const codegen_prog_node = zcu.codegen_prog_node.start(Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), 0);
3151 defer codegen_prog_node.end();3138 defer codegen_prog_node.end();
31523139
3140 if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(gpa);
3141
3153 if (!Air.typeFullyResolved(Type.fromInterned(ty), zcu)) {3142 if (!Air.typeFullyResolved(Type.fromInterned(ty), zcu)) {
3154 // This type failed to resolve. This is a transitive failure.3143 // This type failed to resolve. This is a transitive failure.
3155 // TODO: do we need to mark this failure anywhere? I don't think so, since compilation3144 return;
3156 // will fail due to the type error anyway.
3157 } else if (comp.bin_file) |lf| {
3158 lf.updateContainerType(pt, ty) catch |err| switch (err) {
3159 error.OutOfMemory => return error.OutOfMemory,
3160 else => |e| log.err("codegen type failed: {s}", .{@errorName(e)}),
3161 };
3162 }3145 }
3146
3147 if (comp.bin_file) |lf| lf.updateContainerType(pt, ty) catch |err| switch (err) {
3148 error.OutOfMemory => return error.OutOfMemory,
3149 error.TypeFailureReported => assert(zcu.failed_types.contains(ty)),
3150 };
3163}3151}
31643152
3165pub fn linkerUpdateLineNumber(pt: Zcu.PerThread, ti: InternPool.TrackedInst.Index) !void {3153pub fn linkerUpdateLineNumber(pt: Zcu.PerThread, ti: InternPool.TrackedInst.Index) !void {
src/arch/aarch64/CodeGen.zig+139-159
...@@ -24,7 +24,6 @@ const build_options = @import("build_options");...@@ -24,7 +24,6 @@ const build_options = @import("build_options");
24const Alignment = InternPool.Alignment;24const Alignment = InternPool.Alignment;
2525
26const CodeGenError = codegen.CodeGenError;26const CodeGenError = codegen.CodeGenError;
27const Result = codegen.Result;
2827
29const bits = @import("bits.zig");28const bits = @import("bits.zig");
30const abi = @import("abi.zig");29const abi = @import("abi.zig");
...@@ -51,7 +50,6 @@ debug_output: link.File.DebugInfoOutput,...@@ -51,7 +50,6 @@ debug_output: link.File.DebugInfoOutput,
51target: *const std.Target,50target: *const std.Target,
52func_index: InternPool.Index,51func_index: InternPool.Index,
53owner_nav: InternPool.Nav.Index,52owner_nav: InternPool.Nav.Index,
54err_msg: ?*ErrorMsg,
55args: []MCValue,53args: []MCValue,
56ret_mcv: MCValue,54ret_mcv: MCValue,
57fn_type: Type,55fn_type: Type,
...@@ -325,9 +323,9 @@ pub fn generate(...@@ -325,9 +323,9 @@ pub fn generate(
325 func_index: InternPool.Index,323 func_index: InternPool.Index,
326 air: Air,324 air: Air,
327 liveness: Liveness,325 liveness: Liveness,
328 code: *std.ArrayList(u8),326 code: *std.ArrayListUnmanaged(u8),
329 debug_output: link.File.DebugInfoOutput,327 debug_output: link.File.DebugInfoOutput,
330) CodeGenError!Result {328) CodeGenError!void {
331 const zcu = pt.zcu;329 const zcu = pt.zcu;
332 const gpa = zcu.gpa;330 const gpa = zcu.gpa;
333 const func = zcu.funcInfo(func_index);331 const func = zcu.funcInfo(func_index);
...@@ -353,7 +351,6 @@ pub fn generate(...@@ -353,7 +351,6 @@ pub fn generate(
353 .bin_file = lf,351 .bin_file = lf,
354 .func_index = func_index,352 .func_index = func_index,
355 .owner_nav = func.owner_nav,353 .owner_nav = func.owner_nav,
356 .err_msg = null,
357 .args = undefined, // populated after `resolveCallingConventionValues`354 .args = undefined, // populated after `resolveCallingConventionValues`
358 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`355 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
359 .fn_type = fn_type,356 .fn_type = fn_type,
...@@ -370,10 +367,7 @@ pub fn generate(...@@ -370,10 +367,7 @@ pub fn generate(
370 defer function.dbg_info_relocs.deinit(gpa);367 defer function.dbg_info_relocs.deinit(gpa);
371368
372 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {369 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
373 error.CodegenFail => return Result{ .fail = function.err_msg.? },370 error.CodegenFail => return error.CodegenFail,
374 error.OutOfRegisters => return Result{
375 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
376 },
377 else => |e| return e,371 else => |e| return e,
378 };372 };
379 defer call_info.deinit(&function);373 defer call_info.deinit(&function);
...@@ -384,24 +378,23 @@ pub fn generate(...@@ -384,24 +378,23 @@ pub fn generate(
384 function.max_end_stack = call_info.stack_byte_count;378 function.max_end_stack = call_info.stack_byte_count;
385379
386 function.gen() catch |err| switch (err) {380 function.gen() catch |err| switch (err) {
387 error.CodegenFail => return Result{ .fail = function.err_msg.? },381 error.CodegenFail => return error.CodegenFail,
388 error.OutOfRegisters => return Result{382 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
389 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
390 },
391 else => |e| return e,383 else => |e| return e,
392 };384 };
393385
394 for (function.dbg_info_relocs.items) |reloc| {386 for (function.dbg_info_relocs.items) |reloc| {
395 try reloc.genDbgInfo(function);387 reloc.genDbgInfo(function) catch |err|
388 return function.fail("failed to generate debug info: {s}", .{@errorName(err)});
396 }389 }
397390
398 var mir = Mir{391 var mir: Mir = .{
399 .instructions = function.mir_instructions.toOwnedSlice(),392 .instructions = function.mir_instructions.toOwnedSlice(),
400 .extra = try function.mir_extra.toOwnedSlice(gpa),393 .extra = try function.mir_extra.toOwnedSlice(gpa),
401 };394 };
402 defer mir.deinit(gpa);395 defer mir.deinit(gpa);
403396
404 var emit = Emit{397 var emit: Emit = .{
405 .mir = mir,398 .mir = mir,
406 .bin_file = lf,399 .bin_file = lf,
407 .debug_output = debug_output,400 .debug_output = debug_output,
...@@ -417,15 +410,9 @@ pub fn generate(...@@ -417,15 +410,9 @@ pub fn generate(
417 defer emit.deinit();410 defer emit.deinit();
418411
419 emit.emitMir() catch |err| switch (err) {412 emit.emitMir() catch |err| switch (err) {
420 error.EmitFail => return Result{ .fail = emit.err_msg.? },413 error.EmitFail => return function.failMsg(emit.err_msg.?),
421 else => |e| return e,414 else => |e| return e,
422 };415 };
423
424 if (function.err_msg) |em| {
425 return Result{ .fail = em };
426 } else {
427 return Result.ok;
428 }
429}416}
430417
431fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {418fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
...@@ -567,7 +554,7 @@ fn gen(self: *Self) !void {...@@ -567,7 +554,7 @@ fn gen(self: *Self) !void {
567 .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } },554 .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } },
568 });555 });
569 } else {556 } else {
570 return self.failSymbol("TODO AArch64: allow larger stacks", .{});557 @panic("TODO AArch64: allow larger stacks");
571 }558 }
572559
573 _ = try self.addInst(.{560 _ = try self.addInst(.{
...@@ -723,7 +710,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -723,7 +710,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
723 .cmp_gt => try self.airCmp(inst, .gt),710 .cmp_gt => try self.airCmp(inst, .gt),
724 .cmp_neq => try self.airCmp(inst, .neq),711 .cmp_neq => try self.airCmp(inst, .neq),
725712
726 .cmp_vector => try self.airCmpVector(inst),713 .cmp_vector => try self.airCmpVector(inst),
727 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),714 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
728715
729 .alloc => try self.airAlloc(inst),716 .alloc => try self.airAlloc(inst),
...@@ -744,7 +731,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -744,7 +731,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
744 .fpext => try self.airFpext(inst),731 .fpext => try self.airFpext(inst),
745 .intcast => try self.airIntCast(inst),732 .intcast => try self.airIntCast(inst),
746 .trunc => try self.airTrunc(inst),733 .trunc => try self.airTrunc(inst),
747 .int_from_bool => try self.airIntFromBool(inst),734 .int_from_bool => try self.airIntFromBool(inst),
748 .is_non_null => try self.airIsNonNull(inst),735 .is_non_null => try self.airIsNonNull(inst),
749 .is_non_null_ptr => try self.airIsNonNullPtr(inst),736 .is_non_null_ptr => try self.airIsNonNullPtr(inst),
750 .is_null => try self.airIsNull(inst),737 .is_null => try self.airIsNull(inst),
...@@ -756,7 +743,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -756,7 +743,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
756 .load => try self.airLoad(inst),743 .load => try self.airLoad(inst),
757 .loop => try self.airLoop(inst),744 .loop => try self.airLoop(inst),
758 .not => try self.airNot(inst),745 .not => try self.airNot(inst),
759 .int_from_ptr => try self.airIntFromPtr(inst),746 .int_from_ptr => try self.airIntFromPtr(inst),
760 .ret => try self.airRet(inst),747 .ret => try self.airRet(inst),
761 .ret_safe => try self.airRet(inst), // TODO748 .ret_safe => try self.airRet(inst), // TODO
762 .ret_load => try self.airRetLoad(inst),749 .ret_load => try self.airRetLoad(inst),
...@@ -765,8 +752,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -765,8 +752,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
765 .struct_field_ptr=> try self.airStructFieldPtr(inst),752 .struct_field_ptr=> try self.airStructFieldPtr(inst),
766 .struct_field_val=> try self.airStructFieldVal(inst),753 .struct_field_val=> try self.airStructFieldVal(inst),
767 .array_to_slice => try self.airArrayToSlice(inst),754 .array_to_slice => try self.airArrayToSlice(inst),
768 .float_from_int => try self.airFloatFromInt(inst),755 .float_from_int => try self.airFloatFromInt(inst),
769 .int_from_float => try self.airIntFromFloat(inst),756 .int_from_float => try self.airIntFromFloat(inst),
770 .cmpxchg_strong => try self.airCmpxchg(inst),757 .cmpxchg_strong => try self.airCmpxchg(inst),
771 .cmpxchg_weak => try self.airCmpxchg(inst),758 .cmpxchg_weak => try self.airCmpxchg(inst),
772 .atomic_rmw => try self.airAtomicRmw(inst),759 .atomic_rmw => try self.airAtomicRmw(inst),
...@@ -1107,7 +1094,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {...@@ -1107,7 +1094,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
1107/// Copies a value to a register without tracking the register. The register is not considered1094/// Copies a value to a register without tracking the register. The register is not considered
1108/// allocated. A second call to `copyToTmpRegister` may return the same register.1095/// allocated. A second call to `copyToTmpRegister` may return the same register.
1109/// This can have a side effect of spilling instructions to the stack to free up a register.1096/// This can have a side effect of spilling instructions to the stack to free up a register.
1110fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {1097fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) InnerError!Register {
1111 const raw_reg = try self.register_manager.allocReg(null, gp);1098 const raw_reg = try self.register_manager.allocReg(null, gp);
1112 const reg = self.registerAlias(raw_reg, ty);1099 const reg = self.registerAlias(raw_reg, ty);
1113 try self.genSetReg(ty, reg, mcv);1100 try self.genSetReg(ty, reg, mcv);
...@@ -1125,12 +1112,12 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa...@@ -1125,12 +1112,12 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa
1125 return MCValue{ .register = reg };1112 return MCValue{ .register = reg };
1126}1113}
11271114
1128fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {1115fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!void {
1129 const stack_offset = try self.allocMemPtr(inst);1116 const stack_offset = try self.allocMemPtr(inst);
1130 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });1117 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
1131}1118}
11321119
1133fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {1120fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
1134 const pt = self.pt;1121 const pt = self.pt;
1135 const zcu = pt.zcu;1122 const zcu = pt.zcu;
1136 const result: MCValue = switch (self.ret_mcv) {1123 const result: MCValue = switch (self.ret_mcv) {
...@@ -1152,19 +1139,19 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1152,19 +1139,19 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
1152 return self.finishAir(inst, result, .{ .none, .none, .none });1139 return self.finishAir(inst, result, .{ .none, .none, .none });
1153}1140}
11541141
1155fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {1142fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!void {
1156 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1143 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1157 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});1144 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
1158 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1145 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1159}1146}
11601147
1161fn airFpext(self: *Self, inst: Air.Inst.Index) !void {1148fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!void {
1162 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1149 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1163 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});1150 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
1164 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1151 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1165}1152}
11661153
1167fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {1154fn airIntCast(self: *Self, inst: Air.Inst.Index) InnerError!void {
1168 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1155 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1169 if (self.liveness.isUnused(inst))1156 if (self.liveness.isUnused(inst))
1170 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });1157 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
...@@ -1293,7 +1280,7 @@ fn trunc(...@@ -1293,7 +1280,7 @@ fn trunc(
1293 }1280 }
1294}1281}
12951282
1296fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {1283fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!void {
1297 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1284 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1298 const operand = try self.resolveInst(ty_op.operand);1285 const operand = try self.resolveInst(ty_op.operand);
1299 const operand_ty = self.typeOf(ty_op.operand);1286 const operand_ty = self.typeOf(ty_op.operand);
...@@ -1306,14 +1293,14 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {...@@ -1306,14 +1293,14 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
1306 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1293 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1307}1294}
13081295
1309fn airIntFromBool(self: *Self, inst: Air.Inst.Index) !void {1296fn airIntFromBool(self: *Self, inst: Air.Inst.Index) InnerError!void {
1310 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;1297 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
1311 const operand = try self.resolveInst(un_op);1298 const operand = try self.resolveInst(un_op);
1312 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;1299 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
1313 return self.finishAir(inst, result, .{ un_op, .none, .none });1300 return self.finishAir(inst, result, .{ un_op, .none, .none });
1314}1301}
13151302
1316fn airNot(self: *Self, inst: Air.Inst.Index) !void {1303fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!void {
1317 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1304 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1318 const pt = self.pt;1305 const pt = self.pt;
1319 const zcu = pt.zcu;1306 const zcu = pt.zcu;
...@@ -1484,7 +1471,7 @@ fn minMax(...@@ -1484,7 +1471,7 @@ fn minMax(
1484 }1471 }
1485}1472}
14861473
1487fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {1474fn airMinMax(self: *Self, inst: Air.Inst.Index) InnerError!void {
1488 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];1475 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
1489 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;1476 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1490 const lhs_ty = self.typeOf(bin_op.lhs);1477 const lhs_ty = self.typeOf(bin_op.lhs);
...@@ -1502,7 +1489,7 @@ fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {...@@ -1502,7 +1489,7 @@ fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {
1502 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1489 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1503}1490}
15041491
1505fn airSlice(self: *Self, inst: Air.Inst.Index) !void {1492fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!void {
1506 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;1493 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
1507 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1494 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1508 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1495 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
...@@ -2440,7 +2427,7 @@ fn ptrArithmetic(...@@ -2440,7 +2427,7 @@ fn ptrArithmetic(
2440 }2427 }
2441}2428}
24422429
2443fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {2430fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) InnerError!void {
2444 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2431 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2445 const lhs_ty = self.typeOf(bin_op.lhs);2432 const lhs_ty = self.typeOf(bin_op.lhs);
2446 const rhs_ty = self.typeOf(bin_op.rhs);2433 const rhs_ty = self.typeOf(bin_op.rhs);
...@@ -2490,7 +2477,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {...@@ -2490,7 +2477,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
2490 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2477 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2491}2478}
24922479
2493fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {2480fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) InnerError!void {
2494 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;2481 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2495 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;2482 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
2496 const lhs_ty = self.typeOf(bin_op.lhs);2483 const lhs_ty = self.typeOf(bin_op.lhs);
...@@ -2505,25 +2492,25 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void...@@ -2505,25 +2492,25 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void
2505 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2492 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2506}2493}
25072494
2508fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {2495fn airAddSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
2509 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2496 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2510 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});2497 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
2511 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2498 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2512}2499}
25132500
2514fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {2501fn airSubSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
2515 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2502 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2516 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});2503 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
2517 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2504 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2518}2505}
25192506
2520fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {2507fn airMulSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
2521 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2508 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2522 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});2509 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
2523 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2510 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2524}2511}
25252512
2526fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {2513fn airOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
2527 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];2514 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
2528 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;2515 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2529 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2516 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
...@@ -2536,9 +2523,9 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2536,9 +2523,9 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2536 const rhs_ty = self.typeOf(extra.rhs);2523 const rhs_ty = self.typeOf(extra.rhs);
25372524
2538 const tuple_ty = self.typeOfIndex(inst);2525 const tuple_ty = self.typeOfIndex(inst);
2539 const tuple_size = @as(u32, @intCast(tuple_ty.abiSize(zcu)));2526 const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu));
2540 const tuple_align = tuple_ty.abiAlignment(zcu);2527 const tuple_align = tuple_ty.abiAlignment(zcu);
2541 const overflow_bit_offset = @as(u32, @intCast(tuple_ty.structFieldOffset(1, zcu)));2528 const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu));
25422529
2543 switch (lhs_ty.zigTypeTag(zcu)) {2530 switch (lhs_ty.zigTypeTag(zcu)) {
2544 .vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}),2531 .vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}),
...@@ -2652,7 +2639,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2652,7 +2639,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2652 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });2639 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2653}2640}
26542641
2655fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {2642fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
2656 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;2643 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2657 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2644 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2658 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });2645 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
...@@ -2876,7 +2863,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2876,7 +2863,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2876 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });2863 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2877}2864}
28782865
2879fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {2866fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
2880 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;2867 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2881 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2868 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2882 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });2869 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
...@@ -3012,13 +2999,13 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -3012,13 +2999,13 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
3012 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });2999 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
3013}3000}
30143001
3015fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {3002fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
3016 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3003 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3017 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});3004 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
3018 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });3005 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3019}3006}
30203007
3021fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {3008fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3022 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3009 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3023 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3010 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3024 const optional_ty = self.typeOf(ty_op.operand);3011 const optional_ty = self.typeOf(ty_op.operand);
...@@ -3055,13 +3042,13 @@ fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty:...@@ -3055,13 +3042,13 @@ fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty:
3055 }3042 }
3056}3043}
30573044
3058fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {3045fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3059 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3046 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3060 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});3047 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
3061 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3048 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3062}3049}
30633050
3064fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {3051fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void {
3065 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3052 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3066 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});3053 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
3067 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3054 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
...@@ -3137,7 +3124,7 @@ fn errUnionErr(...@@ -3137,7 +3124,7 @@ fn errUnionErr(
3137 }3124 }
3138}3125}
31393126
3140fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {3127fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3141 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3128 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3142 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3129 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3143 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };3130 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
...@@ -3218,7 +3205,7 @@ fn errUnionPayload(...@@ -3218,7 +3205,7 @@ fn errUnionPayload(
3218 }3205 }
3219}3206}
32203207
3221fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {3208fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3222 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3209 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3223 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3210 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3224 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };3211 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
...@@ -3230,26 +3217,26 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -3230,26 +3217,26 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
3230}3217}
32313218
3232// *(E!T) -> E3219// *(E!T) -> E
3233fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {3220fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3234 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3221 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3235 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch});3222 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch});
3236 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3223 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3237}3224}
32383225
3239// *(E!T) -> *T3226// *(E!T) -> *T
3240fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {3227fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3241 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3228 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3242 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch});3229 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch});
3243 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3230 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3244}3231}
32453232
3246fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {3233fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void {
3247 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3234 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3248 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});3235 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
3249 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3236 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3250}3237}
32513238
3252fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {3239fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) InnerError!void {
3253 const result: MCValue = if (self.liveness.isUnused(inst))3240 const result: MCValue = if (self.liveness.isUnused(inst))
3254 .dead3241 .dead
3255 else3242 else
...@@ -3257,17 +3244,17 @@ fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {...@@ -3257,17 +3244,17 @@ fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
3257 return self.finishAir(inst, result, .{ .none, .none, .none });3244 return self.finishAir(inst, result, .{ .none, .none, .none });
3258}3245}
32593246
3260fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {3247fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) InnerError!void {
3261 _ = inst;3248 _ = inst;
3262 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});3249 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
3263}3250}
32643251
3265fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {3252fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) InnerError!void {
3266 _ = inst;3253 _ = inst;
3267 return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});3254 return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
3268}3255}
32693256
3270fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {3257fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!void {
3271 const pt = self.pt;3258 const pt = self.pt;
3272 const zcu = pt.zcu;3259 const zcu = pt.zcu;
3273 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3260 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
...@@ -3313,7 +3300,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {...@@ -3313,7 +3300,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
3313}3300}
33143301
3315/// T to E!T3302/// T to E!T
3316fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {3303fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3317 const pt = self.pt;3304 const pt = self.pt;
3318 const zcu = pt.zcu;3305 const zcu = pt.zcu;
3319 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3306 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
...@@ -3338,7 +3325,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -3338,7 +3325,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
3338}3325}
33393326
3340/// E to E!T3327/// E to E!T
3341fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {3328fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3342 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3329 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3343 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3330 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3344 const pt = self.pt;3331 const pt = self.pt;
...@@ -3379,7 +3366,7 @@ fn slicePtr(mcv: MCValue) MCValue {...@@ -3379,7 +3366,7 @@ fn slicePtr(mcv: MCValue) MCValue {
3379 }3366 }
3380}3367}
33813368
3382fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {3369fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3383 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3370 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3384 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3371 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3385 const mcv = try self.resolveInst(ty_op.operand);3372 const mcv = try self.resolveInst(ty_op.operand);
...@@ -3388,7 +3375,7 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3388,7 +3375,7 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
3388 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3375 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3389}3376}
33903377
3391fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {3378fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!void {
3392 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3379 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3393 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3380 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3394 const ptr_bits = 64;3381 const ptr_bits = 64;
...@@ -3412,7 +3399,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {...@@ -3412,7 +3399,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
3412 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3399 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3413}3400}
34143401
3415fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {3402fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3416 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3403 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3417 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3404 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3418 const ptr_bits = 64;3405 const ptr_bits = 64;
...@@ -3429,7 +3416,7 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3429,7 +3416,7 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
3429 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3416 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3430}3417}
34313418
3432fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {3419fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3433 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3420 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3434 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3421 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3435 const mcv = try self.resolveInst(ty_op.operand);3422 const mcv = try self.resolveInst(ty_op.operand);
...@@ -3444,7 +3431,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3444,7 +3431,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
3444 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3431 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3445}3432}
34463433
3447fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {3434fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3448 const pt = self.pt;3435 const pt = self.pt;
3449 const zcu = pt.zcu;3436 const zcu = pt.zcu;
3450 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3437 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
...@@ -3487,7 +3474,7 @@ fn ptrElemVal(...@@ -3487,7 +3474,7 @@ fn ptrElemVal(
3487 }3474 }
3488}3475}
34893476
3490fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {3477fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3491 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3478 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3492 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;3479 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
3493 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3480 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
...@@ -3506,13 +3493,13 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3506,13 +3493,13 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
3506 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });3493 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
3507}3494}
35083495
3509fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {3496fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3510 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3497 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3511 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});3498 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});
3512 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });3499 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3513}3500}
35143501
3515fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {3502fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3516 const pt = self.pt;3503 const pt = self.pt;
3517 const zcu = pt.zcu;3504 const zcu = pt.zcu;
3518 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3505 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
...@@ -3526,7 +3513,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -3526,7 +3513,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
3526 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });3513 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3527}3514}
35283515
3529fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {3516fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3530 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3517 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3531 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;3518 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
3532 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3519 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
...@@ -3542,55 +3529,55 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3542,55 +3529,55 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
3542 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });3529 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
3543}3530}
35443531
3545fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {3532fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
3546 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3533 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3547 _ = bin_op;3534 _ = bin_op;
3548 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});3535 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});
3549}3536}
35503537
3551fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {3538fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
3552 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3539 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3553 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});3540 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
3554 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3541 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3555}3542}
35563543
3557fn airClz(self: *Self, inst: Air.Inst.Index) !void {3544fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!void {
3558 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3545 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3559 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});3546 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
3560 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3547 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3561}3548}
35623549
3563fn airCtz(self: *Self, inst: Air.Inst.Index) !void {3550fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!void {
3564 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3551 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3565 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});3552 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});
3566 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3553 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3567}3554}
35683555
3569fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {3556fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!void {
3570 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3557 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3571 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});3558 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
3572 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3559 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3573}3560}
35743561
3575fn airAbs(self: *Self, inst: Air.Inst.Index) !void {3562fn airAbs(self: *Self, inst: Air.Inst.Index) InnerError!void {
3576 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3563 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3577 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch});3564 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch});
3578 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3565 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3579}3566}
35803567
3581fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {3568fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!void {
3582 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3569 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3583 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});3570 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});
3584 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3571 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3585}3572}
35863573
3587fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {3574fn airBitReverse(self: *Self, inst: Air.Inst.Index) InnerError!void {
3588 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3575 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3589 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});3576 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});
3590 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3577 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3591}3578}
35923579
3593fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {3580fn airUnaryMath(self: *Self, inst: Air.Inst.Index) InnerError!void {
3594 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;3581 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3595 const result: MCValue = if (self.liveness.isUnused(inst))3582 const result: MCValue = if (self.liveness.isUnused(inst))
3596 .dead3583 .dead
...@@ -3885,7 +3872,7 @@ fn genInlineMemsetCode(...@@ -3885,7 +3872,7 @@ fn genInlineMemsetCode(
3885 // end:3872 // end:
3886}3873}
38873874
3888fn airLoad(self: *Self, inst: Air.Inst.Index) !void {3875fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
3889 const pt = self.pt;3876 const pt = self.pt;
3890 const zcu = pt.zcu;3877 const zcu = pt.zcu;
3891 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3878 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
...@@ -4086,7 +4073,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -4086,7 +4073,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
4086 }4073 }
4087}4074}
40884075
4089fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {4076fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) InnerError!void {
4090 if (safety) {4077 if (safety) {
4091 // TODO if the value is undef, write 0xaa bytes to dest4078 // TODO if the value is undef, write 0xaa bytes to dest
4092 } else {4079 } else {
...@@ -4103,14 +4090,14 @@ fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {...@@ -4103,14 +4090,14 @@ fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
4103 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });4090 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
4104}4091}
41054092
4106fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {4093fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4107 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4094 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4108 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;4095 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
4109 const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index);4096 const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index);
4110 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });4097 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
4111}4098}
41124099
4113fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {4100fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) InnerError!void {
4114 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4101 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4115 const result = try self.structFieldPtr(inst, ty_op.operand, index);4102 const result = try self.structFieldPtr(inst, ty_op.operand, index);
4116 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });4103 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
...@@ -4138,7 +4125,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde...@@ -4138,7 +4125,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
4138 };4125 };
4139}4126}
41404127
4141fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {4128fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
4142 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4129 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4143 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;4130 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
4144 const operand = extra.struct_operand;4131 const operand = extra.struct_operand;
...@@ -4194,7 +4181,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -4194,7 +4181,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
4194 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });4181 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
4195}4182}
41964183
4197fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {4184fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4198 const pt = self.pt;4185 const pt = self.pt;
4199 const zcu = pt.zcu;4186 const zcu = pt.zcu;
4200 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4187 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
...@@ -4218,7 +4205,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4218,7 +4205,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
4218 return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none });4205 return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none });
4219}4206}
42204207
4221fn airArg(self: *Self, inst: Air.Inst.Index) !void {4208fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
4222 // skip zero-bit arguments as they don't have a corresponding arg instruction4209 // skip zero-bit arguments as they don't have a corresponding arg instruction
4223 var arg_index = self.arg_index;4210 var arg_index = self.arg_index;
4224 while (self.args[arg_index] == .none) arg_index += 1;4211 while (self.args[arg_index] == .none) arg_index += 1;
...@@ -4238,7 +4225,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -4238,7 +4225,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
4238 return self.finishAir(inst, result, .{ .none, .none, .none });4225 return self.finishAir(inst, result, .{ .none, .none, .none });
4239}4226}
42404227
4241fn airTrap(self: *Self) !void {4228fn airTrap(self: *Self) InnerError!void {
4242 _ = try self.addInst(.{4229 _ = try self.addInst(.{
4243 .tag = .brk,4230 .tag = .brk,
4244 .data = .{ .imm16 = 0x0001 },4231 .data = .{ .imm16 = 0x0001 },
...@@ -4246,7 +4233,7 @@ fn airTrap(self: *Self) !void {...@@ -4246,7 +4233,7 @@ fn airTrap(self: *Self) !void {
4246 return self.finishAirBookkeeping();4233 return self.finishAirBookkeeping();
4247}4234}
42484235
4249fn airBreakpoint(self: *Self) !void {4236fn airBreakpoint(self: *Self) InnerError!void {
4250 _ = try self.addInst(.{4237 _ = try self.addInst(.{
4251 .tag = .brk,4238 .tag = .brk,
4252 .data = .{ .imm16 = 0xf000 },4239 .data = .{ .imm16 = 0xf000 },
...@@ -4254,17 +4241,17 @@ fn airBreakpoint(self: *Self) !void {...@@ -4254,17 +4241,17 @@ fn airBreakpoint(self: *Self) !void {
4254 return self.finishAirBookkeeping();4241 return self.finishAirBookkeeping();
4255}4242}
42564243
4257fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {4244fn airRetAddr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4258 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for aarch64", .{});4245 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for aarch64", .{});
4259 return self.finishAir(inst, result, .{ .none, .none, .none });4246 return self.finishAir(inst, result, .{ .none, .none, .none });
4260}4247}
42614248
4262fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {4249fn airFrameAddress(self: *Self, inst: Air.Inst.Index) InnerError!void {
4263 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for aarch64", .{});4250 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for aarch64", .{});
4264 return self.finishAir(inst, result, .{ .none, .none, .none });4251 return self.finishAir(inst, result, .{ .none, .none, .none });
4265}4252}
42664253
4267fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {4254fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {
4268 if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{});4255 if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{});
4269 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;4256 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4270 const callee = pl_op.operand;4257 const callee = pl_op.operand;
...@@ -4422,7 +4409,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4422,7 +4409,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4422 return bt.finishAir(result);4409 return bt.finishAir(result);
4423}4410}
44244411
4425fn airRet(self: *Self, inst: Air.Inst.Index) !void {4412fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!void {
4426 const pt = self.pt;4413 const pt = self.pt;
4427 const zcu = pt.zcu;4414 const zcu = pt.zcu;
4428 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4415 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -4455,7 +4442,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {...@@ -4455,7 +4442,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
4455 return self.finishAir(inst, .dead, .{ un_op, .none, .none });4442 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
4456}4443}
44574444
4458fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {4445fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
4459 const pt = self.pt;4446 const pt = self.pt;
4460 const zcu = pt.zcu;4447 const zcu = pt.zcu;
4461 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4448 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -4499,7 +4486,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -4499,7 +4486,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
4499 return self.finishAir(inst, .dead, .{ un_op, .none, .none });4486 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
4500}4487}
45014488
4502fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {4489fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) InnerError!void {
4503 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;4490 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
4504 const lhs_ty = self.typeOf(bin_op.lhs);4491 const lhs_ty = self.typeOf(bin_op.lhs);
45054492
...@@ -4597,12 +4584,12 @@ fn cmp(...@@ -4597,12 +4584,12 @@ fn cmp(
4597 }4584 }
4598}4585}
45994586
4600fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {4587fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!void {
4601 _ = inst;4588 _ = inst;
4602 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});4589 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
4603}4590}
46044591
4605fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {4592fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!void {
4606 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4593 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4607 const operand = try self.resolveInst(un_op);4594 const operand = try self.resolveInst(un_op);
4608 _ = operand;4595 _ = operand;
...@@ -4610,7 +4597,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {...@@ -4610,7 +4597,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
4610 return self.finishAir(inst, result, .{ un_op, .none, .none });4597 return self.finishAir(inst, result, .{ un_op, .none, .none });
4611}4598}
46124599
4613fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {4600fn airDbgStmt(self: *Self, inst: Air.Inst.Index) InnerError!void {
4614 const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;4601 const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
46154602
4616 _ = try self.addInst(.{4603 _ = try self.addInst(.{
...@@ -4624,7 +4611,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {...@@ -4624,7 +4611,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
4624 return self.finishAirBookkeeping();4611 return self.finishAirBookkeeping();
4625}4612}
46264613
4627fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {4614fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) InnerError!void {
4628 const pt = self.pt;4615 const pt = self.pt;
4629 const zcu = pt.zcu;4616 const zcu = pt.zcu;
4630 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4617 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
...@@ -4635,7 +4622,7 @@ fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {...@@ -4635,7 +4622,7 @@ fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
4635 try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));4622 try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
4636}4623}
46374624
4638fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {4625fn airDbgVar(self: *Self, inst: Air.Inst.Index) InnerError!void {
4639 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;4626 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4640 const operand = pl_op.operand;4627 const operand = pl_op.operand;
4641 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];4628 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
...@@ -4686,7 +4673,7 @@ fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {...@@ -4686,7 +4673,7 @@ fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
4686 }4673 }
4687}4674}
46884675
4689fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {4676fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4690 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;4677 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4691 const cond = try self.resolveInst(pl_op.operand);4678 const cond = try self.resolveInst(pl_op.operand);
4692 const extra = self.air.extraData(Air.CondBr, pl_op.payload);4679 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
...@@ -4919,7 +4906,7 @@ fn isNonErr(...@@ -4919,7 +4906,7 @@ fn isNonErr(
4919 }4906 }
4920}4907}
49214908
4922fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {4909fn airIsNull(self: *Self, inst: Air.Inst.Index) InnerError!void {
4923 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4910 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4924 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4911 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4925 const operand = try self.resolveInst(un_op);4912 const operand = try self.resolveInst(un_op);
...@@ -4930,7 +4917,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {...@@ -4930,7 +4917,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
4930 return self.finishAir(inst, result, .{ un_op, .none, .none });4917 return self.finishAir(inst, result, .{ un_op, .none, .none });
4931}4918}
49324919
4933fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {4920fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4934 const pt = self.pt;4921 const pt = self.pt;
4935 const zcu = pt.zcu;4922 const zcu = pt.zcu;
4936 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4923 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -4947,7 +4934,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4947,7 +4934,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4947 return self.finishAir(inst, result, .{ un_op, .none, .none });4934 return self.finishAir(inst, result, .{ un_op, .none, .none });
4948}4935}
49494936
4950fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {4937fn airIsNonNull(self: *Self, inst: Air.Inst.Index) InnerError!void {
4951 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4938 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4952 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4939 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4953 const operand = try self.resolveInst(un_op);4940 const operand = try self.resolveInst(un_op);
...@@ -4958,7 +4945,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {...@@ -4958,7 +4945,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
4958 return self.finishAir(inst, result, .{ un_op, .none, .none });4945 return self.finishAir(inst, result, .{ un_op, .none, .none });
4959}4946}
49604947
4961fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {4948fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4962 const pt = self.pt;4949 const pt = self.pt;
4963 const zcu = pt.zcu;4950 const zcu = pt.zcu;
4964 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4951 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -4975,7 +4962,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4975,7 +4962,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4975 return self.finishAir(inst, result, .{ un_op, .none, .none });4962 return self.finishAir(inst, result, .{ un_op, .none, .none });
4976}4963}
49774964
4978fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {4965fn airIsErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4979 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4966 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4980 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4967 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4981 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };4968 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
...@@ -4986,7 +4973,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4986,7 +4973,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
4986 return self.finishAir(inst, result, .{ un_op, .none, .none });4973 return self.finishAir(inst, result, .{ un_op, .none, .none });
4987}4974}
49884975
4989fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {4976fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4990 const pt = self.pt;4977 const pt = self.pt;
4991 const zcu = pt.zcu;4978 const zcu = pt.zcu;
4992 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4979 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -5003,7 +4990,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -5003,7 +4990,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
5003 return self.finishAir(inst, result, .{ un_op, .none, .none });4990 return self.finishAir(inst, result, .{ un_op, .none, .none });
5004}4991}
50054992
5006fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {4993fn airIsNonErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
5007 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4994 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5008 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4995 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
5009 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };4996 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
...@@ -5014,7 +5001,7 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {...@@ -5014,7 +5001,7 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
5014 return self.finishAir(inst, result, .{ un_op, .none, .none });5001 return self.finishAir(inst, result, .{ un_op, .none, .none });
5015}5002}
50165003
5017fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {5004fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
5018 const pt = self.pt;5005 const pt = self.pt;
5019 const zcu = pt.zcu;5006 const zcu = pt.zcu;
5020 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;5007 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -5031,7 +5018,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -5031,7 +5018,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
5031 return self.finishAir(inst, result, .{ un_op, .none, .none });5018 return self.finishAir(inst, result, .{ un_op, .none, .none });
5032}5019}
50335020
5034fn airLoop(self: *Self, inst: Air.Inst.Index) !void {5021fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!void {
5035 // A loop is a setup to be able to jump back to the beginning.5022 // A loop is a setup to be able to jump back to the beginning.
5036 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5023 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5037 const loop = self.air.extraData(Air.Block, ty_pl.payload);5024 const loop = self.air.extraData(Air.Block, ty_pl.payload);
...@@ -5052,7 +5039,7 @@ fn jump(self: *Self, inst: Mir.Inst.Index) !void {...@@ -5052,7 +5039,7 @@ fn jump(self: *Self, inst: Mir.Inst.Index) !void {
5052 });5039 });
5053}5040}
50545041
5055fn airBlock(self: *Self, inst: Air.Inst.Index) !void {5042fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!void {
5056 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5043 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5057 const extra = self.air.extraData(Air.Block, ty_pl.payload);5044 const extra = self.air.extraData(Air.Block, ty_pl.payload);
5058 try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));5045 try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
...@@ -5090,7 +5077,7 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !...@@ -5090,7 +5077,7 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !
5090 return self.finishAir(inst, result, .{ .none, .none, .none });5077 return self.finishAir(inst, result, .{ .none, .none, .none });
5091}5078}
50925079
5093fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {5080fn airSwitch(self: *Self, inst: Air.Inst.Index) InnerError!void {
5094 const switch_br = self.air.unwrapSwitch(inst);5081 const switch_br = self.air.unwrapSwitch(inst);
5095 const condition_ty = self.typeOf(switch_br.operand);5082 const condition_ty = self.typeOf(switch_br.operand);
5096 const liveness = try self.liveness.getSwitchBr(5083 const liveness = try self.liveness.getSwitchBr(
...@@ -5224,7 +5211,7 @@ fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {...@@ -5224,7 +5211,7 @@ fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
5224 }5211 }
5225}5212}
52265213
5227fn airBr(self: *Self, inst: Air.Inst.Index) !void {5214fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
5228 const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br;5215 const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br;
5229 try self.br(branch.block_inst, branch.operand);5216 try self.br(branch.block_inst, branch.operand);
5230 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });5217 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
...@@ -5268,7 +5255,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -5268,7 +5255,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
5268 }));5255 }));
5269}5256}
52705257
5271fn airAsm(self: *Self, inst: Air.Inst.Index) !void {5258fn airAsm(self: *Self, inst: Air.Inst.Index) InnerError!void {
5272 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5259 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5273 const extra = self.air.extraData(Air.Asm, ty_pl.payload);5260 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
5274 const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;5261 const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
...@@ -5601,7 +5588,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5601,7 +5588,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5601 .tag = .ldr_ptr_stack,5588 .tag = .ldr_ptr_stack,
5602 .data = .{ .load_store_stack = .{5589 .data = .{ .load_store_stack = .{
5603 .rt = reg,5590 .rt = reg,
5604 .offset = @as(u32, @intCast(off)),5591 .offset = @intCast(off),
5605 } },5592 } },
5606 });5593 });
5607 },5594 },
...@@ -5617,13 +5604,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5617,13 +5604,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5617 .immediate => |x| {5604 .immediate => |x| {
5618 _ = try self.addInst(.{5605 _ = try self.addInst(.{
5619 .tag = .movz,5606 .tag = .movz,
5620 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x)) } },5607 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x) } },
5621 });5608 });
56225609
5623 if (x & 0x0000_0000_ffff_0000 != 0) {5610 if (x & 0x0000_0000_ffff_0000 != 0) {
5624 _ = try self.addInst(.{5611 _ = try self.addInst(.{
5625 .tag = .movk,5612 .tag = .movk,
5626 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x >> 16)), .hw = 1 } },5613 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 16), .hw = 1 } },
5627 });5614 });
5628 }5615 }
56295616
...@@ -5631,13 +5618,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5631,13 +5618,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5631 if (x & 0x0000_ffff_0000_0000 != 0) {5618 if (x & 0x0000_ffff_0000_0000 != 0) {
5632 _ = try self.addInst(.{5619 _ = try self.addInst(.{
5633 .tag = .movk,5620 .tag = .movk,
5634 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x >> 32)), .hw = 2 } },5621 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 32), .hw = 2 } },
5635 });5622 });
5636 }5623 }
5637 if (x & 0xffff_0000_0000_0000 != 0) {5624 if (x & 0xffff_0000_0000_0000 != 0) {
5638 _ = try self.addInst(.{5625 _ = try self.addInst(.{
5639 .tag = .movk,5626 .tag = .movk,
5640 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x >> 48)), .hw = 3 } },5627 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 48), .hw = 3 } },
5641 });5628 });
5642 }5629 }
5643 }5630 }
...@@ -5709,7 +5696,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5709,7 +5696,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5709 .tag = tag,5696 .tag = tag,
5710 .data = .{ .load_store_stack = .{5697 .data = .{ .load_store_stack = .{
5711 .rt = reg,5698 .rt = reg,
5712 .offset = @as(u32, @intCast(off)),5699 .offset = @intCast(off),
5713 } },5700 } },
5714 });5701 });
5715 },5702 },
...@@ -5733,7 +5720,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5733,7 +5720,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5733 .tag = tag,5720 .tag = tag,
5734 .data = .{ .load_store_stack = .{5721 .data = .{ .load_store_stack = .{
5735 .rt = reg,5722 .rt = reg,
5736 .offset = @as(u32, @intCast(off)),5723 .offset = @intCast(off),
5737 } },5724 } },
5738 });5725 });
5739 },5726 },
...@@ -5918,13 +5905,13 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I...@@ -5918,13 +5905,13 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
5918 }5905 }
5919}5906}
59205907
5921fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void {5908fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
5922 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;5909 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5923 const result = try self.resolveInst(un_op);5910 const result = try self.resolveInst(un_op);
5924 return self.finishAir(inst, result, .{ un_op, .none, .none });5911 return self.finishAir(inst, result, .{ un_op, .none, .none });
5925}5912}
59265913
5927fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {5914fn airBitCast(self: *Self, inst: Air.Inst.Index) InnerError!void {
5928 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5915 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5929 const result = if (self.liveness.isUnused(inst)) .dead else result: {5916 const result = if (self.liveness.isUnused(inst)) .dead else result: {
5930 const operand = try self.resolveInst(ty_op.operand);5917 const operand = try self.resolveInst(ty_op.operand);
...@@ -5945,7 +5932,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {...@@ -5945,7 +5932,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
5945 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });5932 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5946}5933}
59475934
5948fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {5935fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!void {
5949 const pt = self.pt;5936 const pt = self.pt;
5950 const zcu = pt.zcu;5937 const zcu = pt.zcu;
5951 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5938 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
...@@ -5963,7 +5950,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {...@@ -5963,7 +5950,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
5963 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });5950 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5964}5951}
59655952
5966fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {5953fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) InnerError!void {
5967 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5954 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5968 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{5955 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{
5969 self.target.cpu.arch,5956 self.target.cpu.arch,
...@@ -5971,7 +5958,7 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {...@@ -5971,7 +5958,7 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {
5971 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });5958 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5972}5959}
59735960
5974fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {5961fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) InnerError!void {
5975 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5962 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5976 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{5963 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{
5977 self.target.cpu.arch,5964 self.target.cpu.arch,
...@@ -5979,7 +5966,7 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {...@@ -5979,7 +5966,7 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {
5979 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });5966 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5980}5967}
59815968
5982fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {5969fn airCmpxchg(self: *Self, inst: Air.Inst.Index) InnerError!void {
5983 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5970 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5984 const extra = self.air.extraData(Air.Block, ty_pl.payload);5971 const extra = self.air.extraData(Air.Block, ty_pl.payload);
5985 _ = extra;5972 _ = extra;
...@@ -5989,23 +5976,23 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {...@@ -5989,23 +5976,23 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
5989 });5976 });
5990}5977}
59915978
5992fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {5979fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) InnerError!void {
5993 _ = inst;5980 _ = inst;
5994 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});5981 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
5995}5982}
59965983
5997fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {5984fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
5998 _ = inst;5985 _ = inst;
5999 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});5986 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});
6000}5987}
60015988
6002fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {5989fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) InnerError!void {
6003 _ = inst;5990 _ = inst;
6004 _ = order;5991 _ = order;
6005 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});5992 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
6006}5993}
60075994
6008fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {5995fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) InnerError!void {
6009 _ = inst;5996 _ = inst;
6010 if (safety) {5997 if (safety) {
6011 // TODO if the value is undef, write 0xaa bytes to dest5998 // TODO if the value is undef, write 0xaa bytes to dest
...@@ -6015,12 +6002,12 @@ fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {...@@ -6015,12 +6002,12 @@ fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
6015 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});6002 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
6016}6003}
60176004
6018fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {6005fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void {
6019 _ = inst;6006 _ = inst;
6020 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});6007 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
6021}6008}
60226009
6023fn airTagName(self: *Self, inst: Air.Inst.Index) !void {6010fn airTagName(self: *Self, inst: Air.Inst.Index) InnerError!void {
6024 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;6011 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
6025 const operand = try self.resolveInst(un_op);6012 const operand = try self.resolveInst(un_op);
6026 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {6013 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
...@@ -6030,7 +6017,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {...@@ -6030,7 +6017,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
6030 return self.finishAir(inst, result, .{ un_op, .none, .none });6017 return self.finishAir(inst, result, .{ un_op, .none, .none });
6031}6018}
60326019
6033fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {6020fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!void {
6034 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;6021 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
6035 const operand = try self.resolveInst(un_op);6022 const operand = try self.resolveInst(un_op);
6036 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {6023 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
...@@ -6040,33 +6027,33 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {...@@ -6040,33 +6027,33 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
6040 return self.finishAir(inst, result, .{ un_op, .none, .none });6027 return self.finishAir(inst, result, .{ un_op, .none, .none });
6041}6028}
60426029
6043fn airSplat(self: *Self, inst: Air.Inst.Index) !void {6030fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!void {
6044 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;6031 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6045 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for {}", .{self.target.cpu.arch});6032 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for {}", .{self.target.cpu.arch});
6046 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });6033 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
6047}6034}
60486035
6049fn airSelect(self: *Self, inst: Air.Inst.Index) !void {6036fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!void {
6050 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;6037 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6051 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;6038 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
6052 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch});6039 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch});
6053 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });6040 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });
6054}6041}
60556042
6056fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {6043fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!void {
6057 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6044 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6058 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;6045 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
6059 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for {}", .{self.target.cpu.arch});6046 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for {}", .{self.target.cpu.arch});
6060 return self.finishAir(inst, result, .{ extra.a, extra.b, .none });6047 return self.finishAir(inst, result, .{ extra.a, extra.b, .none });
6061}6048}
60626049
6063fn airReduce(self: *Self, inst: Air.Inst.Index) !void {6050fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!void {
6064 const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce;6051 const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
6065 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for aarch64", .{});6052 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for aarch64", .{});
6066 return self.finishAir(inst, result, .{ reduce.operand, .none, .none });6053 return self.finishAir(inst, result, .{ reduce.operand, .none, .none });
6067}6054}
60686055
6069fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {6056fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!void {
6070 const pt = self.pt;6057 const pt = self.pt;
6071 const zcu = pt.zcu;6058 const zcu = pt.zcu;
6072 const vector_ty = self.typeOfIndex(inst);6059 const vector_ty = self.typeOfIndex(inst);
...@@ -6090,19 +6077,19 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {...@@ -6090,19 +6077,19 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
6090 return bt.finishAir(result);6077 return bt.finishAir(result);
6091}6078}
60926079
6093fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {6080fn airUnionInit(self: *Self, inst: Air.Inst.Index) InnerError!void {
6094 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6081 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6095 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;6082 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
6096 _ = extra;6083 _ = extra;
6097 return self.fail("TODO implement airUnionInit for aarch64", .{});6084 return self.fail("TODO implement airUnionInit for aarch64", .{});
6098}6085}
60996086
6100fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {6087fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!void {
6101 const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;6088 const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
6102 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });6089 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
6103}6090}
61046091
6105fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {6092fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!void {
6106 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;6093 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6107 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;6094 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
6108 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {6095 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
...@@ -6111,7 +6098,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {...@@ -6111,7 +6098,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
6111 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });6098 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
6112}6099}
61136100
6114fn airTry(self: *Self, inst: Air.Inst.Index) !void {6101fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!void {
6115 const pt = self.pt;6102 const pt = self.pt;
6116 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;6103 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6117 const extra = self.air.extraData(Air.Try, pl_op.payload);6104 const extra = self.air.extraData(Air.Try, pl_op.payload);
...@@ -6139,7 +6126,7 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {...@@ -6139,7 +6126,7 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {
6139 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });6126 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
6140}6127}
61416128
6142fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {6129fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
6143 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6130 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6144 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);6131 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
6145 const body = self.air.extra[extra.end..][0..extra.data.body_len];6132 const body = self.air.extra[extra.end..][0..extra.data.body_len];
...@@ -6191,10 +6178,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {...@@ -6191,10 +6178,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
6191 .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },6178 .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },
6192 .load_symbol, .load_tlv, .lea_symbol, .lea_direct => unreachable, // TODO6179 .load_symbol, .load_tlv, .lea_symbol, .lea_direct => unreachable, // TODO
6193 },6180 },
6194 .fail => |msg| {6181 .fail => |msg| return self.failMsg(msg),
6195 self.err_msg = msg;
6196 return error.CodegenFail;
6197 },
6198 };6182 };
6199 return mcv;6183 return mcv;
6200}6184}
...@@ -6355,18 +6339,14 @@ fn wantSafety(self: *Self) bool {...@@ -6355,18 +6339,14 @@ fn wantSafety(self: *Self) bool {
6355 };6339 };
6356}6340}
63576341
6358fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {6342fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
6359 @branchHint(.cold);6343 @branchHint(.cold);
6360 assert(self.err_msg == null);6344 return self.pt.zcu.codegenFail(self.owner_nav, format, args);
6361 self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args);
6362 return error.CodegenFail;
6363}6345}
63646346
6365fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError {6347fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } {
6366 @branchHint(.cold);6348 @branchHint(.cold);
6367 assert(self.err_msg == null);6349 return self.pt.zcu.codegenFailMsg(self.owner_nav, msg);
6368 self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args);
6369 return error.CodegenFail;
6370}6350}
63716351
6372fn parseRegName(name: []const u8) ?Register {6352fn parseRegName(name: []const u8) ?Register {
src/arch/aarch64/Emit.zig+4-2
...@@ -20,7 +20,7 @@ debug_output: link.File.DebugInfoOutput,...@@ -20,7 +20,7 @@ debug_output: link.File.DebugInfoOutput,
20target: *const std.Target,20target: *const std.Target,
21err_msg: ?*ErrorMsg = null,21err_msg: ?*ErrorMsg = null,
22src_loc: Zcu.LazySrcLoc,22src_loc: Zcu.LazySrcLoc,
23code: *std.ArrayList(u8),23code: *std.ArrayListUnmanaged(u8),
2424
25prev_di_line: u32,25prev_di_line: u32,
26prev_di_column: u32,26prev_di_column: u32,
...@@ -424,8 +424,10 @@ fn lowerBranches(emit: *Emit) !void {...@@ -424,8 +424,10 @@ fn lowerBranches(emit: *Emit) !void {
424}424}
425425
426fn writeInstruction(emit: *Emit, instruction: Instruction) !void {426fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
427 const comp = emit.bin_file.comp;
428 const gpa = comp.gpa;
427 const endian = emit.target.cpu.arch.endian();429 const endian = emit.target.cpu.arch.endian();
428 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);430 std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), instruction.toU32(), endian);
429}431}
430432
431fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {433fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
src/arch/arm/CodeGen.zig+17-29
...@@ -23,7 +23,6 @@ const log = std.log.scoped(.codegen);...@@ -23,7 +23,6 @@ const log = std.log.scoped(.codegen);
23const build_options = @import("build_options");23const build_options = @import("build_options");
24const Alignment = InternPool.Alignment;24const Alignment = InternPool.Alignment;
2525
26const Result = codegen.Result;
27const CodeGenError = codegen.CodeGenError;26const CodeGenError = codegen.CodeGenError;
2827
29const bits = @import("bits.zig");28const bits = @import("bits.zig");
...@@ -333,9 +332,9 @@ pub fn generate(...@@ -333,9 +332,9 @@ pub fn generate(
333 func_index: InternPool.Index,332 func_index: InternPool.Index,
334 air: Air,333 air: Air,
335 liveness: Liveness,334 liveness: Liveness,
336 code: *std.ArrayList(u8),335 code: *std.ArrayListUnmanaged(u8),
337 debug_output: link.File.DebugInfoOutput,336 debug_output: link.File.DebugInfoOutput,
338) CodeGenError!Result {337) CodeGenError!void {
339 const zcu = pt.zcu;338 const zcu = pt.zcu;
340 const gpa = zcu.gpa;339 const gpa = zcu.gpa;
341 const func = zcu.funcInfo(func_index);340 const func = zcu.funcInfo(func_index);
...@@ -377,10 +376,7 @@ pub fn generate(...@@ -377,10 +376,7 @@ pub fn generate(
377 defer function.dbg_info_relocs.deinit(gpa);376 defer function.dbg_info_relocs.deinit(gpa);
378377
379 var call_info = function.resolveCallingConventionValues(func_ty) catch |err| switch (err) {378 var call_info = function.resolveCallingConventionValues(func_ty) catch |err| switch (err) {
380 error.CodegenFail => return Result{ .fail = function.err_msg.? },379 error.CodegenFail => return error.CodegenFail,
381 error.OutOfRegisters => return Result{
382 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
383 },
384 else => |e| return e,380 else => |e| return e,
385 };381 };
386 defer call_info.deinit(&function);382 defer call_info.deinit(&function);
...@@ -391,15 +387,14 @@ pub fn generate(...@@ -391,15 +387,14 @@ pub fn generate(
391 function.max_end_stack = call_info.stack_byte_count;387 function.max_end_stack = call_info.stack_byte_count;
392388
393 function.gen() catch |err| switch (err) {389 function.gen() catch |err| switch (err) {
394 error.CodegenFail => return Result{ .fail = function.err_msg.? },390 error.CodegenFail => return error.CodegenFail,
395 error.OutOfRegisters => return Result{391 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
396 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
397 },
398 else => |e| return e,392 else => |e| return e,
399 };393 };
400394
401 for (function.dbg_info_relocs.items) |reloc| {395 for (function.dbg_info_relocs.items) |reloc| {
402 try reloc.genDbgInfo(function);396 reloc.genDbgInfo(function) catch |err|
397 return function.fail("failed to generate debug info: {s}", .{@errorName(err)});
403 }398 }
404399
405 var mir = Mir{400 var mir = Mir{
...@@ -424,15 +419,9 @@ pub fn generate(...@@ -424,15 +419,9 @@ pub fn generate(
424 defer emit.deinit();419 defer emit.deinit();
425420
426 emit.emitMir() catch |err| switch (err) {421 emit.emitMir() catch |err| switch (err) {
427 error.EmitFail => return Result{ .fail = emit.err_msg.? },422 error.EmitFail => return function.failMsg(emit.err_msg.?),
428 else => |e| return e,423 else => |e| return e,
429 };424 };
430
431 if (function.err_msg) |em| {
432 return Result{ .fail = em };
433 } else {
434 return Result.ok;
435 }
436}425}
437426
438fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {427fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
...@@ -6310,20 +6299,19 @@ fn wantSafety(self: *Self) bool {...@@ -6310,20 +6299,19 @@ fn wantSafety(self: *Self) bool {
6310 };6299 };
6311}6300}
63126301
6313fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {6302fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
6314 @branchHint(.cold);6303 @branchHint(.cold);
6315 assert(self.err_msg == null);6304 const zcu = self.pt.zcu;
6316 const gpa = self.gpa;6305 const func = zcu.funcInfo(self.func_index);
6317 self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args);6306 const msg = try ErrorMsg.create(zcu.gpa, self.src_loc, format, args);
6318 return error.CodegenFail;6307 return zcu.codegenFailMsg(func.owner_nav, msg);
6319}6308}
63206309
6321fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError {6310fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } {
6322 @branchHint(.cold);6311 @branchHint(.cold);
6323 assert(self.err_msg == null);6312 const zcu = self.pt.zcu;
6324 const gpa = self.gpa;6313 const func = zcu.funcInfo(self.func_index);
6325 self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args);6314 return zcu.codegenFailMsg(func.owner_nav, msg);
6326 return error.CodegenFail;
6327}6315}
63286316
6329fn parseRegName(name: []const u8) ?Register {6317fn parseRegName(name: []const u8) ?Register {
src/arch/arm/Emit.zig+4-2
...@@ -24,7 +24,7 @@ debug_output: link.File.DebugInfoOutput,...@@ -24,7 +24,7 @@ debug_output: link.File.DebugInfoOutput,
24target: *const std.Target,24target: *const std.Target,
25err_msg: ?*ErrorMsg = null,25err_msg: ?*ErrorMsg = null,
26src_loc: Zcu.LazySrcLoc,26src_loc: Zcu.LazySrcLoc,
27code: *std.ArrayList(u8),27code: *std.ArrayListUnmanaged(u8),
2828
29prev_di_line: u32,29prev_di_line: u32,
30prev_di_column: u32,30prev_di_column: u32,
...@@ -342,8 +342,10 @@ fn lowerBranches(emit: *Emit) !void {...@@ -342,8 +342,10 @@ fn lowerBranches(emit: *Emit) !void {
342}342}
343343
344fn writeInstruction(emit: *Emit, instruction: Instruction) !void {344fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
345 const comp = emit.bin_file.comp;
346 const gpa = comp.gpa;
345 const endian = emit.target.cpu.arch.endian();347 const endian = emit.target.cpu.arch.endian();
346 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);348 std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), instruction.toU32(), endian);
347}349}
348350
349fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {351fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
src/arch/riscv64/CodeGen.zig+33-76
...@@ -32,7 +32,6 @@ const wip_mir_log = std.log.scoped(.wip_mir);...@@ -32,7 +32,6 @@ const wip_mir_log = std.log.scoped(.wip_mir);
32const Alignment = InternPool.Alignment;32const Alignment = InternPool.Alignment;
3333
34const CodeGenError = codegen.CodeGenError;34const CodeGenError = codegen.CodeGenError;
35const Result = codegen.Result;
3635
37const bits = @import("bits.zig");36const bits = @import("bits.zig");
38const abi = @import("abi.zig");37const abi = @import("abi.zig");
...@@ -62,7 +61,6 @@ gpa: Allocator,...@@ -62,7 +61,6 @@ gpa: Allocator,
62mod: *Package.Module,61mod: *Package.Module,
63target: *const std.Target,62target: *const std.Target,
64debug_output: link.File.DebugInfoOutput,63debug_output: link.File.DebugInfoOutput,
65err_msg: ?*ErrorMsg,
66args: []MCValue,64args: []MCValue,
67ret_mcv: InstTracking,65ret_mcv: InstTracking,
68fn_type: Type,66fn_type: Type,
...@@ -759,9 +757,9 @@ pub fn generate(...@@ -759,9 +757,9 @@ pub fn generate(
759 func_index: InternPool.Index,757 func_index: InternPool.Index,
760 air: Air,758 air: Air,
761 liveness: Liveness,759 liveness: Liveness,
762 code: *std.ArrayList(u8),760 code: *std.ArrayListUnmanaged(u8),
763 debug_output: link.File.DebugInfoOutput,761 debug_output: link.File.DebugInfoOutput,
764) CodeGenError!Result {762) CodeGenError!void {
765 const zcu = pt.zcu;763 const zcu = pt.zcu;
766 const comp = zcu.comp;764 const comp = zcu.comp;
767 const gpa = zcu.gpa;765 const gpa = zcu.gpa;
...@@ -788,7 +786,6 @@ pub fn generate(...@@ -788,7 +786,6 @@ pub fn generate(
788 .target = &mod.resolved_target.result,786 .target = &mod.resolved_target.result,
789 .debug_output = debug_output,787 .debug_output = debug_output,
790 .owner = .{ .nav_index = func.owner_nav },788 .owner = .{ .nav_index = func.owner_nav },
791 .err_msg = null,
792 .args = undefined, // populated after `resolveCallingConventionValues`789 .args = undefined, // populated after `resolveCallingConventionValues`
793 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`790 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
794 .fn_type = fn_type,791 .fn_type = fn_type,
...@@ -829,10 +826,7 @@ pub fn generate(...@@ -829,10 +826,7 @@ pub fn generate(
829826
830 const fn_info = zcu.typeToFunc(fn_type).?;827 const fn_info = zcu.typeToFunc(fn_type).?;
831 var call_info = function.resolveCallingConventionValues(fn_info, &.{}) catch |err| switch (err) {828 var call_info = function.resolveCallingConventionValues(fn_info, &.{}) catch |err| switch (err) {
832 error.CodegenFail => return Result{ .fail = function.err_msg.? },829 error.CodegenFail => return error.CodegenFail,
833 error.OutOfRegisters => return Result{
834 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
835 },
836 else => |e| return e,830 else => |e| return e,
837 };831 };
838832
...@@ -861,10 +855,8 @@ pub fn generate(...@@ -861,10 +855,8 @@ pub fn generate(
861 }));855 }));
862856
863 function.gen() catch |err| switch (err) {857 function.gen() catch |err| switch (err) {
864 error.CodegenFail => return Result{ .fail = function.err_msg.? },858 error.CodegenFail => return error.CodegenFail,
865 error.OutOfRegisters => return Result{859 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
866 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
867 },
868 else => |e| return e,860 else => |e| return e,
869 };861 };
870862
...@@ -895,28 +887,10 @@ pub fn generate(...@@ -895,28 +887,10 @@ pub fn generate(
895 defer emit.deinit();887 defer emit.deinit();
896888
897 emit.emitMir() catch |err| switch (err) {889 emit.emitMir() catch |err| switch (err) {
898 error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? },890 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?),
899 error.InvalidInstruction => |e| {891 error.InvalidInstruction => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}),
900 const msg = switch (e) {
901 error.InvalidInstruction => "CodeGen failed to find a viable instruction.",
902 };
903 return Result{
904 .fail = try ErrorMsg.create(
905 gpa,
906 src_loc,
907 "{s} This is a bug in the Zig compiler.",
908 .{msg},
909 ),
910 };
911 },
912 else => |e| return e,892 else => |e| return e,
913 };893 };
914
915 if (function.err_msg) |em| {
916 return Result{ .fail = em };
917 } else {
918 return Result.ok;
919 }
920}894}
921895
922pub fn generateLazy(896pub fn generateLazy(
...@@ -924,9 +898,9 @@ pub fn generateLazy(...@@ -924,9 +898,9 @@ pub fn generateLazy(
924 pt: Zcu.PerThread,898 pt: Zcu.PerThread,
925 src_loc: Zcu.LazySrcLoc,899 src_loc: Zcu.LazySrcLoc,
926 lazy_sym: link.File.LazySymbol,900 lazy_sym: link.File.LazySymbol,
927 code: *std.ArrayList(u8),901 code: *std.ArrayListUnmanaged(u8),
928 debug_output: link.File.DebugInfoOutput,902 debug_output: link.File.DebugInfoOutput,
929) CodeGenError!Result {903) CodeGenError!void {
930 const comp = bin_file.comp;904 const comp = bin_file.comp;
931 const gpa = comp.gpa;905 const gpa = comp.gpa;
932 const mod = comp.root_mod;906 const mod = comp.root_mod;
...@@ -941,7 +915,6 @@ pub fn generateLazy(...@@ -941,7 +915,6 @@ pub fn generateLazy(
941 .target = &mod.resolved_target.result,915 .target = &mod.resolved_target.result,
942 .debug_output = debug_output,916 .debug_output = debug_output,
943 .owner = .{ .lazy_sym = lazy_sym },917 .owner = .{ .lazy_sym = lazy_sym },
944 .err_msg = null,
945 .args = undefined, // populated after `resolveCallingConventionValues`918 .args = undefined, // populated after `resolveCallingConventionValues`
946 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`919 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
947 .fn_type = undefined,920 .fn_type = undefined,
...@@ -957,10 +930,8 @@ pub fn generateLazy(...@@ -957,10 +930,8 @@ pub fn generateLazy(
957 defer function.mir_instructions.deinit(gpa);930 defer function.mir_instructions.deinit(gpa);
958931
959 function.genLazy(lazy_sym) catch |err| switch (err) {932 function.genLazy(lazy_sym) catch |err| switch (err) {
960 error.CodegenFail => return Result{ .fail = function.err_msg.? },933 error.CodegenFail => return error.CodegenFail,
961 error.OutOfRegisters => return Result{934 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
962 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
963 },
964 else => |e| return e,935 else => |e| return e,
965 };936 };
966937
...@@ -991,28 +962,10 @@ pub fn generateLazy(...@@ -991,28 +962,10 @@ pub fn generateLazy(
991 defer emit.deinit();962 defer emit.deinit();
992963
993 emit.emitMir() catch |err| switch (err) {964 emit.emitMir() catch |err| switch (err) {
994 error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? },965 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?),
995 error.InvalidInstruction => |e| {966 error.InvalidInstruction => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}),
996 const msg = switch (e) {
997 error.InvalidInstruction => "CodeGen failed to find a viable instruction.",
998 };
999 return Result{
1000 .fail = try ErrorMsg.create(
1001 gpa,
1002 src_loc,
1003 "{s} This is a bug in the Zig compiler.",
1004 .{msg},
1005 ),
1006 };
1007 },
1008 else => |e| return e,967 else => |e| return e,
1009 };968 };
1010
1011 if (function.err_msg) |em| {
1012 return Result{ .fail = em };
1013 } else {
1014 return Result.ok;
1015 }
1016}969}
1017970
1018const FormatWipMirData = struct {971const FormatWipMirData = struct {
...@@ -4758,19 +4711,19 @@ fn airFieldParentPtr(func: *Func, inst: Air.Inst.Index) !void {...@@ -4758,19 +4711,19 @@ fn airFieldParentPtr(func: *Func, inst: Air.Inst.Index) !void {
4758 return func.fail("TODO implement codegen airFieldParentPtr", .{});4711 return func.fail("TODO implement codegen airFieldParentPtr", .{});
4759}4712}
47604713
4761fn genArgDbgInfo(func: Func, inst: Air.Inst.Index, mcv: MCValue) !void {4714fn genArgDbgInfo(func: *const Func, inst: Air.Inst.Index, mcv: MCValue) InnerError!void {
4762 const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg;4715 const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg;
4763 const ty = arg.ty.toType();4716 const ty = arg.ty.toType();
4764 if (arg.name == .none) return;4717 if (arg.name == .none) return;
47654718
4766 switch (func.debug_output) {4719 switch (func.debug_output) {
4767 .dwarf => |dw| switch (mcv) {4720 .dwarf => |dw| switch (mcv) {
4768 .register => |reg| try dw.genLocalDebugInfo(4721 .register => |reg| dw.genLocalDebugInfo(
4769 .local_arg,4722 .local_arg,
4770 arg.name.toSlice(func.air),4723 arg.name.toSlice(func.air),
4771 ty,4724 ty,
4772 .{ .reg = reg.dwarfNum() },4725 .{ .reg = reg.dwarfNum() },
4773 ),4726 ) catch |err| return func.fail("failed to generate debug info: {s}", .{@errorName(err)}),
4774 .load_frame => {},4727 .load_frame => {},
4775 else => {},4728 else => {},
4776 },4729 },
...@@ -4779,7 +4732,7 @@ fn genArgDbgInfo(func: Func, inst: Air.Inst.Index, mcv: MCValue) !void {...@@ -4779,7 +4732,7 @@ fn genArgDbgInfo(func: Func, inst: Air.Inst.Index, mcv: MCValue) !void {
4779 }4732 }
4780}4733}
47814734
4782fn airArg(func: *Func, inst: Air.Inst.Index) !void {4735fn airArg(func: *Func, inst: Air.Inst.Index) InnerError!void {
4783 var arg_index = func.arg_index;4736 var arg_index = func.arg_index;
47844737
4785 // we skip over args that have no bits4738 // we skip over args that have no bits
...@@ -5255,7 +5208,7 @@ fn airDbgInlineBlock(func: *Func, inst: Air.Inst.Index) !void {...@@ -5255,7 +5208,7 @@ fn airDbgInlineBlock(func: *Func, inst: Air.Inst.Index) !void {
5255 try func.lowerBlock(inst, @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]));5208 try func.lowerBlock(inst, @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]));
5256}5209}
52575210
5258fn airDbgVar(func: *Func, inst: Air.Inst.Index) !void {5211fn airDbgVar(func: *Func, inst: Air.Inst.Index) InnerError!void {
5259 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;5212 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
5260 const operand = pl_op.operand;5213 const operand = pl_op.operand;
5261 const ty = func.typeOf(operand);5214 const ty = func.typeOf(operand);
...@@ -5263,7 +5216,8 @@ fn airDbgVar(func: *Func, inst: Air.Inst.Index) !void {...@@ -5263,7 +5216,8 @@ fn airDbgVar(func: *Func, inst: Air.Inst.Index) !void {
5263 const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);5216 const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
52645217
5265 const tag = func.air.instructions.items(.tag)[@intFromEnum(inst)];5218 const tag = func.air.instructions.items(.tag)[@intFromEnum(inst)];
5266 try func.genVarDbgInfo(tag, ty, mcv, name.toSlice(func.air));5219 func.genVarDbgInfo(tag, ty, mcv, name.toSlice(func.air)) catch |err|
5220 return func.fail("failed to generate variable debug info: {s}", .{@errorName(err)});
52675221
5268 return func.finishAir(inst, .unreach, .{ operand, .none, .none });5222 return func.finishAir(inst, .unreach, .{ operand, .none, .none });
5269}5223}
...@@ -8236,10 +8190,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {...@@ -8236,10 +8190,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
8236 return func.fail("TODO: genTypedValue {s}", .{@tagName(mcv)});8190 return func.fail("TODO: genTypedValue {s}", .{@tagName(mcv)});
8237 },8191 },
8238 },8192 },
8239 .fail => |msg| {8193 .fail => |msg| return func.failMsg(msg),
8240 func.err_msg = msg;
8241 return error.CodegenFail;
8242 },
8243 };8194 };
8244 return mcv;8195 return mcv;
8245}8196}
...@@ -8427,17 +8378,23 @@ fn wantSafety(func: *Func) bool {...@@ -8427,17 +8378,23 @@ fn wantSafety(func: *Func) bool {
8427 };8378 };
8428}8379}
84298380
8430fn fail(func: *Func, comptime format: []const u8, args: anytype) InnerError {8381fn fail(func: *const Func, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
8431 @branchHint(.cold);8382 @branchHint(.cold);
8432 assert(func.err_msg == null);8383 const zcu = func.pt.zcu;
8433 func.err_msg = try ErrorMsg.create(func.gpa, func.src_loc, format, args);8384 switch (func.owner) {
8385 .nav_index => |i| return zcu.codegenFail(i, format, args),
8386 .lazy_sym => |s| return zcu.codegenFailType(s.ty, format, args),
8387 }
8434 return error.CodegenFail;8388 return error.CodegenFail;
8435}8389}
84368390
8437fn failSymbol(func: *Func, comptime format: []const u8, args: anytype) InnerError {8391fn failMsg(func: *const Func, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } {
8438 @branchHint(.cold);8392 @branchHint(.cold);
8439 assert(func.err_msg == null);8393 const zcu = func.pt.zcu;
8440 func.err_msg = try ErrorMsg.create(func.gpa, func.src_loc, format, args);8394 switch (func.owner) {
8395 .nav_index => |i| return zcu.codegenFailMsg(i, msg),
8396 .lazy_sym => |s| return zcu.codegenFailTypeMsg(s.ty, msg),
8397 }
8441 return error.CodegenFail;8398 return error.CodegenFail;
8442}8399}
84438400
src/arch/riscv64/Emit.zig+9-8
...@@ -3,7 +3,7 @@...@@ -3,7 +3,7 @@
3bin_file: *link.File,3bin_file: *link.File,
4lower: Lower,4lower: Lower,
5debug_output: link.File.DebugInfoOutput,5debug_output: link.File.DebugInfoOutput,
6code: *std.ArrayList(u8),6code: *std.ArrayListUnmanaged(u8),
77
8prev_di_line: u32,8prev_di_line: u32,
9prev_di_column: u32,9prev_di_column: u32,
...@@ -18,6 +18,7 @@ pub const Error = Lower.Error || error{...@@ -18,6 +18,7 @@ pub const Error = Lower.Error || error{
18};18};
1919
20pub fn emitMir(emit: *Emit) Error!void {20pub fn emitMir(emit: *Emit) Error!void {
21 const gpa = emit.bin_file.comp.gpa;
21 log.debug("mir instruction len: {}", .{emit.lower.mir.instructions.len});22 log.debug("mir instruction len: {}", .{emit.lower.mir.instructions.len});
22 for (0..emit.lower.mir.instructions.len) |mir_i| {23 for (0..emit.lower.mir.instructions.len) |mir_i| {
23 const mir_index: Mir.Inst.Index = @intCast(mir_i);24 const mir_index: Mir.Inst.Index = @intCast(mir_i);
...@@ -30,7 +31,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -30,7 +31,7 @@ pub fn emitMir(emit: *Emit) Error!void {
30 var lowered_relocs = lowered.relocs;31 var lowered_relocs = lowered.relocs;
31 for (lowered.insts, 0..) |lowered_inst, lowered_index| {32 for (lowered.insts, 0..) |lowered_inst, lowered_index| {
32 const start_offset: u32 = @intCast(emit.code.items.len);33 const start_offset: u32 = @intCast(emit.code.items.len);
33 try lowered_inst.encode(emit.code.writer());34 try lowered_inst.encode(emit.code.writer(gpa));
3435
35 while (lowered_relocs.len > 0 and36 while (lowered_relocs.len > 0 and
36 lowered_relocs[0].lowered_inst_index == lowered_index) : ({37 lowered_relocs[0].lowered_inst_index == lowered_index) : ({
...@@ -56,13 +57,13 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -56,13 +57,13 @@ pub fn emitMir(emit: *Emit) Error!void {
56 const hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20);57 const hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20);
57 const lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I);58 const lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I);
5859
59 try atom_ptr.addReloc(elf_file.base.comp.gpa, .{60 try atom_ptr.addReloc(gpa, .{
60 .r_offset = start_offset,61 .r_offset = start_offset,
61 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type,62 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type,
62 .r_addend = 0,63 .r_addend = 0,
63 }, zo);64 }, zo);
6465
65 try atom_ptr.addReloc(elf_file.base.comp.gpa, .{66 try atom_ptr.addReloc(gpa, .{
66 .r_offset = start_offset + 4,67 .r_offset = start_offset + 4,
67 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type,68 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type,
68 .r_addend = 0,69 .r_addend = 0,
...@@ -76,19 +77,19 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -76,19 +77,19 @@ pub fn emitMir(emit: *Emit) Error!void {
7677
77 const R_RISCV = std.elf.R_RISCV;78 const R_RISCV = std.elf.R_RISCV;
7879
79 try atom_ptr.addReloc(elf_file.base.comp.gpa, .{80 try atom_ptr.addReloc(gpa, .{
80 .r_offset = start_offset,81 .r_offset = start_offset,
81 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20),82 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20),
82 .r_addend = 0,83 .r_addend = 0,
83 }, zo);84 }, zo);
8485
85 try atom_ptr.addReloc(elf_file.base.comp.gpa, .{86 try atom_ptr.addReloc(gpa, .{
86 .r_offset = start_offset + 4,87 .r_offset = start_offset + 4,
87 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD),88 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD),
88 .r_addend = 0,89 .r_addend = 0,
89 }, zo);90 }, zo);
9091
91 try atom_ptr.addReloc(elf_file.base.comp.gpa, .{92 try atom_ptr.addReloc(gpa, .{
92 .r_offset = start_offset + 8,93 .r_offset = start_offset + 8,
93 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I),94 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I),
94 .r_addend = 0,95 .r_addend = 0,
...@@ -101,7 +102,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -101,7 +102,7 @@ pub fn emitMir(emit: *Emit) Error!void {
101102
102 const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT);103 const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT);
103104
104 try atom_ptr.addReloc(elf_file.base.comp.gpa, .{105 try atom_ptr.addReloc(gpa, .{
105 .r_offset = start_offset,106 .r_offset = start_offset,
106 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,107 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
107 .r_addend = 0,108 .r_addend = 0,
src/arch/sparc64/CodeGen.zig+24-28
...@@ -21,7 +21,6 @@ const Emit = @import("Emit.zig");...@@ -21,7 +21,6 @@ const Emit = @import("Emit.zig");
21const Liveness = @import("../../Liveness.zig");21const Liveness = @import("../../Liveness.zig");
22const Type = @import("../../Type.zig");22const Type = @import("../../Type.zig");
23const CodeGenError = codegen.CodeGenError;23const CodeGenError = codegen.CodeGenError;
24const Result = @import("../../codegen.zig").Result;
25const Endian = std.builtin.Endian;24const Endian = std.builtin.Endian;
26const Alignment = InternPool.Alignment;25const Alignment = InternPool.Alignment;
2726
...@@ -55,7 +54,7 @@ liveness: Liveness,...@@ -55,7 +54,7 @@ liveness: Liveness,
55bin_file: *link.File,54bin_file: *link.File,
56target: *const std.Target,55target: *const std.Target,
57func_index: InternPool.Index,56func_index: InternPool.Index,
58code: *std.ArrayList(u8),57code: *std.ArrayListUnmanaged(u8),
59debug_output: link.File.DebugInfoOutput,58debug_output: link.File.DebugInfoOutput,
60err_msg: ?*ErrorMsg,59err_msg: ?*ErrorMsg,
61args: []MCValue,60args: []MCValue,
...@@ -266,9 +265,9 @@ pub fn generate(...@@ -266,9 +265,9 @@ pub fn generate(
266 func_index: InternPool.Index,265 func_index: InternPool.Index,
267 air: Air,266 air: Air,
268 liveness: Liveness,267 liveness: Liveness,
269 code: *std.ArrayList(u8),268 code: *std.ArrayListUnmanaged(u8),
270 debug_output: link.File.DebugInfoOutput,269 debug_output: link.File.DebugInfoOutput,
271) CodeGenError!Result {270) CodeGenError!void {
272 const zcu = pt.zcu;271 const zcu = pt.zcu;
273 const gpa = zcu.gpa;272 const gpa = zcu.gpa;
274 const func = zcu.funcInfo(func_index);273 const func = zcu.funcInfo(func_index);
...@@ -284,7 +283,7 @@ pub fn generate(...@@ -284,7 +283,7 @@ pub fn generate(
284 }283 }
285 try branch_stack.append(.{});284 try branch_stack.append(.{});
286285
287 var function = Self{286 var function: Self = .{
288 .gpa = gpa,287 .gpa = gpa,
289 .pt = pt,288 .pt = pt,
290 .air = air,289 .air = air,
...@@ -310,10 +309,7 @@ pub fn generate(...@@ -310,10 +309,7 @@ pub fn generate(
310 defer function.exitlude_jump_relocs.deinit(gpa);309 defer function.exitlude_jump_relocs.deinit(gpa);
311310
312 var call_info = function.resolveCallingConventionValues(func_ty, .callee) catch |err| switch (err) {311 var call_info = function.resolveCallingConventionValues(func_ty, .callee) catch |err| switch (err) {
313 error.CodegenFail => return Result{ .fail = function.err_msg.? },312 error.CodegenFail => return error.CodegenFail,
314 error.OutOfRegisters => return Result{
315 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
316 },
317 else => |e| return e,313 else => |e| return e,
318 };314 };
319 defer call_info.deinit(&function);315 defer call_info.deinit(&function);
...@@ -324,10 +320,8 @@ pub fn generate(...@@ -324,10 +320,8 @@ pub fn generate(
324 function.max_end_stack = call_info.stack_byte_count;320 function.max_end_stack = call_info.stack_byte_count;
325321
326 function.gen() catch |err| switch (err) {322 function.gen() catch |err| switch (err) {
327 error.CodegenFail => return Result{ .fail = function.err_msg.? },323 error.CodegenFail => return error.CodegenFail,
328 error.OutOfRegisters => return Result{324 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
329 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
330 },
331 else => |e| return e,325 else => |e| return e,
332 };326 };
333327
...@@ -337,7 +331,7 @@ pub fn generate(...@@ -337,7 +331,7 @@ pub fn generate(
337 };331 };
338 defer mir.deinit(gpa);332 defer mir.deinit(gpa);
339333
340 var emit = Emit{334 var emit: Emit = .{
341 .mir = mir,335 .mir = mir,
342 .bin_file = lf,336 .bin_file = lf,
343 .debug_output = debug_output,337 .debug_output = debug_output,
...@@ -351,15 +345,9 @@ pub fn generate(...@@ -351,15 +345,9 @@ pub fn generate(
351 defer emit.deinit();345 defer emit.deinit();
352346
353 emit.emitMir() catch |err| switch (err) {347 emit.emitMir() catch |err| switch (err) {
354 error.EmitFail => return Result{ .fail = emit.err_msg.? },348 error.EmitFail => return function.failMsg(emit.err_msg.?),
355 else => |e| return e,349 else => |e| return e,
356 };350 };
357
358 if (function.err_msg) |em| {
359 return Result{ .fail = em };
360 } else {
361 return Result.ok;
362 }
363}351}
364352
365fn gen(self: *Self) !void {353fn gen(self: *Self) !void {
...@@ -1014,7 +1002,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -1014,7 +1002,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1014 return bt.finishAir(result);1002 return bt.finishAir(result);
1015}1003}
10161004
1017fn airArg(self: *Self, inst: Air.Inst.Index) !void {1005fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
1018 const pt = self.pt;1006 const pt = self.pt;
1019 const zcu = pt.zcu;1007 const zcu = pt.zcu;
1020 const arg_index = self.arg_index;1008 const arg_index = self.arg_index;
...@@ -1036,7 +1024,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -1036,7 +1024,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1036 }1024 }
1037 };1025 };
10381026
1039 try self.genArgDbgInfo(inst, mcv);1027 self.genArgDbgInfo(inst, mcv) catch |err|
1028 return self.fail("failed to generate debug info for parameter: {s}", .{@errorName(err)});
10401029
1041 if (self.liveness.isUnused(inst))1030 if (self.liveness.isUnused(inst))
1042 return self.finishAirBookkeeping();1031 return self.finishAirBookkeeping();
...@@ -3511,12 +3500,19 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)...@@ -3511,12 +3500,19 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)
3511 }3500 }
3512}3501}
35133502
3514fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {3503fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
3515 @branchHint(.cold);3504 @branchHint(.cold);
3516 assert(self.err_msg == null);3505 const zcu = self.pt.zcu;
3517 const gpa = self.gpa;3506 const func = zcu.funcInfo(self.func_index);
3518 self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args);3507 const msg = try ErrorMsg.create(zcu.gpa, self.src_loc, format, args);
3519 return error.CodegenFail;3508 return zcu.codegenFailMsg(func.owner_nav, msg);
3509}
3510
3511fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } {
3512 @branchHint(.cold);
3513 const zcu = self.pt.zcu;
3514 const func = zcu.funcInfo(self.func_index);
3515 return zcu.codegenFailMsg(func.owner_nav, msg);
3520}3516}
35213517
3522/// Called when there are no operands, and the instruction is always unreferenced.3518/// Called when there are no operands, and the instruction is always unreferenced.
src/arch/sparc64/Emit.zig+6-3
...@@ -22,7 +22,7 @@ debug_output: link.File.DebugInfoOutput,...@@ -22,7 +22,7 @@ debug_output: link.File.DebugInfoOutput,
22target: *const std.Target,22target: *const std.Target,
23err_msg: ?*ErrorMsg = null,23err_msg: ?*ErrorMsg = null,
24src_loc: Zcu.LazySrcLoc,24src_loc: Zcu.LazySrcLoc,
25code: *std.ArrayList(u8),25code: *std.ArrayListUnmanaged(u8),
2626
27prev_di_line: u32,27prev_di_line: u32,
28prev_di_column: u32,28prev_di_column: u32,
...@@ -678,10 +678,13 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType {...@@ -678,10 +678,13 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType {
678}678}
679679
680fn writeInstruction(emit: *Emit, instruction: Instruction) !void {680fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
681 const comp = emit.bin_file.comp;
682 const gpa = comp.gpa;
683
681 // SPARCv9 instructions are always arranged in BE regardless of the684 // SPARCv9 instructions are always arranged in BE regardless of the
682 // endianness mode the CPU is running in (Section 3.1 of the ISA specification).685 // endianness mode the CPU is running in (Section 3.1 of the ISA specification).
683 // This is to ease porting in case someone wants to do a LE SPARCv9 backend.686 // This is to ease porting in case someone wants to do a LE SPARCv9 backend.
684 const endian = Endian.big;687 const endian: Endian = .big;
685688
686 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);689 std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), instruction.toU32(), endian);
687}690}
src/arch/wasm/CodeGen.zig+3170-3455
...@@ -1,14 +1,13 @@...@@ -1,14 +1,13 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
4const ArrayList = std.ArrayList;
5const assert = std.debug.assert;4const assert = std.debug.assert;
6const testing = std.testing;5const testing = std.testing;
7const leb = std.leb;6const leb = std.leb;
8const mem = std.mem;7const mem = std.mem;
9const wasm = std.wasm;
10const log = std.log.scoped(.codegen);8const log = std.log.scoped(.codegen);
119
10const CodeGen = @This();
12const codegen = @import("../../codegen.zig");11const codegen = @import("../../codegen.zig");
13const Zcu = @import("../../Zcu.zig");12const Zcu = @import("../../Zcu.zig");
14const InternPool = @import("../../InternPool.zig");13const InternPool = @import("../../InternPool.zig");
...@@ -19,13 +18,113 @@ const Compilation = @import("../../Compilation.zig");...@@ -19,13 +18,113 @@ const Compilation = @import("../../Compilation.zig");
19const link = @import("../../link.zig");18const link = @import("../../link.zig");
20const Air = @import("../../Air.zig");19const Air = @import("../../Air.zig");
21const Liveness = @import("../../Liveness.zig");20const Liveness = @import("../../Liveness.zig");
22const target_util = @import("../../target.zig");
23const Mir = @import("Mir.zig");21const Mir = @import("Mir.zig");
24const Emit = @import("Emit.zig");22const Emit = @import("Emit.zig");
25const abi = @import("abi.zig");23const abi = @import("abi.zig");
26const Alignment = InternPool.Alignment;24const Alignment = InternPool.Alignment;
27const errUnionPayloadOffset = codegen.errUnionPayloadOffset;25const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
28const errUnionErrorOffset = codegen.errUnionErrorOffset;26const errUnionErrorOffset = codegen.errUnionErrorOffset;
27const Wasm = link.File.Wasm;
28
29const target_util = @import("../../target.zig");
30const libcFloatPrefix = target_util.libcFloatPrefix;
31const libcFloatSuffix = target_util.libcFloatSuffix;
32const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev;
33const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev;
34
35/// Reference to the function declaration the code
36/// section belongs to
37owner_nav: InternPool.Nav.Index,
38/// Current block depth. Used to calculate the relative difference between a break
39/// and block
40block_depth: u32 = 0,
41air: Air,
42liveness: Liveness,
43gpa: mem.Allocator,
44func_index: InternPool.Index,
45/// Contains a list of current branches.
46/// When we return from a branch, the branch will be popped from this list,
47/// which means branches can only contain references from within its own branch,
48/// or a branch higher (lower index) in the tree.
49branches: std.ArrayListUnmanaged(Branch) = .empty,
50/// Table to save `WValue`'s generated by an `Air.Inst`
51// values: ValueTable,
52/// Mapping from Air.Inst.Index to block ids
53blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct {
54 label: u32,
55 value: WValue,
56}) = .{},
57/// Maps `loop` instructions to their label. `br` to here repeats the loop.
58loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty,
59/// The index the next local generated will have
60/// NOTE: arguments share the index with locals therefore the first variable
61/// will have the index that comes after the last argument's index
62local_index: u32,
63/// The index of the current argument.
64/// Used to track which argument is being referenced in `airArg`.
65arg_index: u32 = 0,
66/// List of simd128 immediates. Each value is stored as an array of bytes.
67/// This list will only be populated for 128bit-simd values when the target features
68/// are enabled also.
69simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty,
70/// The Target we're emitting (used to call intInfo)
71target: *const std.Target,
72ptr_size: enum { wasm32, wasm64 },
73wasm: *link.File.Wasm,
74pt: Zcu.PerThread,
75/// List of MIR Instructions
76mir_instructions: *std.MultiArrayList(Mir.Inst),
77/// Contains extra data for MIR
78mir_extra: *std.ArrayListUnmanaged(u32),
79start_mir_extra_off: u32,
80start_locals_off: u32,
81/// List of all locals' types generated throughout this declaration
82/// used to emit locals count at start of 'code' section.
83locals: *std.ArrayListUnmanaged(std.wasm.Valtype),
84/// When a function is executing, we store the the current stack pointer's value within this local.
85/// This value is then used to restore the stack pointer to the original value at the return of the function.
86initial_stack_value: WValue = .none,
87/// The current stack pointer subtracted with the stack size. From this value, we will calculate
88/// all offsets of the stack values.
89bottom_stack_value: WValue = .none,
90/// Arguments of this function declaration
91/// This will be set after `resolveCallingConventionValues`
92args: []WValue,
93/// This will only be `.none` if the function returns void, or returns an immediate.
94/// When it returns a pointer to the stack, the `.local` tag will be active and must be populated
95/// before this function returns its execution to the caller.
96return_value: WValue,
97/// The size of the stack this function occupies. In the function prologue
98/// we will move the stack pointer by this number, forward aligned with the `stack_alignment`.
99stack_size: u32 = 0,
100/// The stack alignment, which is 16 bytes by default. This is specified by the
101/// tool-conventions: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md
102/// and also what the llvm backend will emit.
103/// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default.
104stack_alignment: Alignment = .@"16",
105
106// For each individual Wasm valtype we store a seperate free list which
107// allows us to re-use locals that are no longer used. e.g. a temporary local.
108/// A list of indexes which represents a local of valtype `i32`.
109/// It is illegal to store a non-i32 valtype in this list.
110free_locals_i32: std.ArrayListUnmanaged(u32) = .empty,
111/// A list of indexes which represents a local of valtype `i64`.
112/// It is illegal to store a non-i64 valtype in this list.
113free_locals_i64: std.ArrayListUnmanaged(u32) = .empty,
114/// A list of indexes which represents a local of valtype `f32`.
115/// It is illegal to store a non-f32 valtype in this list.
116free_locals_f32: std.ArrayListUnmanaged(u32) = .empty,
117/// A list of indexes which represents a local of valtype `f64`.
118/// It is illegal to store a non-f64 valtype in this list.
119free_locals_f64: std.ArrayListUnmanaged(u32) = .empty,
120/// A list of indexes which represents a local of valtype `v127`.
121/// It is illegal to store a non-v128 valtype in this list.
122free_locals_v128: std.ArrayListUnmanaged(u32) = .empty,
123
124/// When in debug mode, this tracks if no `finishAir` was missed.
125/// Forgetting to call `finishAir` will cause the result to not be
126/// stored in our `values` map and therefore cause bugs.
127air_bookkeeping: @TypeOf(bookkeeping_init) = bookkeeping_init,
29128
30/// Wasm Value, created when generating an instruction129/// Wasm Value, created when generating an instruction
31const WValue = union(enum) {130const WValue = union(enum) {
...@@ -55,22 +154,15 @@ const WValue = union(enum) {...@@ -55,22 +154,15 @@ const WValue = union(enum) {
55 float32: f32,154 float32: f32,
56 /// A constant 64bit float value155 /// A constant 64bit float value
57 float64: f64,156 float64: f64,
58 /// A value that represents a pointer to the data section157 nav_ref: struct {
59 /// Note: The value contains the symbol index, rather than the actual address158 nav_index: InternPool.Nav.Index,
60 /// as we use this to perform the relocation.159 offset: i32 = 0,
61 memory: u32,160 },
62 /// A value that represents a parent pointer and an offset161 uav_ref: struct {
63 /// from that pointer. i.e. when slicing with constant values.162 ip_index: InternPool.Index,
64 memory_offset: struct {163 offset: i32 = 0,
65 /// The symbol of the parent pointer164 orig_ptr_ty: InternPool.Index = .none,
66 pointer: u32,
67 /// Offset will be set as addend when relocating
68 offset: u32,
69 },165 },
70 /// Represents a function pointer
71 /// In wasm function pointers are indexes into a function table,
72 /// rather than an address in the data section.
73 function_index: u32,
74 /// Offset from the bottom of the virtual stack, with the offset166 /// Offset from the bottom of the virtual stack, with the offset
75 /// pointing to where the value lives.167 /// pointing to where the value lives.
76 stack_offset: struct {168 stack_offset: struct {
...@@ -101,7 +193,7 @@ const WValue = union(enum) {...@@ -101,7 +193,7 @@ const WValue = union(enum) {
101 switch (value) {193 switch (value) {
102 .stack => {194 .stack => {
103 const new_local = try gen.allocLocal(ty);195 const new_local = try gen.allocLocal(ty);
104 try gen.addLabel(.local_set, new_local.local.value);196 try gen.addLocal(.local_set, new_local.local.value);
105 return new_local;197 return new_local;
106 },198 },
107 .local, .stack_offset => return value,199 .local, .stack_offset => return value,
...@@ -119,7 +211,7 @@ const WValue = union(enum) {...@@ -119,7 +211,7 @@ const WValue = union(enum) {
119 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.211 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.
120212
121 const index = local_value - reserved;213 const index = local_value - reserved;
122 const valtype = @as(wasm.Valtype, @enumFromInt(gen.locals.items[index]));214 const valtype = gen.locals.items[gen.start_locals_off + index];
123 switch (valtype) {215 switch (valtype) {
124 .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead216 .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead
125 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,217 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,
...@@ -132,8 +224,6 @@ const WValue = union(enum) {...@@ -132,8 +224,6 @@ const WValue = union(enum) {
132 }224 }
133};225};
134226
135/// Wasm ops, but without input/output/signedness information
136/// Used for `buildOpcode`
137const Op = enum {227const Op = enum {
138 @"unreachable",228 @"unreachable",
139 nop,229 nop,
...@@ -147,12 +237,8 @@ const Op = enum {...@@ -147,12 +237,8 @@ const Op = enum {
147 br_table,237 br_table,
148 @"return",238 @"return",
149 call,239 call,
150 call_indirect,
151 drop,240 drop,
152 select,241 select,
153 local_get,
154 local_set,
155 local_tee,
156 global_get,242 global_get,
157 global_set,243 global_set,
158 load,244 load,
...@@ -200,70 +286,38 @@ const Op = enum {...@@ -200,70 +286,38 @@ const Op = enum {
200 extend,286 extend,
201};287};
202288
203/// Contains the settings needed to create an `Opcode` using `buildOpcode`.
204///
205/// The fields correspond to the opcode name. Here is an example
206/// i32_trunc_f32_s
207/// ^ ^ ^ ^
208/// | | | |
209/// valtype1 | | |
210/// = .i32 | | |
211/// | | |
212/// op | |
213/// = .trunc | |
214/// | |
215/// valtype2 |
216/// = .f32 |
217/// |
218/// width |
219/// = null |
220/// |
221/// signed
222/// = true
223///
224/// There can be missing fields, here are some more examples:
225/// i64_load8_u
226/// --> .{ .valtype1 = .i64, .op = .load, .width = 8, signed = false }
227/// i32_mul
228/// --> .{ .valtype1 = .i32, .op = .trunc }
229/// nop
230/// --> .{ .op = .nop }
231const OpcodeBuildArguments = struct {289const OpcodeBuildArguments = struct {
232 /// First valtype in the opcode (usually represents the type of the output)290 /// First valtype in the opcode (usually represents the type of the output)
233 valtype1: ?wasm.Valtype = null,291 valtype1: ?std.wasm.Valtype = null,
234 /// The operation (e.g. call, unreachable, div, min, sqrt, etc.)292 /// The operation (e.g. call, unreachable, div, min, sqrt, etc.)
235 op: Op,293 op: Op,
236 /// Width of the operation (e.g. 8 for i32_load8_s, 16 for i64_extend16_i32_s)294 /// Width of the operation (e.g. 8 for i32_load8_s, 16 for i64_extend16_i32_s)
237 width: ?u8 = null,295 width: ?u8 = null,
238 /// Second valtype in the opcode name (usually represents the type of the input)296 /// Second valtype in the opcode name (usually represents the type of the input)
239 valtype2: ?wasm.Valtype = null,297 valtype2: ?std.wasm.Valtype = null,
240 /// Signedness of the op298 /// Signedness of the op
241 signedness: ?std.builtin.Signedness = null,299 signedness: ?std.builtin.Signedness = null,
242};300};
243301
244/// Helper function that builds an Opcode given the arguments needed302/// TODO: deprecated, should be split up per tag.
245fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {303fn buildOpcode(args: OpcodeBuildArguments) std.wasm.Opcode {
246 switch (args.op) {304 switch (args.op) {
247 .@"unreachable" => return .@"unreachable",305 .@"unreachable" => unreachable,
248 .nop => return .nop,306 .nop => unreachable,
249 .block => return .block,307 .block => unreachable,
250 .loop => return .loop,308 .loop => unreachable,
251 .@"if" => return .@"if",309 .@"if" => unreachable,
252 .@"else" => return .@"else",310 .@"else" => unreachable,
253 .end => return .end,311 .end => unreachable,
254 .br => return .br,312 .br => unreachable,
255 .br_if => return .br_if,313 .br_if => unreachable,
256 .br_table => return .br_table,314 .br_table => unreachable,
257 .@"return" => return .@"return",315 .@"return" => unreachable,
258 .call => return .call,316 .call => unreachable,
259 .call_indirect => return .call_indirect,317 .drop => unreachable,
260 .drop => return .drop,318 .select => unreachable,
261 .select => return .select,319 .global_get => unreachable,
262 .local_get => return .local_get,320 .global_set => unreachable,
263 .local_set => return .local_set,
264 .local_tee => return .local_tee,
265 .global_get => return .global_get,
266 .global_set => return .global_set,
267321
268 .load => if (args.width) |width| switch (width) {322 .load => if (args.width) |width| switch (width) {
269 8 => switch (args.valtype1.?) {323 8 => switch (args.valtype1.?) {
...@@ -621,121 +675,17 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -621,121 +675,17 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
621test "Wasm - buildOpcode" {675test "Wasm - buildOpcode" {
622 // Make sure buildOpcode is referenced, and test some examples676 // Make sure buildOpcode is referenced, and test some examples
623 const i32_const = buildOpcode(.{ .op = .@"const", .valtype1 = .i32 });677 const i32_const = buildOpcode(.{ .op = .@"const", .valtype1 = .i32 });
624 const end = buildOpcode(.{ .op = .end });
625 const local_get = buildOpcode(.{ .op = .local_get });
626 const i64_extend32_s = buildOpcode(.{ .op = .extend, .valtype1 = .i64, .width = 32, .signedness = .signed });678 const i64_extend32_s = buildOpcode(.{ .op = .extend, .valtype1 = .i64, .width = 32, .signedness = .signed });
627 const f64_reinterpret_i64 = buildOpcode(.{ .op = .reinterpret, .valtype1 = .f64, .valtype2 = .i64 });679 const f64_reinterpret_i64 = buildOpcode(.{ .op = .reinterpret, .valtype1 = .f64, .valtype2 = .i64 });
628680
629 try testing.expectEqual(@as(wasm.Opcode, .i32_const), i32_const);681 try testing.expectEqual(@as(std.wasm.Opcode, .i32_const), i32_const);
630 try testing.expectEqual(@as(wasm.Opcode, .end), end);682 try testing.expectEqual(@as(std.wasm.Opcode, .i64_extend32_s), i64_extend32_s);
631 try testing.expectEqual(@as(wasm.Opcode, .local_get), local_get);683 try testing.expectEqual(@as(std.wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64);
632 try testing.expectEqual(@as(wasm.Opcode, .i64_extend32_s), i64_extend32_s);
633 try testing.expectEqual(@as(wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64);
634}684}
635685
636/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`686/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
637pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue);687pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue);
638688
639const CodeGen = @This();
640
641/// Reference to the function declaration the code
642/// section belongs to
643owner_nav: InternPool.Nav.Index,
644src_loc: Zcu.LazySrcLoc,
645/// Current block depth. Used to calculate the relative difference between a break
646/// and block
647block_depth: u32 = 0,
648air: Air,
649liveness: Liveness,
650gpa: mem.Allocator,
651debug_output: link.File.DebugInfoOutput,
652func_index: InternPool.Index,
653/// Contains a list of current branches.
654/// When we return from a branch, the branch will be popped from this list,
655/// which means branches can only contain references from within its own branch,
656/// or a branch higher (lower index) in the tree.
657branches: std.ArrayListUnmanaged(Branch) = .empty,
658/// Table to save `WValue`'s generated by an `Air.Inst`
659// values: ValueTable,
660/// Mapping from Air.Inst.Index to block ids
661blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct {
662 label: u32,
663 value: WValue,
664}) = .{},
665/// Maps `loop` instructions to their label. `br` to here repeats the loop.
666loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty,
667/// `bytes` contains the wasm bytecode belonging to the 'code' section.
668code: *ArrayList(u8),
669/// The index the next local generated will have
670/// NOTE: arguments share the index with locals therefore the first variable
671/// will have the index that comes after the last argument's index
672local_index: u32 = 0,
673/// The index of the current argument.
674/// Used to track which argument is being referenced in `airArg`.
675arg_index: u32 = 0,
676/// If codegen fails, an error messages will be allocated and saved in `err_msg`
677err_msg: *Zcu.ErrorMsg,
678/// List of all locals' types generated throughout this declaration
679/// used to emit locals count at start of 'code' section.
680locals: std.ArrayListUnmanaged(u8),
681/// List of simd128 immediates. Each value is stored as an array of bytes.
682/// This list will only be populated for 128bit-simd values when the target features
683/// are enabled also.
684simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty,
685/// The Target we're emitting (used to call intInfo)
686target: *const std.Target,
687/// Represents the wasm binary file that is being linked.
688bin_file: *link.File.Wasm,
689pt: Zcu.PerThread,
690/// List of MIR Instructions
691mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
692/// Contains extra data for MIR
693mir_extra: std.ArrayListUnmanaged(u32) = .empty,
694/// When a function is executing, we store the the current stack pointer's value within this local.
695/// This value is then used to restore the stack pointer to the original value at the return of the function.
696initial_stack_value: WValue = .none,
697/// The current stack pointer subtracted with the stack size. From this value, we will calculate
698/// all offsets of the stack values.
699bottom_stack_value: WValue = .none,
700/// Arguments of this function declaration
701/// This will be set after `resolveCallingConventionValues`
702args: []WValue = &.{},
703/// This will only be `.none` if the function returns void, or returns an immediate.
704/// When it returns a pointer to the stack, the `.local` tag will be active and must be populated
705/// before this function returns its execution to the caller.
706return_value: WValue = .none,
707/// The size of the stack this function occupies. In the function prologue
708/// we will move the stack pointer by this number, forward aligned with the `stack_alignment`.
709stack_size: u32 = 0,
710/// The stack alignment, which is 16 bytes by default. This is specified by the
711/// tool-conventions: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md
712/// and also what the llvm backend will emit.
713/// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default.
714stack_alignment: Alignment = .@"16",
715
716// For each individual Wasm valtype we store a seperate free list which
717// allows us to re-use locals that are no longer used. e.g. a temporary local.
718/// A list of indexes which represents a local of valtype `i32`.
719/// It is illegal to store a non-i32 valtype in this list.
720free_locals_i32: std.ArrayListUnmanaged(u32) = .empty,
721/// A list of indexes which represents a local of valtype `i64`.
722/// It is illegal to store a non-i64 valtype in this list.
723free_locals_i64: std.ArrayListUnmanaged(u32) = .empty,
724/// A list of indexes which represents a local of valtype `f32`.
725/// It is illegal to store a non-f32 valtype in this list.
726free_locals_f32: std.ArrayListUnmanaged(u32) = .empty,
727/// A list of indexes which represents a local of valtype `f64`.
728/// It is illegal to store a non-f64 valtype in this list.
729free_locals_f64: std.ArrayListUnmanaged(u32) = .empty,
730/// A list of indexes which represents a local of valtype `v127`.
731/// It is illegal to store a non-v128 valtype in this list.
732free_locals_v128: std.ArrayListUnmanaged(u32) = .empty,
733
734/// When in debug mode, this tracks if no `finishAir` was missed.
735/// Forgetting to call `finishAir` will cause the result to not be
736/// stored in our `values` map and therefore cause bugs.
737air_bookkeeping: @TypeOf(bookkeeping_init) = bookkeeping_init,
738
739const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};689const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
740690
741const InnerError = error{691const InnerError = error{
...@@ -746,38 +696,33 @@ const InnerError = error{...@@ -746,38 +696,33 @@ const InnerError = error{
746 Overflow,696 Overflow,
747} || link.File.UpdateDebugInfoError;697} || link.File.UpdateDebugInfoError;
748698
749pub fn deinit(func: *CodeGen) void {699pub fn deinit(cg: *CodeGen) void {
750 // in case of an error and we still have branches700 const gpa = cg.gpa;
751 for (func.branches.items) |*branch| {701 for (cg.branches.items) |*branch| branch.deinit(gpa);
752 branch.deinit(func.gpa);702 cg.branches.deinit(gpa);
753 }703 cg.blocks.deinit(gpa);
754 func.branches.deinit(func.gpa);704 cg.loops.deinit(gpa);
755 func.blocks.deinit(func.gpa);705 cg.simd_immediates.deinit(gpa);
756 func.loops.deinit(func.gpa);706 cg.free_locals_i32.deinit(gpa);
757 func.locals.deinit(func.gpa);707 cg.free_locals_i64.deinit(gpa);
758 func.simd_immediates.deinit(func.gpa);708 cg.free_locals_f32.deinit(gpa);
759 func.mir_instructions.deinit(func.gpa);709 cg.free_locals_f64.deinit(gpa);
760 func.mir_extra.deinit(func.gpa);710 cg.free_locals_v128.deinit(gpa);
761 func.free_locals_i32.deinit(func.gpa);711 cg.* = undefined;
762 func.free_locals_i64.deinit(func.gpa);
763 func.free_locals_f32.deinit(func.gpa);
764 func.free_locals_f64.deinit(func.gpa);
765 func.free_locals_v128.deinit(func.gpa);
766 func.* = undefined;
767}712}
768713
769/// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig714fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
770fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError {715 const zcu = cg.pt.zcu;
771 func.err_msg = try Zcu.ErrorMsg.create(func.gpa, func.src_loc, fmt, args);716 const func = zcu.funcInfo(cg.func_index);
772 return error.CodegenFail;717 return zcu.codegenFail(func.owner_nav, fmt, args);
773}718}
774719
775/// Resolves the `WValue` for the given instruction `inst`720/// Resolves the `WValue` for the given instruction `inst`
776/// When the given instruction has a `Value`, it returns a constant instead721/// When the given instruction has a `Value`, it returns a constant instead
777fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {722fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
778 var branch_index = func.branches.items.len;723 var branch_index = cg.branches.items.len;
779 while (branch_index > 0) : (branch_index -= 1) {724 while (branch_index > 0) : (branch_index -= 1) {
780 const branch = func.branches.items[branch_index - 1];725 const branch = cg.branches.items[branch_index - 1];
781 if (branch.values.get(ref)) |value| {726 if (branch.values.get(ref)) |value| {
782 return value;727 return value;
783 }728 }
...@@ -787,16 +732,16 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {...@@ -787,16 +732,16 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
787 // means we must generate it from a constant.732 // means we must generate it from a constant.
788 // We always store constants in the most outer branch as they must never733 // We always store constants in the most outer branch as they must never
789 // be removed. The most outer branch is always at index 0.734 // be removed. The most outer branch is always at index 0.
790 const gop = try func.branches.items[0].values.getOrPut(func.gpa, ref);735 const gop = try cg.branches.items[0].values.getOrPut(cg.gpa, ref);
791 assert(!gop.found_existing);736 assert(!gop.found_existing);
792737
793 const pt = func.pt;738 const pt = cg.pt;
794 const zcu = pt.zcu;739 const zcu = pt.zcu;
795 const val = (try func.air.value(ref, pt)).?;740 const val = (try cg.air.value(ref, pt)).?;
796 const ty = func.typeOf(ref);741 const ty = cg.typeOf(ref);
797 if (!ty.hasRuntimeBitsIgnoreComptime(zcu) and !ty.isInt(zcu) and !ty.isError(zcu)) {742 if (!ty.hasRuntimeBitsIgnoreComptime(zcu) and !ty.isInt(zcu) and !ty.isError(zcu)) {
798 gop.value_ptr.* = .none;743 gop.value_ptr.* = .none;
799 return gop.value_ptr.*;744 return .none;
800 }745 }
801746
802 // When we need to pass the value by reference (such as a struct), we will747 // When we need to pass the value by reference (such as a struct), we will
...@@ -805,30 +750,24 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {...@@ -805,30 +750,24 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
805 //750 //
806 // In the other cases, we will simply lower the constant to a value that fits751 // In the other cases, we will simply lower the constant to a value that fits
807 // into a single local (such as a pointer, integer, bool, etc).752 // into a single local (such as a pointer, integer, bool, etc).
808 const result: WValue = if (isByRef(ty, pt, func.target.*))753 const result: WValue = if (isByRef(ty, zcu, cg.target))
809 switch (try func.bin_file.lowerUav(pt, val.toIntern(), .none, func.src_loc)) {754 .{ .uav_ref = .{ .ip_index = val.toIntern() } }
810 .mcv => |mcv| .{ .memory = mcv.load_symbol },
811 .fail => |err_msg| {
812 func.err_msg = err_msg;
813 return error.CodegenFail;
814 },
815 }
816 else755 else
817 try func.lowerConstant(val, ty);756 try cg.lowerConstant(val, ty);
818757
819 gop.value_ptr.* = result;758 gop.value_ptr.* = result;
820 return result;759 return result;
821}760}
822761
823/// NOTE: if result == .stack, it will be stored in .local762/// NOTE: if result == .stack, it will be stored in .local
824fn finishAir(func: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) InnerError!void {763fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) InnerError!void {
825 assert(operands.len <= Liveness.bpi - 1);764 assert(operands.len <= Liveness.bpi - 1);
826 var tomb_bits = func.liveness.getTombBits(inst);765 var tomb_bits = cg.liveness.getTombBits(inst);
827 for (operands) |operand| {766 for (operands) |operand| {
828 const dies = @as(u1, @truncate(tomb_bits)) != 0;767 const dies = @as(u1, @truncate(tomb_bits)) != 0;
829 tomb_bits >>= 1;768 tomb_bits >>= 1;
830 if (!dies) continue;769 if (!dies) continue;
831 processDeath(func, operand);770 processDeath(cg, operand);
832 }771 }
833772
834 // results of `none` can never be referenced.773 // results of `none` can never be referenced.
...@@ -836,13 +775,13 @@ fn finishAir(func: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []c...@@ -836,13 +775,13 @@ fn finishAir(func: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []c
836 const trackable_result = if (result != .stack)775 const trackable_result = if (result != .stack)
837 result776 result
838 else777 else
839 try result.toLocal(func, func.typeOfIndex(inst));778 try result.toLocal(cg, cg.typeOfIndex(inst));
840 const branch = func.currentBranch();779 const branch = cg.currentBranch();
841 branch.values.putAssumeCapacityNoClobber(inst.toRef(), trackable_result);780 branch.values.putAssumeCapacityNoClobber(inst.toRef(), trackable_result);
842 }781 }
843782
844 if (std.debug.runtime_safety) {783 if (std.debug.runtime_safety) {
845 func.air_bookkeeping += 1;784 cg.air_bookkeeping += 1;
846 }785 }
847}786}
848787
...@@ -855,8 +794,8 @@ const Branch = struct {...@@ -855,8 +794,8 @@ const Branch = struct {
855 }794 }
856};795};
857796
858inline fn currentBranch(func: *CodeGen) *Branch {797inline fn currentBranch(cg: *CodeGen) *Branch {
859 return &func.branches.items[func.branches.items.len - 1];798 return &cg.branches.items[cg.branches.items.len - 1];
860}799}
861800
862const BigTomb = struct {801const BigTomb = struct {
...@@ -883,131 +822,143 @@ const BigTomb = struct {...@@ -883,131 +822,143 @@ const BigTomb = struct {
883 }822 }
884};823};
885824
886fn iterateBigTomb(func: *CodeGen, inst: Air.Inst.Index, operand_count: usize) !BigTomb {825fn iterateBigTomb(cg: *CodeGen, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
887 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, operand_count + 1);826 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, operand_count + 1);
888 return BigTomb{827 return BigTomb{
889 .gen = func,828 .gen = cg,
890 .inst = inst,829 .inst = inst,
891 .lbt = func.liveness.iterateBigTomb(inst),830 .lbt = cg.liveness.iterateBigTomb(inst),
892 };831 };
893}832}
894833
895fn processDeath(func: *CodeGen, ref: Air.Inst.Ref) void {834fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void {
896 if (ref.toIndex() == null) return;835 if (ref.toIndex() == null) return;
897 // Branches are currently only allowed to free locals allocated836 // Branches are currently only allowed to free locals allocated
898 // within their own branch.837 // within their own branch.
899 // TODO: Upon branch consolidation free any locals if needed.838 // TODO: Upon branch consolidation free any locals if needed.
900 const value = func.currentBranch().values.getPtr(ref) orelse return;839 const value = cg.currentBranch().values.getPtr(ref) orelse return;
901 if (value.* != .local) return;840 if (value.* != .local) return;
902 const reserved_indexes = func.args.len + @intFromBool(func.return_value != .none);841 const reserved_indexes = cg.args.len + @intFromBool(cg.return_value != .none);
903 if (value.local.value < reserved_indexes) {842 if (value.local.value < reserved_indexes) {
904 return; // function arguments can never be re-used843 return; // function arguments can never be re-used
905 }844 }
906 log.debug("Decreasing reference for ref: %{d}, using local '{d}'", .{ @intFromEnum(ref.toIndex().?), value.local.value });845 log.debug("Decreasing reference for ref: %{d}, using local '{d}'", .{ @intFromEnum(ref.toIndex().?), value.local.value });
907 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer846 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer
908 if (value.local.references == 0) {847 if (value.local.references == 0) {
909 value.free(func);848 value.free(cg);
910 }849 }
911}850}
912851
913/// Appends a MIR instruction and returns its index within the list of instructions852fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {
914fn addInst(func: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {853 try cg.mir_instructions.append(cg.gpa, inst);
915 try func.mir_instructions.append(func.gpa, inst);854}
855
856fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
857 try cg.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
916}858}
917859
918fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {860fn addExtended(cg: *CodeGen, opcode: std.wasm.MiscOpcode) error{OutOfMemory}!void {
919 try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } });861 const extra_index = cg.extraLen();
862 try cg.mir_extra.append(cg.gpa, @intFromEnum(opcode));
863 try cg.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });
920}864}
921865
922fn addExtended(func: *CodeGen, opcode: wasm.MiscOpcode) error{OutOfMemory}!void {866fn addLabel(cg: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {
923 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));867 try cg.addInst(.{ .tag = tag, .data = .{ .label = label } });
924 try func.mir_extra.append(func.gpa, @intFromEnum(opcode));
925 try func.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });
926}868}
927869
928fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {870fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void {
929 try func.addInst(.{ .tag = tag, .data = .{ .label = label } });871 try cg.addInst(.{ .tag = tag, .data = .{ .local = local } });
872}
873
874fn addFuncTy(cg: *CodeGen, tag: Mir.Inst.Tag, i: Wasm.FunctionType.Index) error{OutOfMemory}!void {
875 try cg.addInst(.{ .tag = tag, .data = .{ .func_ty = i } });
930}876}
931877
932/// Accepts an unsigned 32bit integer rather than a signed integer to878/// Accepts an unsigned 32bit integer rather than a signed integer to
933/// prevent us from having to bitcast multiple times as most values879/// prevent us from having to bitcast multiple times as most values
934/// within codegen are represented as unsigned rather than signed.880/// within codegen are represented as unsigned rather than signed.
935fn addImm32(func: *CodeGen, imm: u32) error{OutOfMemory}!void {881fn addImm32(cg: *CodeGen, imm: u32) error{OutOfMemory}!void {
936 try func.addInst(.{ .tag = .i32_const, .data = .{ .imm32 = @bitCast(imm) } });882 try cg.addInst(.{ .tag = .i32_const, .data = .{ .imm32 = @bitCast(imm) } });
937}883}
938884
939/// Accepts an unsigned 64bit integer rather than a signed integer to885/// Accepts an unsigned 64bit integer rather than a signed integer to
940/// prevent us from having to bitcast multiple times as most values886/// prevent us from having to bitcast multiple times as most values
941/// within codegen are represented as unsigned rather than signed.887/// within codegen are represented as unsigned rather than signed.
942fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void {888fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void {
943 const extra_index = try func.addExtra(Mir.Imm64.fromU64(imm));889 const extra_index = try cg.addExtra(Mir.Imm64.init(imm));
944 try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });890 try cg.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });
945}891}
946892
947/// Accepts the index into the list of 128bit-immediates893/// Accepts the index into the list of 128bit-immediates
948fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void {894fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void {
949 const simd_values = func.simd_immediates.items[index];895 const simd_values = cg.simd_immediates.items[index];
950 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));896 const extra_index = cg.extraLen();
951 // tag + 128bit value897 // tag + 128bit value
952 try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);898 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 5);
953 func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));899 cg.mir_extra.appendAssumeCapacity(@intFromEnum(std.wasm.SimdOpcode.v128_const));
954 func.mir_extra.appendSliceAssumeCapacity(@alignCast(mem.bytesAsSlice(u32, &simd_values)));900 cg.mir_extra.appendSliceAssumeCapacity(@alignCast(mem.bytesAsSlice(u32, &simd_values)));
955 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });901 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
956}902}
957903
958fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {904fn addFloat64(cg: *CodeGen, float: f64) error{OutOfMemory}!void {
959 const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float));905 const extra_index = try cg.addExtra(Mir.Float64.init(float));
960 try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });906 try cg.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
961}907}
962908
963/// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`.909/// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`.
964fn addMemArg(func: *CodeGen, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void {910fn addMemArg(cg: *CodeGen, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
965 const extra_index = try func.addExtra(mem_arg);911 const extra_index = try cg.addExtra(mem_arg);
966 try func.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } });912 try cg.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } });
967}913}
968914
969/// Inserts an instruction from the 'atomics' feature which accesses wasm's linear memory dependent on the915/// Inserts an instruction from the 'atomics' feature which accesses wasm's linear memory dependent on the
970/// given `tag`.916/// given `tag`.
971fn addAtomicMemArg(func: *CodeGen, tag: wasm.AtomicsOpcode, mem_arg: Mir.MemArg) error{OutOfMemory}!void {917fn addAtomicMemArg(cg: *CodeGen, tag: std.wasm.AtomicsOpcode, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
972 const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = wasm.atomicsOpcode(tag) }));918 const extra_index = try cg.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) }));
973 _ = try func.addExtra(mem_arg);919 _ = try cg.addExtra(mem_arg);
974 try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });920 try cg.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });
975}921}
976922
977/// Helper function to emit atomic mir opcodes.923/// Helper function to emit atomic mir opcodes.
978fn addAtomicTag(func: *CodeGen, tag: wasm.AtomicsOpcode) error{OutOfMemory}!void {924fn addAtomicTag(cg: *CodeGen, tag: std.wasm.AtomicsOpcode) error{OutOfMemory}!void {
979 const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = wasm.atomicsOpcode(tag) }));925 const extra_index = try cg.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) }));
980 try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });926 try cg.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });
981}927}
982928
983/// Appends entries to `mir_extra` based on the type of `extra`.929/// Appends entries to `mir_extra` based on the type of `extra`.
984/// Returns the index into `mir_extra`930/// Returns the index into `mir_extra`
985fn addExtra(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {931fn addExtra(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
986 const fields = std.meta.fields(@TypeOf(extra));932 const fields = std.meta.fields(@TypeOf(extra));
987 try func.mir_extra.ensureUnusedCapacity(func.gpa, fields.len);933 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, fields.len);
988 return func.addExtraAssumeCapacity(extra);934 return cg.addExtraAssumeCapacity(extra);
989}935}
990936
991/// Appends entries to `mir_extra` based on the type of `extra`.937/// Appends entries to `mir_extra` based on the type of `extra`.
992/// Returns the index into `mir_extra`938/// Returns the index into `mir_extra`
993fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {939fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
994 const fields = std.meta.fields(@TypeOf(extra));940 const fields = std.meta.fields(@TypeOf(extra));
995 const result = @as(u32, @intCast(func.mir_extra.items.len));941 const result = cg.extraLen();
996 inline for (fields) |field| {942 inline for (fields) |field| {
997 func.mir_extra.appendAssumeCapacity(switch (field.type) {943 cg.mir_extra.appendAssumeCapacity(switch (field.type) {
998 u32 => @field(extra, field.name),944 u32 => @field(extra, field.name),
945 i32 => @bitCast(@field(extra, field.name)),
946 InternPool.Index,
947 InternPool.Nav.Index,
948 Wasm.UavsObjIndex,
949 Wasm.UavsExeIndex,
950 => @intFromEnum(@field(extra, field.name)),
999 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),951 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
1000 });952 });
1001 }953 }
1002 return result;954 return result;
1003}955}
1004956
1005/// Using a given `Type`, returns the corresponding valtype for .auto callconv957/// For `std.builtin.CallingConvention.auto`.
1006fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype {958pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.wasm.Valtype {
1007 const zcu = pt.zcu;
1008 const ip = &zcu.intern_pool;959 const ip = &zcu.intern_pool;
1009 return switch (ty.zigTypeTag(zcu)) {960 return switch (ty.zigTypeTag(zcu)) {
1010 .float => switch (ty.floatBits(target)) {961 .float => switch (ty.floatBits(target.*)) {
1011 16 => .i32, // stored/loaded as u16962 16 => .i32, // stored/loaded as u16
1012 32 => .f32,963 32 => .f32,
1013 64 => .f64,964 64 => .f64,
...@@ -1022,19 +973,20 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype {...@@ -1022,19 +973,20 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype {
1022 .@"struct" => blk: {973 .@"struct" => blk: {
1023 if (zcu.typeToPackedStruct(ty)) |packed_struct| {974 if (zcu.typeToPackedStruct(ty)) |packed_struct| {
1024 const backing_int_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip));975 const backing_int_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip));
1025 break :blk typeToValtype(backing_int_ty, pt, target);976 break :blk typeToValtype(backing_int_ty, zcu, target);
1026 } else {977 } else {
1027 break :blk .i32;978 break :blk .i32;
1028 }979 }
1029 },980 },
1030 .vector => switch (determineSimdStoreStrategy(ty, zcu, target)) {981 .vector => switch (CodeGen.determineSimdStoreStrategy(ty, zcu, target)) {
1031 .direct => .v128,982 .direct => .v128,
1032 .unrolled => .i32,983 .unrolled => .i32,
1033 },984 },
1034 .@"union" => switch (ty.containerLayout(zcu)) {985 .@"union" => switch (ty.containerLayout(zcu)) {
1035 .@"packed" => blk: {986 .@"packed" => switch (ty.bitSize(zcu)) {
1036 const int_ty = pt.intType(.unsigned, @as(u16, @intCast(ty.bitSize(zcu)))) catch @panic("out of memory");987 0...32 => .i32,
1037 break :blk typeToValtype(int_ty, pt, target);988 33...64 => .i64,
989 else => .i32,
1038 },990 },
1039 else => .i32,991 else => .i32,
1040 },992 },
...@@ -1042,42 +994,94 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype {...@@ -1042,42 +994,94 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype {
1042 };994 };
1043}995}
1044996
1045/// Using a given `Type`, returns the byte representation of its wasm value type
1046fn genValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 {
1047 return wasm.valtype(typeToValtype(ty, pt, target));
1048}
1049
1050/// Using a given `Type`, returns the corresponding wasm value type997/// Using a given `Type`, returns the corresponding wasm value type
1051/// Differently from `genValtype` this also allows `void` to create a block998/// Differently from `typeToValtype` this also allows `void` to create a block
1052/// with no return type999/// with no return type
1053fn genBlockType(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 {1000fn genBlockType(ty: Type, zcu: *const Zcu, target: *const std.Target) std.wasm.BlockType {
1054 return switch (ty.ip_index) {1001 return switch (ty.ip_index) {
1055 .void_type, .noreturn_type => wasm.block_empty,1002 .void_type, .noreturn_type => .empty,
1056 else => genValtype(ty, pt, target),1003 else => .fromValtype(typeToValtype(ty, zcu, target)),
1057 };1004 };
1058}1005}
10591006
1060/// Writes the bytecode depending on the given `WValue` in `val`1007/// Writes the bytecode depending on the given `WValue` in `val`
1061fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {1008fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
1062 switch (value) {1009 switch (value) {
1063 .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?)1010 .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?)
1064 .none, .stack => {}, // no-op1011 .none, .stack => {}, // no-op
1065 .local => |idx| try func.addLabel(.local_get, idx.value),1012 .local => |idx| try cg.addLocal(.local_get, idx.value),
1066 .imm32 => |val| try func.addImm32(val),1013 .imm32 => |val| try cg.addImm32(val),
1067 .imm64 => |val| try func.addImm64(val),1014 .imm64 => |val| try cg.addImm64(val),
1068 .imm128 => |val| try func.addImm128(val),1015 .imm128 => |val| try cg.addImm128(val),
1069 .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),1016 .float32 => |val| try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
1070 .float64 => |val| try func.addFloat64(val),1017 .float64 => |val| try cg.addFloat64(val),
1071 .memory => |ptr| {1018 .nav_ref => |nav_ref| {
1072 const extra_index = try func.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 });1019 const wasm = cg.wasm;
1073 try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });1020 const comp = wasm.base.comp;
1021 const zcu = comp.zcu.?;
1022 const ip = &zcu.intern_pool;
1023 if (ip.getNav(nav_ref.nav_index).isFn(ip)) {
1024 assert(nav_ref.offset == 0);
1025 const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, nav_ref.nav_index);
1026 if (!gop.found_existing) gop.value_ptr.* = {};
1027 try cg.addInst(.{
1028 .tag = .func_ref,
1029 .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) },
1030 });
1031 } else if (nav_ref.offset == 0) {
1032 try cg.addInst(.{ .tag = .nav_ref, .data = .{ .nav_index = nav_ref.nav_index } });
1033 } else {
1034 try cg.addInst(.{
1035 .tag = .nav_ref_off,
1036 .data = .{
1037 .payload = try cg.addExtra(Mir.NavRefOff{
1038 .nav_index = nav_ref.nav_index,
1039 .offset = nav_ref.offset,
1040 }),
1041 },
1042 });
1043 }
1074 },1044 },
1075 .memory_offset => |mem_off| {1045 .uav_ref => |uav| {
1076 const extra_index = try func.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset });1046 const wasm = cg.wasm;
1077 try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });1047 const comp = wasm.base.comp;
1048 const is_obj = comp.config.output_mode == .Obj;
1049 const zcu = comp.zcu.?;
1050 const ip = &zcu.intern_pool;
1051 if (ip.isFunctionType(ip.typeOf(uav.ip_index))) {
1052 assert(uav.offset == 0);
1053 const owner_nav = ip.toFunc(uav.ip_index).owner_nav;
1054 const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, owner_nav);
1055 if (!gop.found_existing) gop.value_ptr.* = {};
1056 try cg.addInst(.{
1057 .tag = .func_ref,
1058 .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) },
1059 });
1060 } else if (uav.offset == 0) {
1061 try cg.addInst(.{
1062 .tag = .uav_ref,
1063 .data = if (is_obj) .{
1064 .uav_obj = try wasm.refUavObj(uav.ip_index, uav.orig_ptr_ty),
1065 } else .{
1066 .uav_exe = try wasm.refUavExe(uav.ip_index, uav.orig_ptr_ty),
1067 },
1068 });
1069 } else {
1070 try cg.addInst(.{
1071 .tag = .uav_ref_off,
1072 .data = .{
1073 .payload = if (is_obj) try cg.addExtra(Mir.UavRefOffObj{
1074 .uav_obj = try wasm.refUavObj(uav.ip_index, uav.orig_ptr_ty),
1075 .offset = uav.offset,
1076 }) else try cg.addExtra(Mir.UavRefOffExe{
1077 .uav_exe = try wasm.refUavExe(uav.ip_index, uav.orig_ptr_ty),
1078 .offset = uav.offset,
1079 }),
1080 },
1081 });
1082 }
1078 },1083 },
1079 .function_index => |index| try func.addLabel(.function_index, index), // write function index and generate relocation1084 .stack_offset => try cg.addLocal(.local_get, cg.bottom_stack_value.local.value), // caller must ensure to address the offset
1080 .stack_offset => try func.addLabel(.local_get, func.bottom_stack_value.local.value), // caller must ensure to address the offset
1081 }1085 }
1082}1086}
10831087
...@@ -1085,7 +1089,7 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {...@@ -1085,7 +1089,7 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
1085/// The old `WValue` found at instruction `ref` is then replaced by the1089/// The old `WValue` found at instruction `ref` is then replaced by the
1086/// modified `WValue` and returned. When given a non-local or non-stack-offset,1090/// modified `WValue` and returned. When given a non-local or non-stack-offset,
1087/// returns the given `operand` itfunc instead.1091/// returns the given `operand` itfunc instead.
1088fn reuseOperand(func: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue {1092fn reuseOperand(cg: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue {
1089 if (operand != .local and operand != .stack_offset) return operand;1093 if (operand != .local and operand != .stack_offset) return operand;
1090 var new_value = operand;1094 var new_value = operand;
1091 switch (new_value) {1095 switch (new_value) {
...@@ -1093,17 +1097,17 @@ fn reuseOperand(func: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue {...@@ -1093,17 +1097,17 @@ fn reuseOperand(func: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue {
1093 .stack_offset => |*stack_offset| stack_offset.references += 1,1097 .stack_offset => |*stack_offset| stack_offset.references += 1,
1094 else => unreachable,1098 else => unreachable,
1095 }1099 }
1096 const old_value = func.getResolvedInst(ref);1100 const old_value = cg.getResolvedInst(ref);
1097 old_value.* = new_value;1101 old_value.* = new_value;
1098 return new_value;1102 return new_value;
1099}1103}
11001104
1101/// From a reference, returns its resolved `WValue`.1105/// From a reference, returns its resolved `WValue`.
1102/// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet.1106/// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet.
1103fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue {1107fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue {
1104 var index = func.branches.items.len;1108 var index = cg.branches.items.len;
1105 while (index > 0) : (index -= 1) {1109 while (index > 0) : (index -= 1) {
1106 const branch = func.branches.items[index - 1];1110 const branch = cg.branches.items[index - 1];
1107 if (branch.values.getPtr(ref)) |value| {1111 if (branch.values.getPtr(ref)) |value| {
1108 return value;1112 return value;
1109 }1113 }
...@@ -1113,243 +1117,238 @@ fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue {...@@ -1113,243 +1117,238 @@ fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue {
11131117
1114/// Creates one locals for a given `Type`.1118/// Creates one locals for a given `Type`.
1115/// Returns a corresponding `Wvalue` with `local` as active tag1119/// Returns a corresponding `Wvalue` with `local` as active tag
1116fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {1120fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
1117 const pt = func.pt;1121 const zcu = cg.pt.zcu;
1118 const valtype = typeToValtype(ty, pt, func.target.*);1122 const valtype = typeToValtype(ty, zcu, cg.target);
1119 const index_or_null = switch (valtype) {1123 const index_or_null = switch (valtype) {
1120 .i32 => func.free_locals_i32.popOrNull(),1124 .i32 => cg.free_locals_i32.popOrNull(),
1121 .i64 => func.free_locals_i64.popOrNull(),1125 .i64 => cg.free_locals_i64.popOrNull(),
1122 .f32 => func.free_locals_f32.popOrNull(),1126 .f32 => cg.free_locals_f32.popOrNull(),
1123 .f64 => func.free_locals_f64.popOrNull(),1127 .f64 => cg.free_locals_f64.popOrNull(),
1124 .v128 => func.free_locals_v128.popOrNull(),1128 .v128 => cg.free_locals_v128.popOrNull(),
1125 };1129 };
1126 if (index_or_null) |index| {1130 if (index_or_null) |index| {
1127 log.debug("reusing local ({d}) of type {}", .{ index, valtype });1131 log.debug("reusing local ({d}) of type {}", .{ index, valtype });
1128 return .{ .local = .{ .value = index, .references = 1 } };1132 return .{ .local = .{ .value = index, .references = 1 } };
1129 }1133 }
1130 log.debug("new local of type {}", .{valtype});1134 log.debug("new local of type {}", .{valtype});
1131 return func.ensureAllocLocal(ty);1135 return cg.ensureAllocLocal(ty);
1132}1136}
11331137
1134/// Ensures a new local will be created. This is useful when it's useful1138/// Ensures a new local will be created. This is useful when it's useful
1135/// to use a zero-initialized local.1139/// to use a zero-initialized local.
1136fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue {1140fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
1137 const pt = func.pt;1141 const zcu = cg.pt.zcu;
1138 try func.locals.append(func.gpa, genValtype(ty, pt, func.target.*));1142 try cg.locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target));
1139 const initial_index = func.local_index;1143 const initial_index = cg.local_index;
1140 func.local_index += 1;1144 cg.local_index += 1;
1141 return .{ .local = .{ .value = initial_index, .references = 1 } };1145 return .{ .local = .{ .value = initial_index, .references = 1 } };
1142}1146}
11431147
1144/// Generates a `wasm.Type` from a given function type.1148pub const Function = extern struct {
1145/// Memory is owned by the caller.1149 /// Index into `Wasm.mir_instructions`.
1146fn genFunctype(1150 mir_off: u32,
1147 gpa: Allocator,1151 /// This is unused except for as a safety slice bound and could be removed.
1148 cc: std.builtin.CallingConvention,1152 mir_len: u32,
1149 params: []const InternPool.Index,1153 /// Index into `Wasm.mir_extra`.
1150 return_type: Type,1154 mir_extra_off: u32,
1151 pt: Zcu.PerThread,1155 /// This is unused except for as a safety slice bound and could be removed.
1152 target: std.Target,1156 mir_extra_len: u32,
1153) !wasm.Type {1157 locals_off: u32,
1154 const zcu = pt.zcu;1158 locals_len: u32,
1155 var temp_params = std.ArrayList(wasm.Valtype).init(gpa);1159 prologue: Prologue,
1156 defer temp_params.deinit();1160
1157 var returns = std.ArrayList(wasm.Valtype).init(gpa);1161 pub const Prologue = extern struct {
1158 defer returns.deinit();1162 flags: Flags,
11591163 sp_local: u32,
1160 if (firstParamSRet(cc, return_type, pt, target)) {1164 stack_size: u32,
1161 try temp_params.append(.i32); // memory address is always a 32-bit handle1165 bottom_stack_local: u32,
1162 } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) {1166
1163 if (cc == .wasm_watc) {1167 pub const Flags = packed struct(u32) {
1164 const res_classes = abi.classifyType(return_type, zcu);1168 stack_alignment: Alignment,
1165 assert(res_classes[0] == .direct and res_classes[1] == .none);1169 padding: u26 = 0,
1166 const scalar_type = abi.scalarType(return_type, zcu);1170 };
1167 try returns.append(typeToValtype(scalar_type, pt, target));1171
1168 } else {1172 pub const none: Prologue = .{
1169 try returns.append(typeToValtype(return_type, pt, target));1173 .sp_local = 0,
1174 .flags = .{ .stack_alignment = .none },
1175 .stack_size = 0,
1176 .bottom_stack_local = 0,
1177 };
1178
1179 pub fn isNone(p: *const Prologue) bool {
1180 return p.flags.stack_alignment != .none;
1170 }1181 }
1171 } else if (return_type.isError(zcu)) {1182 };
1172 try returns.append(.i32);1183
1173 }1184 pub fn lower(f: *Function, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) Allocator.Error!void {
11741185 const gpa = wasm.base.comp.gpa;
1175 // param types1186
1176 for (params) |param_type_ip| {1187 // Write the locals in the prologue of the function body.
1177 const param_type = Type.fromInterned(param_type_ip);1188 const locals = wasm.all_zcu_locals.items[f.locals_off..][0..f.locals_len];
1178 if (!param_type.hasRuntimeBitsIgnoreComptime(zcu)) continue;1189 try code.ensureUnusedCapacity(gpa, 5 + locals.len * 6 + 38);
11791190
1180 switch (cc) {1191 std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(locals.len))) catch unreachable;
1181 .wasm_watc => {1192 for (locals) |local| {
1182 const param_classes = abi.classifyType(param_type, zcu);1193 std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable;
1183 if (param_classes[1] == .none) {1194 code.appendAssumeCapacity(@intFromEnum(local));
1184 if (param_classes[0] == .direct) {1195 }
1185 const scalar_type = abi.scalarType(param_type, zcu);1196
1186 try temp_params.append(typeToValtype(scalar_type, pt, target));1197 // Stack management section of function prologue.
1187 } else {1198 const stack_alignment = f.prologue.flags.stack_alignment;
1188 try temp_params.append(typeToValtype(param_type, pt, target));1199 if (stack_alignment.toByteUnits()) |align_bytes| {
1189 }1200 const sp_global: Wasm.GlobalIndex = .stack_pointer;
1190 } else {1201 // load stack pointer
1191 // i128/f1281202 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get));
1192 try temp_params.append(.i64);1203 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
1193 try temp_params.append(.i64);1204 // store stack pointer so we can restore it when we return from the function
1194 }1205 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));
1195 },1206 leb.writeUleb128(code.fixedWriter(), f.prologue.sp_local) catch unreachable;
1196 else => try temp_params.append(typeToValtype(param_type, pt, target)),1207 // get the total stack size
1208 const aligned_stack: i32 = @intCast(stack_alignment.forward(f.prologue.stack_size));
1209 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1210 leb.writeIleb128(code.fixedWriter(), aligned_stack) catch unreachable;
1211 // subtract it from the current stack pointer
1212 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_sub));
1213 // Get negative stack alignment
1214 const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1;
1215 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1216 leb.writeIleb128(code.fixedWriter(), neg_stack_align) catch unreachable;
1217 // Bitwise-and the value to get the new stack pointer to ensure the
1218 // pointers are aligned with the abi alignment.
1219 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_and));
1220 // The bottom will be used to calculate all stack pointer offsets.
1221 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));
1222 leb.writeUleb128(code.fixedWriter(), f.prologue.bottom_stack_local) catch unreachable;
1223 // Store the current stack pointer value into the global stack pointer so other function calls will
1224 // start from this value instead and not overwrite the current stack.
1225 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
1226 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
1197 }1227 }
1228
1229 var emit: Emit = .{
1230 .mir = .{
1231 .instruction_tags = wasm.mir_instructions.items(.tag)[f.mir_off..][0..f.mir_len],
1232 .instruction_datas = wasm.mir_instructions.items(.data)[f.mir_off..][0..f.mir_len],
1233 .extra = wasm.mir_extra.items[f.mir_extra_off..][0..f.mir_extra_len],
1234 },
1235 .wasm = wasm,
1236 .code = code,
1237 };
1238 try emit.lowerToCode();
1198 }1239 }
1240};
11991241
1200 return wasm.Type{1242pub const Error = error{
1201 .params = try temp_params.toOwnedSlice(),1243 OutOfMemory,
1202 .returns = try returns.toOwnedSlice(),1244 /// Compiler was asked to operate on a number larger than supported.
1203 };1245 Overflow,
1204}1246 /// Indicates the error is already stored in Zcu `failed_codegen`.
1247 CodegenFail,
1248};
12051249
1206pub fn generate(1250pub fn function(
1207 bin_file: *link.File,1251 wasm: *Wasm,
1208 pt: Zcu.PerThread,1252 pt: Zcu.PerThread,
1209 src_loc: Zcu.LazySrcLoc,
1210 func_index: InternPool.Index,1253 func_index: InternPool.Index,
1211 air: Air,1254 air: Air,
1212 liveness: Liveness,1255 liveness: Liveness,
1213 code: *std.ArrayList(u8),1256) Error!Function {
1214 debug_output: link.File.DebugInfoOutput,
1215) codegen.CodeGenError!codegen.Result {
1216 const zcu = pt.zcu;1257 const zcu = pt.zcu;
1217 const gpa = zcu.gpa;1258 const gpa = zcu.gpa;
1218 const func = zcu.funcInfo(func_index);1259 const cg = zcu.funcInfo(func_index);
1219 const file_scope = zcu.navFileScope(func.owner_nav);1260 const file_scope = zcu.navFileScope(cg.owner_nav);
1220 const target = &file_scope.mod.resolved_target.result;1261 const target = &file_scope.mod.resolved_target.result;
1262 const fn_ty = zcu.navValue(cg.owner_nav).typeOf(zcu);
1263 const fn_info = zcu.typeToFunc(fn_ty).?;
1264 const ip = &zcu.intern_pool;
1265 const fn_ty_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target);
1266 const returns = fn_ty_index.ptr(wasm).returns.slice(wasm);
1267 const any_returns = returns.len != 0;
1268
1269 var cc_result = try resolveCallingConventionValues(zcu, fn_ty, target);
1270 defer cc_result.deinit(gpa);
1271
1221 var code_gen: CodeGen = .{1272 var code_gen: CodeGen = .{
1222 .gpa = gpa,1273 .gpa = gpa,
1223 .pt = pt,1274 .pt = pt,
1224 .air = air,1275 .air = air,
1225 .liveness = liveness,1276 .liveness = liveness,
1226 .code = code,1277 .owner_nav = cg.owner_nav,
1227 .owner_nav = func.owner_nav,
1228 .src_loc = src_loc,
1229 .err_msg = undefined,
1230 .locals = .{},
1231 .target = target,1278 .target = target,
1232 .bin_file = bin_file.cast(.wasm).?,1279 .ptr_size = switch (target.cpu.arch) {
1233 .debug_output = debug_output,1280 .wasm32 => .wasm32,
1281 .wasm64 => .wasm64,
1282 else => unreachable,
1283 },
1284 .wasm = wasm,
1234 .func_index = func_index,1285 .func_index = func_index,
1286 .args = cc_result.args,
1287 .return_value = cc_result.return_value,
1288 .local_index = cc_result.local_index,
1289 .mir_instructions = &wasm.mir_instructions,
1290 .mir_extra = &wasm.mir_extra,
1291 .locals = &wasm.all_zcu_locals,
1292 .start_mir_extra_off = @intCast(wasm.mir_extra.items.len),
1293 .start_locals_off = @intCast(wasm.all_zcu_locals.items.len),
1235 };1294 };
1236 defer code_gen.deinit();1295 defer code_gen.deinit();
12371296
1238 genFunc(&code_gen) catch |err| switch (err) {1297 return functionInner(&code_gen, any_returns) catch |err| switch (err) {
1239 error.CodegenFail => return codegen.Result{ .fail = code_gen.err_msg },1298 error.CodegenFail => return error.CodegenFail,
1240 else => |e| return e,1299 else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}),
1241 };1300 };
1242
1243 return codegen.Result.ok;
1244}1301}
12451302
1246fn genFunc(func: *CodeGen) InnerError!void {1303fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function {
1247 const pt = func.pt;1304 const wasm = cg.wasm;
1248 const zcu = pt.zcu;1305 const zcu = cg.pt.zcu;
1249 const ip = &zcu.intern_pool;
1250 const fn_ty = zcu.navValue(func.owner_nav).typeOf(zcu);
1251 const fn_info = zcu.typeToFunc(fn_ty).?;
1252 var func_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target.*);
1253 defer func_type.deinit(func.gpa);
1254 _ = try func.bin_file.storeNavType(func.owner_nav, func_type);
1255
1256 var cc_result = try func.resolveCallingConventionValues(fn_ty);
1257 defer cc_result.deinit(func.gpa);
1258
1259 func.args = cc_result.args;
1260 func.return_value = cc_result.return_value;
12611306
1262 try func.addTag(.dbg_prologue_end);1307 const start_mir_off: u32 = @intCast(wasm.mir_instructions.len);
12631308
1264 try func.branches.append(func.gpa, .{});1309 try cg.branches.append(cg.gpa, .{});
1265 // clean up outer branch1310 // clean up outer branch
1266 defer {1311 defer {
1267 var outer_branch = func.branches.pop();1312 var outer_branch = cg.branches.pop();
1268 outer_branch.deinit(func.gpa);1313 outer_branch.deinit(cg.gpa);
1269 assert(func.branches.items.len == 0); // missing branch merge1314 assert(cg.branches.items.len == 0); // missing branch merge
1270 }1315 }
1271 // Generate MIR for function body1316 // Generate MIR for function body
1272 try func.genBody(func.air.getMainBody());1317 try cg.genBody(cg.air.getMainBody());
12731318
1274 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)1319 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)
1275 // we emit an unreachable instruction to tell the stack validator that part will never be reached.1320 // we emit an unreachable instruction to tell the stack validator that part will never be reached.
1276 if (func_type.returns.len != 0 and func.air.instructions.len > 0) {1321 if (any_returns and cg.air.instructions.len > 0) {
1277 const inst: Air.Inst.Index = @enumFromInt(func.air.instructions.len - 1);1322 const inst: Air.Inst.Index = @enumFromInt(cg.air.instructions.len - 1);
1278 const last_inst_ty = func.typeOfIndex(inst);1323 const last_inst_ty = cg.typeOfIndex(inst);
1279 if (!last_inst_ty.hasRuntimeBitsIgnoreComptime(zcu) or last_inst_ty.isNoReturn(zcu)) {1324 if (!last_inst_ty.hasRuntimeBitsIgnoreComptime(zcu) or last_inst_ty.isNoReturn(zcu)) {
1280 try func.addTag(.@"unreachable");1325 try cg.addTag(.@"unreachable");
1281 }1326 }
1282 }1327 }
1283 // End of function body1328 // End of function body
1284 try func.addTag(.end);1329 try cg.addTag(.end);
12851330 try cg.addTag(.dbg_epilogue_begin);
1286 try func.addTag(.dbg_epilogue_begin);1331
12871332 return .{
1288 // check if we have to initialize and allocate anything into the stack frame.1333 .mir_off = start_mir_off,
1289 // If so, create enough stack space and insert the instructions at the front of the list.1334 .mir_len = @intCast(wasm.mir_instructions.len - start_mir_off),
1290 if (func.initial_stack_value != .none) {1335 .mir_extra_off = cg.start_mir_extra_off,
1291 var prologue = std.ArrayList(Mir.Inst).init(func.gpa);1336 .mir_extra_len = cg.extraLen(),
1292 defer prologue.deinit();1337 .locals_off = cg.start_locals_off,
12931338 .locals_len = @intCast(wasm.all_zcu_locals.items.len - cg.start_locals_off),
1294 const sp = @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym);1339 .prologue = if (cg.initial_stack_value == .none) .none else .{
1295 // load stack pointer1340 .sp_local = cg.initial_stack_value.local.value,
1296 try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } });1341 .flags = .{ .stack_alignment = cg.stack_alignment },
1297 // store stack pointer so we can restore it when we return from the function1342 .stack_size = cg.stack_size,
1298 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_stack_value.local.value } });1343 .bottom_stack_local = cg.bottom_stack_value.local.value,
1299 // get the total stack size
1300 const aligned_stack = func.stack_alignment.forward(func.stack_size);
1301 try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(aligned_stack) } });
1302 // subtract it from the current stack pointer
1303 try prologue.append(.{ .tag = .i32_sub, .data = .{ .tag = {} } });
1304 // Get negative stack alignment
1305 try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @as(i32, @intCast(func.stack_alignment.toByteUnits().?)) * -1 } });
1306 // Bitwise-and the value to get the new stack pointer to ensure the pointers are aligned with the abi alignment
1307 try prologue.append(.{ .tag = .i32_and, .data = .{ .tag = {} } });
1308 // store the current stack pointer as the bottom, which will be used to calculate all stack pointer offsets
1309 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.bottom_stack_value.local.value } });
1310 // Store the current stack pointer value into the global stack pointer so other function calls will
1311 // start from this value instead and not overwrite the current stack.
1312 try prologue.append(.{ .tag = .global_set, .data = .{ .label = sp } });
1313
1314 // reserve space and insert all prologue instructions at the front of the instruction list
1315 // We insert them in reserve order as there is no insertSlice in multiArrayList.
1316 try func.mir_instructions.ensureUnusedCapacity(func.gpa, prologue.items.len);
1317 for (prologue.items, 0..) |_, index| {
1318 const inst = prologue.items[prologue.items.len - 1 - index];
1319 func.mir_instructions.insertAssumeCapacity(0, inst);
1320 }
1321 }
1322
1323 var mir: Mir = .{
1324 .instructions = func.mir_instructions.toOwnedSlice(),
1325 .extra = try func.mir_extra.toOwnedSlice(func.gpa),
1326 };
1327 defer mir.deinit(func.gpa);
1328
1329 var emit: Emit = .{
1330 .mir = mir,
1331 .bin_file = func.bin_file,
1332 .code = func.code,
1333 .locals = func.locals.items,
1334 .owner_nav = func.owner_nav,
1335 .dbg_output = func.debug_output,
1336 .prev_di_line = 0,
1337 .prev_di_column = 0,
1338 .prev_di_offset = 0,
1339 };
1340
1341 emit.emitMir() catch |err| switch (err) {
1342 error.EmitFail => {
1343 func.err_msg = emit.error_msg.?;
1344 return error.CodegenFail;
1345 },1344 },
1346 else => |e| return e,
1347 };1345 };
1348}1346}
13491347
1350const CallWValues = struct {1348const CallWValues = struct {
1351 args: []WValue,1349 args: []WValue,
1352 return_value: WValue,1350 return_value: WValue,
1351 local_index: u32,
13531352
1354 fn deinit(values: *CallWValues, gpa: Allocator) void {1353 fn deinit(values: *CallWValues, gpa: Allocator) void {
1355 gpa.free(values.args);1354 gpa.free(values.args);
...@@ -1357,28 +1356,33 @@ const CallWValues = struct {...@@ -1357,28 +1356,33 @@ const CallWValues = struct {
1357 }1356 }
1358};1357};
13591358
1360fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWValues {1359fn resolveCallingConventionValues(
1361 const pt = func.pt;1360 zcu: *const Zcu,
1362 const zcu = pt.zcu;1361 fn_ty: Type,
1362 target: *const std.Target,
1363) Allocator.Error!CallWValues {
1364 const gpa = zcu.gpa;
1363 const ip = &zcu.intern_pool;1365 const ip = &zcu.intern_pool;
1364 const fn_info = zcu.typeToFunc(fn_ty).?;1366 const fn_info = zcu.typeToFunc(fn_ty).?;
1365 const cc = fn_info.cc;1367 const cc = fn_info.cc;
1368
1366 var result: CallWValues = .{1369 var result: CallWValues = .{
1367 .args = &.{},1370 .args = &.{},
1368 .return_value = .none,1371 .return_value = .none,
1372 .local_index = 0,
1369 };1373 };
1370 if (cc == .naked) return result;1374 if (cc == .naked) return result;
13711375
1372 var args = std.ArrayList(WValue).init(func.gpa);1376 var args = std.ArrayList(WValue).init(gpa);
1373 defer args.deinit();1377 defer args.deinit();
13741378
1375 // Check if we store the result as a pointer to the stack rather than1379 // Check if we store the result as a pointer to the stack rather than
1376 // by value1380 // by value
1377 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) {1381 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, target)) {
1378 // the sret arg will be passed as first argument, therefore we1382 // the sret arg will be passed as first argument, therefore we
1379 // set the `return_value` before allocating locals for regular args.1383 // set the `return_value` before allocating locals for regular args.
1380 result.return_value = .{ .local = .{ .value = func.local_index, .references = 1 } };1384 result.return_value = .{ .local = .{ .value = result.local_index, .references = 1 } };
1381 func.local_index += 1;1385 result.local_index += 1;
1382 }1386 }
13831387
1384 switch (cc) {1388 switch (cc) {
...@@ -1388,8 +1392,8 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV...@@ -1388,8 +1392,8 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV
1388 continue;1392 continue;
1389 }1393 }
13901394
1391 try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } });1395 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1392 func.local_index += 1;1396 result.local_index += 1;
1393 }1397 }
1394 },1398 },
1395 .wasm_watc => {1399 .wasm_watc => {
...@@ -1397,23 +1401,28 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV...@@ -1397,23 +1401,28 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV
1397 const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu);1401 const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu);
1398 for (ty_classes) |class| {1402 for (ty_classes) |class| {
1399 if (class == .none) continue;1403 if (class == .none) continue;
1400 try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } });1404 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1401 func.local_index += 1;1405 result.local_index += 1;
1402 }1406 }
1403 }1407 }
1404 },1408 },
1405 else => return func.fail("calling convention '{s}' not supported for Wasm", .{@tagName(cc)}),1409 else => unreachable, // Frontend is responsible for emitting an error earlier.
1406 }1410 }
1407 result.args = try args.toOwnedSlice();1411 result.args = try args.toOwnedSlice();
1408 return result;1412 return result;
1409}1413}
14101414
1411fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu.PerThread, target: std.Target) bool {1415pub fn firstParamSRet(
1416 cc: std.builtin.CallingConvention,
1417 return_type: Type,
1418 zcu: *const Zcu,
1419 target: *const std.Target,
1420) bool {
1412 switch (cc) {1421 switch (cc) {
1413 .@"inline" => unreachable,1422 .@"inline" => unreachable,
1414 .auto => return isByRef(return_type, pt, target),1423 .auto => return isByRef(return_type, zcu, target),
1415 .wasm_watc => {1424 .wasm_watc => {
1416 const ty_classes = abi.classifyType(return_type, pt.zcu);1425 const ty_classes = abi.classifyType(return_type, zcu);
1417 if (ty_classes[0] == .indirect) return true;1426 if (ty_classes[0] == .indirect) return true;
1418 if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true;1427 if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true;
1419 return false;1428 return false;
...@@ -1424,94 +1433,88 @@ fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu....@@ -1424,94 +1433,88 @@ fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu.
14241433
1425/// Lowers a Zig type and its value based on a given calling convention to ensure1434/// Lowers a Zig type and its value based on a given calling convention to ensure
1426/// it matches the ABI.1435/// it matches the ABI.
1427fn lowerArg(func: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WValue) !void {1436fn lowerArg(cg: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WValue) !void {
1428 if (cc != .wasm_watc) {1437 if (cc != .wasm_watc) {
1429 return func.lowerToStack(value);1438 return cg.lowerToStack(value);
1430 }1439 }
14311440
1432 const pt = func.pt;1441 const zcu = cg.pt.zcu;
1433 const zcu = pt.zcu;
1434 const ty_classes = abi.classifyType(ty, zcu);1442 const ty_classes = abi.classifyType(ty, zcu);
1435 assert(ty_classes[0] != .none);1443 assert(ty_classes[0] != .none);
1436 switch (ty.zigTypeTag(zcu)) {1444 switch (ty.zigTypeTag(zcu)) {
1437 .@"struct", .@"union" => {1445 .@"struct", .@"union" => {
1438 if (ty_classes[0] == .indirect) {1446 if (ty_classes[0] == .indirect) {
1439 return func.lowerToStack(value);1447 return cg.lowerToStack(value);
1440 }1448 }
1441 assert(ty_classes[0] == .direct);1449 assert(ty_classes[0] == .direct);
1442 const scalar_type = abi.scalarType(ty, zcu);1450 const scalar_type = abi.scalarType(ty, zcu);
1443 switch (value) {1451 switch (value) {
1444 .memory,1452 .nav_ref, .stack_offset => _ = try cg.load(value, scalar_type, 0),
1445 .memory_offset,
1446 .stack_offset,
1447 => _ = try func.load(value, scalar_type, 0),
1448 .dead => unreachable,1453 .dead => unreachable,
1449 else => try func.emitWValue(value),1454 else => try cg.emitWValue(value),
1450 }1455 }
1451 },1456 },
1452 .int, .float => {1457 .int, .float => {
1453 if (ty_classes[1] == .none) {1458 if (ty_classes[1] == .none) {
1454 return func.lowerToStack(value);1459 return cg.lowerToStack(value);
1455 }1460 }
1456 assert(ty_classes[0] == .direct and ty_classes[1] == .direct);1461 assert(ty_classes[0] == .direct and ty_classes[1] == .direct);
1457 assert(ty.abiSize(zcu) == 16);1462 assert(ty.abiSize(zcu) == 16);
1458 // in this case we have an integer or float that must be lowered as 2 i64's.1463 // in this case we have an integer or float that must be lowered as 2 i64's.
1459 try func.emitWValue(value);1464 try cg.emitWValue(value);
1460 try func.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 });1465 try cg.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 });
1461 try func.emitWValue(value);1466 try cg.emitWValue(value);
1462 try func.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 });1467 try cg.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 });
1463 },1468 },
1464 else => return func.lowerToStack(value),1469 else => return cg.lowerToStack(value),
1465 }1470 }
1466}1471}
14671472
1468/// Lowers a `WValue` to the stack. This means when the `value` results in1473/// Lowers a `WValue` to the stack. This means when the `value` results in
1469/// `.stack_offset` we calculate the pointer of this offset and use that.1474/// `.stack_offset` we calculate the pointer of this offset and use that.
1470/// The value is left on the stack, and not stored in any temporary.1475/// The value is left on the stack, and not stored in any temporary.
1471fn lowerToStack(func: *CodeGen, value: WValue) !void {1476fn lowerToStack(cg: *CodeGen, value: WValue) !void {
1472 switch (value) {1477 switch (value) {
1473 .stack_offset => |offset| {1478 .stack_offset => |offset| {
1474 try func.emitWValue(value);1479 try cg.emitWValue(value);
1475 if (offset.value > 0) {1480 if (offset.value > 0) {
1476 switch (func.arch()) {1481 switch (cg.ptr_size) {
1477 .wasm32 => {1482 .wasm32 => {
1478 try func.addImm32(offset.value);1483 try cg.addImm32(offset.value);
1479 try func.addTag(.i32_add);1484 try cg.addTag(.i32_add);
1480 },1485 },
1481 .wasm64 => {1486 .wasm64 => {
1482 try func.addImm64(offset.value);1487 try cg.addImm64(offset.value);
1483 try func.addTag(.i64_add);1488 try cg.addTag(.i64_add);
1484 },1489 },
1485 else => unreachable,
1486 }1490 }
1487 }1491 }
1488 },1492 },
1489 else => try func.emitWValue(value),1493 else => try cg.emitWValue(value),
1490 }1494 }
1491}1495}
14921496
1493/// Creates a local for the initial stack value1497/// Creates a local for the initial stack value
1494/// Asserts `initial_stack_value` is `.none`1498/// Asserts `initial_stack_value` is `.none`
1495fn initializeStack(func: *CodeGen) !void {1499fn initializeStack(cg: *CodeGen) !void {
1496 assert(func.initial_stack_value == .none);1500 assert(cg.initial_stack_value == .none);
1497 // Reserve a local to store the current stack pointer1501 // Reserve a local to store the current stack pointer
1498 // We can later use this local to set the stack pointer back to the value1502 // We can later use this local to set the stack pointer back to the value
1499 // we have stored here.1503 // we have stored here.
1500 func.initial_stack_value = try func.ensureAllocLocal(Type.usize);1504 cg.initial_stack_value = try cg.ensureAllocLocal(Type.usize);
1501 // Also reserve a local to store the bottom stack value1505 // Also reserve a local to store the bottom stack value
1502 func.bottom_stack_value = try func.ensureAllocLocal(Type.usize);1506 cg.bottom_stack_value = try cg.ensureAllocLocal(Type.usize);
1503}1507}
15041508
1505/// Reads the stack pointer from `Context.initial_stack_value` and writes it1509/// Reads the stack pointer from `Context.initial_stack_value` and writes it
1506/// to the global stack pointer variable1510/// to the global stack pointer variable
1507fn restoreStackPointer(func: *CodeGen) !void {1511fn restoreStackPointer(cg: *CodeGen) !void {
1508 // only restore the pointer if it was initialized1512 // only restore the pointer if it was initialized
1509 if (func.initial_stack_value == .none) return;1513 if (cg.initial_stack_value == .none) return;
1510 // Get the original stack pointer's value1514 // Get the original stack pointer's value
1511 try func.emitWValue(func.initial_stack_value);1515 try cg.emitWValue(cg.initial_stack_value);
15121516
1513 // save its value in the global stack pointer1517 try cg.addTag(.global_set_sp);
1514 try func.addLabel(.global_set, @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym));
1515}1518}
15161519
1517/// From a given type, will create space on the virtual stack to store the value of such type.1520/// From a given type, will create space on the virtual stack to store the value of such type.
...@@ -1520,24 +1523,25 @@ fn restoreStackPointer(func: *CodeGen) !void {...@@ -1520,24 +1523,25 @@ fn restoreStackPointer(func: *CodeGen) !void {
1520/// moveStack unless a local was already created to store the pointer.1523/// moveStack unless a local was already created to store the pointer.
1521///1524///
1522/// Asserts Type has codegenbits1525/// Asserts Type has codegenbits
1523fn allocStack(func: *CodeGen, ty: Type) !WValue {1526fn allocStack(cg: *CodeGen, ty: Type) !WValue {
1524 const zcu = func.pt.zcu;1527 const pt = cg.pt;
1528 const zcu = pt.zcu;
1525 assert(ty.hasRuntimeBitsIgnoreComptime(zcu));1529 assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
1526 if (func.initial_stack_value == .none) {1530 if (cg.initial_stack_value == .none) {
1527 try func.initializeStack();1531 try cg.initializeStack();
1528 }1532 }
15291533
1530 const abi_size = std.math.cast(u32, ty.abiSize(zcu)) orelse {1534 const abi_size = std.math.cast(u32, ty.abiSize(zcu)) orelse {
1531 return func.fail("Type {} with ABI size of {d} exceeds stack frame size", .{1535 return cg.fail("Type {} with ABI size of {d} exceeds stack frame size", .{
1532 ty.fmt(func.pt), ty.abiSize(zcu),1536 ty.fmt(pt), ty.abiSize(zcu),
1533 });1537 });
1534 };1538 };
1535 const abi_align = ty.abiAlignment(zcu);1539 const abi_align = ty.abiAlignment(zcu);
15361540
1537 func.stack_alignment = func.stack_alignment.max(abi_align);1541 cg.stack_alignment = cg.stack_alignment.max(abi_align);
15381542
1539 const offset: u32 = @intCast(abi_align.forward(func.stack_size));1543 const offset: u32 = @intCast(abi_align.forward(cg.stack_size));
1540 defer func.stack_size = offset + abi_size;1544 defer cg.stack_size = offset + abi_size;
15411545
1542 return .{ .stack_offset = .{ .value = offset, .references = 1 } };1546 return .{ .stack_offset = .{ .value = offset, .references = 1 } };
1543}1547}
...@@ -1546,30 +1550,30 @@ fn allocStack(func: *CodeGen, ty: Type) !WValue {...@@ -1546,30 +1550,30 @@ fn allocStack(func: *CodeGen, ty: Type) !WValue {
1546/// the value of its type will live.1550/// the value of its type will live.
1547/// This is different from allocStack where this will use the pointer's alignment1551/// This is different from allocStack where this will use the pointer's alignment
1548/// if it is set, to ensure the stack alignment will be set correctly.1552/// if it is set, to ensure the stack alignment will be set correctly.
1549fn allocStackPtr(func: *CodeGen, inst: Air.Inst.Index) !WValue {1553fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue {
1550 const pt = func.pt;1554 const pt = cg.pt;
1551 const zcu = pt.zcu;1555 const zcu = pt.zcu;
1552 const ptr_ty = func.typeOfIndex(inst);1556 const ptr_ty = cg.typeOfIndex(inst);
1553 const pointee_ty = ptr_ty.childType(zcu);1557 const pointee_ty = ptr_ty.childType(zcu);
15541558
1555 if (func.initial_stack_value == .none) {1559 if (cg.initial_stack_value == .none) {
1556 try func.initializeStack();1560 try cg.initializeStack();
1557 }1561 }
15581562
1559 if (!pointee_ty.hasRuntimeBitsIgnoreComptime(zcu)) {1563 if (!pointee_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
1560 return func.allocStack(Type.usize); // create a value containing just the stack pointer.1564 return cg.allocStack(Type.usize); // create a value containing just the stack pointer.
1561 }1565 }
15621566
1563 const abi_alignment = ptr_ty.ptrAlignment(zcu);1567 const abi_alignment = ptr_ty.ptrAlignment(zcu);
1564 const abi_size = std.math.cast(u32, pointee_ty.abiSize(zcu)) orelse {1568 const abi_size = std.math.cast(u32, pointee_ty.abiSize(zcu)) orelse {
1565 return func.fail("Type {} with ABI size of {d} exceeds stack frame size", .{1569 return cg.fail("Type {} with ABI size of {d} exceeds stack frame size", .{
1566 pointee_ty.fmt(pt), pointee_ty.abiSize(zcu),1570 pointee_ty.fmt(pt), pointee_ty.abiSize(zcu),
1567 });1571 });
1568 };1572 };
1569 func.stack_alignment = func.stack_alignment.max(abi_alignment);1573 cg.stack_alignment = cg.stack_alignment.max(abi_alignment);
15701574
1571 const offset: u32 = @intCast(abi_alignment.forward(func.stack_size));1575 const offset: u32 = @intCast(abi_alignment.forward(cg.stack_size));
1572 defer func.stack_size = offset + abi_size;1576 defer cg.stack_size = offset + abi_size;
15731577
1574 return .{ .stack_offset = .{ .value = offset, .references = 1 } };1578 return .{ .stack_offset = .{ .value = offset, .references = 1 } };
1575}1579}
...@@ -1583,14 +1587,14 @@ fn toWasmBits(bits: u16) ?u16 {...@@ -1583,14 +1587,14 @@ fn toWasmBits(bits: u16) ?u16 {
15831587
1584/// Performs a copy of bytes for a given type. Copying all bytes1588/// Performs a copy of bytes for a given type. Copying all bytes
1585/// from rhs to lhs.1589/// from rhs to lhs.
1586fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {1590fn memcpy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
1587 // When bulk_memory is enabled, we lower it to wasm's memcpy instruction.1591 // When bulk_memory is enabled, we lower it to wasm's memcpy instruction.
1588 // If not, we lower it ourselves manually1592 // If not, we lower it ourselves manually
1589 if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory)) {1593 if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory)) {
1590 try func.lowerToStack(dst);1594 try cg.lowerToStack(dst);
1591 try func.lowerToStack(src);1595 try cg.lowerToStack(src);
1592 try func.emitWValue(len);1596 try cg.emitWValue(len);
1593 try func.addExtended(.memory_copy);1597 try cg.addExtended(.memory_copy);
1594 return;1598 return;
1595 }1599 }
15961600
...@@ -1611,19 +1615,18 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {...@@ -1611,19 +1615,18 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
1611 const rhs_base = src.offset();1615 const rhs_base = src.offset();
1612 while (offset < length) : (offset += 1) {1616 while (offset < length) : (offset += 1) {
1613 // get dst's address to store the result1617 // get dst's address to store the result
1614 try func.emitWValue(dst);1618 try cg.emitWValue(dst);
1615 // load byte from src's address1619 // load byte from src's address
1616 try func.emitWValue(src);1620 try cg.emitWValue(src);
1617 switch (func.arch()) {1621 switch (cg.ptr_size) {
1618 .wasm32 => {1622 .wasm32 => {
1619 try func.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });1623 try cg.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });
1620 try func.addMemArg(.i32_store8, .{ .offset = lhs_base + offset, .alignment = 1 });1624 try cg.addMemArg(.i32_store8, .{ .offset = lhs_base + offset, .alignment = 1 });
1621 },1625 },
1622 .wasm64 => {1626 .wasm64 => {
1623 try func.addMemArg(.i64_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });1627 try cg.addMemArg(.i64_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });
1624 try func.addMemArg(.i64_store8, .{ .offset = lhs_base + offset, .alignment = 1 });1628 try cg.addMemArg(.i64_store8, .{ .offset = lhs_base + offset, .alignment = 1 });
1625 },1629 },
1626 else => unreachable,
1627 }1630 }
1628 }1631 }
1629 return;1632 return;
...@@ -1633,94 +1636,84 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {...@@ -1633,94 +1636,84 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
16331636
1634 // allocate a local for the offset, and set it to 0.1637 // allocate a local for the offset, and set it to 0.
1635 // This to ensure that inside loops we correctly re-set the counter.1638 // This to ensure that inside loops we correctly re-set the counter.
1636 var offset = try func.allocLocal(Type.usize); // local for counter1639 var offset = try cg.allocLocal(Type.usize); // local for counter
1637 defer offset.free(func);1640 defer offset.free(cg);
1638 switch (func.arch()) {1641 switch (cg.ptr_size) {
1639 .wasm32 => try func.addImm32(0),1642 .wasm32 => try cg.addImm32(0),
1640 .wasm64 => try func.addImm64(0),1643 .wasm64 => try cg.addImm64(0),
1641 else => unreachable,
1642 }1644 }
1643 try func.addLabel(.local_set, offset.local.value);1645 try cg.addLocal(.local_set, offset.local.value);
16441646
1645 // outer block to jump to when loop is done1647 // outer block to jump to when loop is done
1646 try func.startBlock(.block, wasm.block_empty);1648 try cg.startBlock(.block, .empty);
1647 try func.startBlock(.loop, wasm.block_empty);1649 try cg.startBlock(.loop, .empty);
16481650
1649 // loop condition (offset == length -> break)1651 // loop condition (offset == length -> break)
1650 {1652 {
1651 try func.emitWValue(offset);1653 try cg.emitWValue(offset);
1652 try func.emitWValue(len);1654 try cg.emitWValue(len);
1653 switch (func.arch()) {1655 switch (cg.ptr_size) {
1654 .wasm32 => try func.addTag(.i32_eq),1656 .wasm32 => try cg.addTag(.i32_eq),
1655 .wasm64 => try func.addTag(.i64_eq),1657 .wasm64 => try cg.addTag(.i64_eq),
1656 else => unreachable,
1657 }1658 }
1658 try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished)1659 try cg.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
1659 }1660 }
16601661
1661 // get dst ptr1662 // get dst ptr
1662 {1663 {
1663 try func.emitWValue(dst);1664 try cg.emitWValue(dst);
1664 try func.emitWValue(offset);1665 try cg.emitWValue(offset);
1665 switch (func.arch()) {1666 switch (cg.ptr_size) {
1666 .wasm32 => try func.addTag(.i32_add),1667 .wasm32 => try cg.addTag(.i32_add),
1667 .wasm64 => try func.addTag(.i64_add),1668 .wasm64 => try cg.addTag(.i64_add),
1668 else => unreachable,
1669 }1669 }
1670 }1670 }
16711671
1672 // get src value and also store in dst1672 // get src value and also store in dst
1673 {1673 {
1674 try func.emitWValue(src);1674 try cg.emitWValue(src);
1675 try func.emitWValue(offset);1675 try cg.emitWValue(offset);
1676 switch (func.arch()) {1676 switch (cg.ptr_size) {
1677 .wasm32 => {1677 .wasm32 => {
1678 try func.addTag(.i32_add);1678 try cg.addTag(.i32_add);
1679 try func.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 });1679 try cg.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1680 try func.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 });1680 try cg.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 });
1681 },1681 },
1682 .wasm64 => {1682 .wasm64 => {
1683 try func.addTag(.i64_add);1683 try cg.addTag(.i64_add);
1684 try func.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 });1684 try cg.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1685 try func.addMemArg(.i64_store8, .{ .offset = dst.offset(), .alignment = 1 });1685 try cg.addMemArg(.i64_store8, .{ .offset = dst.offset(), .alignment = 1 });
1686 },1686 },
1687 else => unreachable,
1688 }1687 }
1689 }1688 }
16901689
1691 // increment loop counter1690 // increment loop counter
1692 {1691 {
1693 try func.emitWValue(offset);1692 try cg.emitWValue(offset);
1694 switch (func.arch()) {1693 switch (cg.ptr_size) {
1695 .wasm32 => {1694 .wasm32 => {
1696 try func.addImm32(1);1695 try cg.addImm32(1);
1697 try func.addTag(.i32_add);1696 try cg.addTag(.i32_add);
1698 },1697 },
1699 .wasm64 => {1698 .wasm64 => {
1700 try func.addImm64(1);1699 try cg.addImm64(1);
1701 try func.addTag(.i64_add);1700 try cg.addTag(.i64_add);
1702 },1701 },
1703 else => unreachable,
1704 }1702 }
1705 try func.addLabel(.local_set, offset.local.value);1703 try cg.addLocal(.local_set, offset.local.value);
1706 try func.addLabel(.br, 0); // jump to start of loop1704 try cg.addLabel(.br, 0); // jump to start of loop
1707 }1705 }
1708 try func.endBlock(); // close off loop block1706 try cg.endBlock(); // close off loop block
1709 try func.endBlock(); // close off outer block1707 try cg.endBlock(); // close off outer block
1710}1708}
17111709
1712fn ptrSize(func: *const CodeGen) u16 {1710fn ptrSize(cg: *const CodeGen) u16 {
1713 return @divExact(func.target.ptrBitWidth(), 8);1711 return @divExact(cg.target.ptrBitWidth(), 8);
1714}
1715
1716fn arch(func: *const CodeGen) std.Target.Cpu.Arch {
1717 return func.target.cpu.arch;
1718}1712}
17191713
1720/// For a given `Type`, will return true when the type will be passed1714/// For a given `Type`, will return true when the type will be passed
1721/// by reference, rather than by value1715/// by reference, rather than by value
1722fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool {1716fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool {
1723 const zcu = pt.zcu;
1724 const ip = &zcu.intern_pool;1717 const ip = &zcu.intern_pool;
1725 switch (ty.zigTypeTag(zcu)) {1718 switch (ty.zigTypeTag(zcu)) {
1726 .type,1719 .type,
...@@ -1753,14 +1746,14 @@ fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool {...@@ -1753,14 +1746,14 @@ fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool {
1753 },1746 },
1754 .@"struct" => {1747 .@"struct" => {
1755 if (zcu.typeToPackedStruct(ty)) |packed_struct| {1748 if (zcu.typeToPackedStruct(ty)) |packed_struct| {
1756 return isByRef(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)), pt, target);1749 return isByRef(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)), zcu, target);
1757 }1750 }
1758 return ty.hasRuntimeBitsIgnoreComptime(zcu);1751 return ty.hasRuntimeBitsIgnoreComptime(zcu);
1759 },1752 },
1760 .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled,1753 .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled,
1761 .int => return ty.intInfo(zcu).bits > 64,1754 .int => return ty.intInfo(zcu).bits > 64,
1762 .@"enum" => return ty.intInfo(zcu).bits > 64,1755 .@"enum" => return ty.intInfo(zcu).bits > 64,
1763 .float => return ty.floatBits(target) > 64,1756 .float => return ty.floatBits(target.*) > 64,
1764 .error_union => {1757 .error_union => {
1765 const pl_ty = ty.errorUnionPayload(zcu);1758 const pl_ty = ty.errorUnionPayload(zcu);
1766 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {1759 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
...@@ -1791,8 +1784,8 @@ const SimdStoreStrategy = enum {...@@ -1791,8 +1784,8 @@ const SimdStoreStrategy = enum {
1791/// This means when a given type is 128 bits and either the simd128 or relaxed-simd1784/// This means when a given type is 128 bits and either the simd128 or relaxed-simd
1792/// features are enabled, the function will return `.direct`. This would allow to store1785/// features are enabled, the function will return `.direct`. This would allow to store
1793/// it using a instruction, rather than an unrolled version.1786/// it using a instruction, rather than an unrolled version.
1794fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStoreStrategy {1787pub fn determineSimdStoreStrategy(ty: Type, zcu: *const Zcu, target: *const std.Target) SimdStoreStrategy {
1795 std.debug.assert(ty.zigTypeTag(zcu) == .vector);1788 assert(ty.zigTypeTag(zcu) == .vector);
1796 if (ty.bitSize(zcu) != 128) return .unrolled;1789 if (ty.bitSize(zcu) != 128) return .unrolled;
1797 const hasFeature = std.Target.wasm.featureSetHas;1790 const hasFeature = std.Target.wasm.featureSetHas;
1798 const features = target.cpu.features;1791 const features = target.cpu.features;
...@@ -1806,215 +1799,214 @@ fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStore...@@ -1806,215 +1799,214 @@ fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStore
1806/// This can be used to get a pointer to a struct field, error payload, etc.1799/// This can be used to get a pointer to a struct field, error payload, etc.
1807/// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new1800/// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new
1808/// local value to store the pointer. This allows for local re-use and improves binary size.1801/// local value to store the pointer. This allows for local re-use and improves binary size.
1809fn buildPointerOffset(func: *CodeGen, ptr_value: WValue, offset: u64, action: enum { modify, new }) InnerError!WValue {1802fn buildPointerOffset(cg: *CodeGen, ptr_value: WValue, offset: u64, action: enum { modify, new }) InnerError!WValue {
1810 // do not perform arithmetic when offset is 0.1803 // do not perform arithmetic when offset is 0.
1811 if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value;1804 if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value;
1812 const result_ptr: WValue = switch (action) {1805 const result_ptr: WValue = switch (action) {
1813 .new => try func.ensureAllocLocal(Type.usize),1806 .new => try cg.ensureAllocLocal(Type.usize),
1814 .modify => ptr_value,1807 .modify => ptr_value,
1815 };1808 };
1816 try func.emitWValue(ptr_value);1809 try cg.emitWValue(ptr_value);
1817 if (offset + ptr_value.offset() > 0) {1810 if (offset + ptr_value.offset() > 0) {
1818 switch (func.arch()) {1811 switch (cg.ptr_size) {
1819 .wasm32 => {1812 .wasm32 => {
1820 try func.addImm32(@intCast(offset + ptr_value.offset()));1813 try cg.addImm32(@intCast(offset + ptr_value.offset()));
1821 try func.addTag(.i32_add);1814 try cg.addTag(.i32_add);
1822 },1815 },
1823 .wasm64 => {1816 .wasm64 => {
1824 try func.addImm64(offset + ptr_value.offset());1817 try cg.addImm64(offset + ptr_value.offset());
1825 try func.addTag(.i64_add);1818 try cg.addTag(.i64_add);
1826 },1819 },
1827 else => unreachable,
1828 }1820 }
1829 }1821 }
1830 try func.addLabel(.local_set, result_ptr.local.value);1822 try cg.addLocal(.local_set, result_ptr.local.value);
1831 return result_ptr;1823 return result_ptr;
1832}1824}
18331825
1834fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {1826fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1835 const air_tags = func.air.instructions.items(.tag);1827 const air_tags = cg.air.instructions.items(.tag);
1836 return switch (air_tags[@intFromEnum(inst)]) {1828 return switch (air_tags[@intFromEnum(inst)]) {
1837 .inferred_alloc, .inferred_alloc_comptime => unreachable,1829 .inferred_alloc, .inferred_alloc_comptime => unreachable,
18381830
1839 .add => func.airBinOp(inst, .add),1831 .add => cg.airBinOp(inst, .add),
1840 .add_sat => func.airSatBinOp(inst, .add),1832 .add_sat => cg.airSatBinOp(inst, .add),
1841 .add_wrap => func.airWrapBinOp(inst, .add),1833 .add_wrap => cg.airWrapBinOp(inst, .add),
1842 .sub => func.airBinOp(inst, .sub),1834 .sub => cg.airBinOp(inst, .sub),
1843 .sub_sat => func.airSatBinOp(inst, .sub),1835 .sub_sat => cg.airSatBinOp(inst, .sub),
1844 .sub_wrap => func.airWrapBinOp(inst, .sub),1836 .sub_wrap => cg.airWrapBinOp(inst, .sub),
1845 .mul => func.airBinOp(inst, .mul),1837 .mul => cg.airBinOp(inst, .mul),
1846 .mul_sat => func.airSatMul(inst),1838 .mul_sat => cg.airSatMul(inst),
1847 .mul_wrap => func.airWrapBinOp(inst, .mul),1839 .mul_wrap => cg.airWrapBinOp(inst, .mul),
1848 .div_float, .div_exact => func.airDiv(inst),1840 .div_float, .div_exact => cg.airDiv(inst),
1849 .div_trunc => func.airDivTrunc(inst),1841 .div_trunc => cg.airDivTrunc(inst),
1850 .div_floor => func.airDivFloor(inst),1842 .div_floor => cg.airDivFloor(inst),
1851 .bit_and => func.airBinOp(inst, .@"and"),1843 .bit_and => cg.airBinOp(inst, .@"and"),
1852 .bit_or => func.airBinOp(inst, .@"or"),1844 .bit_or => cg.airBinOp(inst, .@"or"),
1853 .bool_and => func.airBinOp(inst, .@"and"),1845 .bool_and => cg.airBinOp(inst, .@"and"),
1854 .bool_or => func.airBinOp(inst, .@"or"),1846 .bool_or => cg.airBinOp(inst, .@"or"),
1855 .rem => func.airRem(inst),1847 .rem => cg.airRem(inst),
1856 .mod => func.airMod(inst),1848 .mod => cg.airMod(inst),
1857 .shl => func.airWrapBinOp(inst, .shl),1849 .shl => cg.airWrapBinOp(inst, .shl),
1858 .shl_exact => func.airBinOp(inst, .shl),1850 .shl_exact => cg.airBinOp(inst, .shl),
1859 .shl_sat => func.airShlSat(inst),1851 .shl_sat => cg.airShlSat(inst),
1860 .shr, .shr_exact => func.airBinOp(inst, .shr),1852 .shr, .shr_exact => cg.airBinOp(inst, .shr),
1861 .xor => func.airBinOp(inst, .xor),1853 .xor => cg.airBinOp(inst, .xor),
1862 .max => func.airMaxMin(inst, .max),1854 .max => cg.airMaxMin(inst, .fmax, .gt),
1863 .min => func.airMaxMin(inst, .min),1855 .min => cg.airMaxMin(inst, .fmin, .lt),
1864 .mul_add => func.airMulAdd(inst),1856 .mul_add => cg.airMulAdd(inst),
18651857
1866 .sqrt => func.airUnaryFloatOp(inst, .sqrt),1858 .sqrt => cg.airUnaryFloatOp(inst, .sqrt),
1867 .sin => func.airUnaryFloatOp(inst, .sin),1859 .sin => cg.airUnaryFloatOp(inst, .sin),
1868 .cos => func.airUnaryFloatOp(inst, .cos),1860 .cos => cg.airUnaryFloatOp(inst, .cos),
1869 .tan => func.airUnaryFloatOp(inst, .tan),1861 .tan => cg.airUnaryFloatOp(inst, .tan),
1870 .exp => func.airUnaryFloatOp(inst, .exp),1862 .exp => cg.airUnaryFloatOp(inst, .exp),
1871 .exp2 => func.airUnaryFloatOp(inst, .exp2),1863 .exp2 => cg.airUnaryFloatOp(inst, .exp2),
1872 .log => func.airUnaryFloatOp(inst, .log),1864 .log => cg.airUnaryFloatOp(inst, .log),
1873 .log2 => func.airUnaryFloatOp(inst, .log2),1865 .log2 => cg.airUnaryFloatOp(inst, .log2),
1874 .log10 => func.airUnaryFloatOp(inst, .log10),1866 .log10 => cg.airUnaryFloatOp(inst, .log10),
1875 .floor => func.airUnaryFloatOp(inst, .floor),1867 .floor => cg.airUnaryFloatOp(inst, .floor),
1876 .ceil => func.airUnaryFloatOp(inst, .ceil),1868 .ceil => cg.airUnaryFloatOp(inst, .ceil),
1877 .round => func.airUnaryFloatOp(inst, .round),1869 .round => cg.airUnaryFloatOp(inst, .round),
1878 .trunc_float => func.airUnaryFloatOp(inst, .trunc),1870 .trunc_float => cg.airUnaryFloatOp(inst, .trunc),
1879 .neg => func.airUnaryFloatOp(inst, .neg),1871 .neg => cg.airUnaryFloatOp(inst, .neg),
18801872
1881 .abs => func.airAbs(inst),1873 .abs => cg.airAbs(inst),
18821874
1883 .add_with_overflow => func.airAddSubWithOverflow(inst, .add),1875 .add_with_overflow => cg.airAddSubWithOverflow(inst, .add),
1884 .sub_with_overflow => func.airAddSubWithOverflow(inst, .sub),1876 .sub_with_overflow => cg.airAddSubWithOverflow(inst, .sub),
1885 .shl_with_overflow => func.airShlWithOverflow(inst),1877 .shl_with_overflow => cg.airShlWithOverflow(inst),
1886 .mul_with_overflow => func.airMulWithOverflow(inst),1878 .mul_with_overflow => cg.airMulWithOverflow(inst),
18871879
1888 .clz => func.airClz(inst),1880 .clz => cg.airClz(inst),
1889 .ctz => func.airCtz(inst),1881 .ctz => cg.airCtz(inst),
18901882
1891 .cmp_eq => func.airCmp(inst, .eq),1883 .cmp_eq => cg.airCmp(inst, .eq),
1892 .cmp_gte => func.airCmp(inst, .gte),1884 .cmp_gte => cg.airCmp(inst, .gte),
1893 .cmp_gt => func.airCmp(inst, .gt),1885 .cmp_gt => cg.airCmp(inst, .gt),
1894 .cmp_lte => func.airCmp(inst, .lte),1886 .cmp_lte => cg.airCmp(inst, .lte),
1895 .cmp_lt => func.airCmp(inst, .lt),1887 .cmp_lt => cg.airCmp(inst, .lt),
1896 .cmp_neq => func.airCmp(inst, .neq),1888 .cmp_neq => cg.airCmp(inst, .neq),
18971889
1898 .cmp_vector => func.airCmpVector(inst),1890 .cmp_vector => cg.airCmpVector(inst),
1899 .cmp_lt_errors_len => func.airCmpLtErrorsLen(inst),1891 .cmp_lt_errors_len => cg.airCmpLtErrorsLen(inst),
19001892
1901 .array_elem_val => func.airArrayElemVal(inst),1893 .array_elem_val => cg.airArrayElemVal(inst),
1902 .array_to_slice => func.airArrayToSlice(inst),1894 .array_to_slice => cg.airArrayToSlice(inst),
1903 .alloc => func.airAlloc(inst),1895 .alloc => cg.airAlloc(inst),
1904 .arg => func.airArg(inst),1896 .arg => cg.airArg(inst),
1905 .bitcast => func.airBitcast(inst),1897 .bitcast => cg.airBitcast(inst),
1906 .block => func.airBlock(inst),1898 .block => cg.airBlock(inst),
1907 .trap => func.airTrap(inst),1899 .trap => cg.airTrap(inst),
1908 .breakpoint => func.airBreakpoint(inst),1900 .breakpoint => cg.airBreakpoint(inst),
1909 .br => func.airBr(inst),1901 .br => cg.airBr(inst),
1910 .repeat => func.airRepeat(inst),1902 .repeat => cg.airRepeat(inst),
1911 .switch_dispatch => return func.fail("TODO implement `switch_dispatch`", .{}),1903 .switch_dispatch => return cg.fail("TODO implement `switch_dispatch`", .{}),
1912 .int_from_bool => func.airIntFromBool(inst),1904 .int_from_bool => cg.airIntFromBool(inst),
1913 .cond_br => func.airCondBr(inst),1905 .cond_br => cg.airCondBr(inst),
1914 .intcast => func.airIntcast(inst),1906 .intcast => cg.airIntcast(inst),
1915 .fptrunc => func.airFptrunc(inst),1907 .fptrunc => cg.airFptrunc(inst),
1916 .fpext => func.airFpext(inst),1908 .fpext => cg.airFpext(inst),
1917 .int_from_float => func.airIntFromFloat(inst),1909 .int_from_float => cg.airIntFromFloat(inst),
1918 .float_from_int => func.airFloatFromInt(inst),1910 .float_from_int => cg.airFloatFromInt(inst),
1919 .get_union_tag => func.airGetUnionTag(inst),1911 .get_union_tag => cg.airGetUnionTag(inst),
19201912
1921 .@"try" => func.airTry(inst),1913 .@"try" => cg.airTry(inst),
1922 .try_cold => func.airTry(inst),1914 .try_cold => cg.airTry(inst),
1923 .try_ptr => func.airTryPtr(inst),1915 .try_ptr => cg.airTryPtr(inst),
1924 .try_ptr_cold => func.airTryPtr(inst),1916 .try_ptr_cold => cg.airTryPtr(inst),
19251917
1926 .dbg_stmt => func.airDbgStmt(inst),1918 .dbg_stmt => cg.airDbgStmt(inst),
1927 .dbg_empty_stmt => try func.finishAir(inst, .none, &.{}),1919 .dbg_empty_stmt => try cg.finishAir(inst, .none, &.{}),
1928 .dbg_inline_block => func.airDbgInlineBlock(inst),1920 .dbg_inline_block => cg.airDbgInlineBlock(inst),
1929 .dbg_var_ptr => func.airDbgVar(inst, .local_var, true),1921 .dbg_var_ptr => cg.airDbgVar(inst, .local_var, true),
1930 .dbg_var_val => func.airDbgVar(inst, .local_var, false),1922 .dbg_var_val => cg.airDbgVar(inst, .local_var, false),
1931 .dbg_arg_inline => func.airDbgVar(inst, .local_arg, false),1923 .dbg_arg_inline => cg.airDbgVar(inst, .local_arg, false),
19321924
1933 .call => func.airCall(inst, .auto),1925 .call => cg.airCall(inst, .auto),
1934 .call_always_tail => func.airCall(inst, .always_tail),1926 .call_always_tail => cg.airCall(inst, .always_tail),
1935 .call_never_tail => func.airCall(inst, .never_tail),1927 .call_never_tail => cg.airCall(inst, .never_tail),
1936 .call_never_inline => func.airCall(inst, .never_inline),1928 .call_never_inline => cg.airCall(inst, .never_inline),
19371929
1938 .is_err => func.airIsErr(inst, .i32_ne),1930 .is_err => cg.airIsErr(inst, .i32_ne),
1939 .is_non_err => func.airIsErr(inst, .i32_eq),1931 .is_non_err => cg.airIsErr(inst, .i32_eq),
19401932
1941 .is_null => func.airIsNull(inst, .i32_eq, .value),1933 .is_null => cg.airIsNull(inst, .i32_eq, .value),
1942 .is_non_null => func.airIsNull(inst, .i32_ne, .value),1934 .is_non_null => cg.airIsNull(inst, .i32_ne, .value),
1943 .is_null_ptr => func.airIsNull(inst, .i32_eq, .ptr),1935 .is_null_ptr => cg.airIsNull(inst, .i32_eq, .ptr),
1944 .is_non_null_ptr => func.airIsNull(inst, .i32_ne, .ptr),1936 .is_non_null_ptr => cg.airIsNull(inst, .i32_ne, .ptr),
19451937
1946 .load => func.airLoad(inst),1938 .load => cg.airLoad(inst),
1947 .loop => func.airLoop(inst),1939 .loop => cg.airLoop(inst),
1948 .memset => func.airMemset(inst, false),1940 .memset => cg.airMemset(inst, false),
1949 .memset_safe => func.airMemset(inst, true),1941 .memset_safe => cg.airMemset(inst, true),
1950 .not => func.airNot(inst),1942 .not => cg.airNot(inst),
1951 .optional_payload => func.airOptionalPayload(inst),1943 .optional_payload => cg.airOptionalPayload(inst),
1952 .optional_payload_ptr => func.airOptionalPayloadPtr(inst),1944 .optional_payload_ptr => cg.airOptionalPayloadPtr(inst),
1953 .optional_payload_ptr_set => func.airOptionalPayloadPtrSet(inst),1945 .optional_payload_ptr_set => cg.airOptionalPayloadPtrSet(inst),
1954 .ptr_add => func.airPtrBinOp(inst, .add),1946 .ptr_add => cg.airPtrBinOp(inst, .add),
1955 .ptr_sub => func.airPtrBinOp(inst, .sub),1947 .ptr_sub => cg.airPtrBinOp(inst, .sub),
1956 .ptr_elem_ptr => func.airPtrElemPtr(inst),1948 .ptr_elem_ptr => cg.airPtrElemPtr(inst),
1957 .ptr_elem_val => func.airPtrElemVal(inst),1949 .ptr_elem_val => cg.airPtrElemVal(inst),
1958 .int_from_ptr => func.airIntFromPtr(inst),1950 .int_from_ptr => cg.airIntFromPtr(inst),
1959 .ret => func.airRet(inst),1951 .ret => cg.airRet(inst),
1960 .ret_safe => func.airRet(inst), // TODO1952 .ret_safe => cg.airRet(inst), // TODO
1961 .ret_ptr => func.airRetPtr(inst),1953 .ret_ptr => cg.airRetPtr(inst),
1962 .ret_load => func.airRetLoad(inst),1954 .ret_load => cg.airRetLoad(inst),
1963 .splat => func.airSplat(inst),1955 .splat => cg.airSplat(inst),
1964 .select => func.airSelect(inst),1956 .select => cg.airSelect(inst),
1965 .shuffle => func.airShuffle(inst),1957 .shuffle => cg.airShuffle(inst),
1966 .reduce => func.airReduce(inst),1958 .reduce => cg.airReduce(inst),
1967 .aggregate_init => func.airAggregateInit(inst),1959 .aggregate_init => cg.airAggregateInit(inst),
1968 .union_init => func.airUnionInit(inst),1960 .union_init => cg.airUnionInit(inst),
1969 .prefetch => func.airPrefetch(inst),1961 .prefetch => cg.airPrefetch(inst),
1970 .popcount => func.airPopcount(inst),1962 .popcount => cg.airPopcount(inst),
1971 .byte_swap => func.airByteSwap(inst),1963 .byte_swap => cg.airByteSwap(inst),
1972 .bit_reverse => func.airBitReverse(inst),1964 .bit_reverse => cg.airBitReverse(inst),
19731965
1974 .slice => func.airSlice(inst),1966 .slice => cg.airSlice(inst),
1975 .slice_len => func.airSliceLen(inst),1967 .slice_len => cg.airSliceLen(inst),
1976 .slice_elem_val => func.airSliceElemVal(inst),1968 .slice_elem_val => cg.airSliceElemVal(inst),
1977 .slice_elem_ptr => func.airSliceElemPtr(inst),1969 .slice_elem_ptr => cg.airSliceElemPtr(inst),
1978 .slice_ptr => func.airSlicePtr(inst),1970 .slice_ptr => cg.airSlicePtr(inst),
1979 .ptr_slice_len_ptr => func.airPtrSliceFieldPtr(inst, func.ptrSize()),1971 .ptr_slice_len_ptr => cg.airPtrSliceFieldPtr(inst, cg.ptrSize()),
1980 .ptr_slice_ptr_ptr => func.airPtrSliceFieldPtr(inst, 0),1972 .ptr_slice_ptr_ptr => cg.airPtrSliceFieldPtr(inst, 0),
1981 .store => func.airStore(inst, false),1973 .store => cg.airStore(inst, false),
1982 .store_safe => func.airStore(inst, true),1974 .store_safe => cg.airStore(inst, true),
19831975
1984 .set_union_tag => func.airSetUnionTag(inst),1976 .set_union_tag => cg.airSetUnionTag(inst),
1985 .struct_field_ptr => func.airStructFieldPtr(inst),1977 .struct_field_ptr => cg.airStructFieldPtr(inst),
1986 .struct_field_ptr_index_0 => func.airStructFieldPtrIndex(inst, 0),1978 .struct_field_ptr_index_0 => cg.airStructFieldPtrIndex(inst, 0),
1987 .struct_field_ptr_index_1 => func.airStructFieldPtrIndex(inst, 1),1979 .struct_field_ptr_index_1 => cg.airStructFieldPtrIndex(inst, 1),
1988 .struct_field_ptr_index_2 => func.airStructFieldPtrIndex(inst, 2),1980 .struct_field_ptr_index_2 => cg.airStructFieldPtrIndex(inst, 2),
1989 .struct_field_ptr_index_3 => func.airStructFieldPtrIndex(inst, 3),1981 .struct_field_ptr_index_3 => cg.airStructFieldPtrIndex(inst, 3),
1990 .struct_field_val => func.airStructFieldVal(inst),1982 .struct_field_val => cg.airStructFieldVal(inst),
1991 .field_parent_ptr => func.airFieldParentPtr(inst),1983 .field_parent_ptr => cg.airFieldParentPtr(inst),
19921984
1993 .switch_br => func.airSwitchBr(inst),1985 .switch_br => cg.airSwitchBr(inst),
1994 .loop_switch_br => return func.fail("TODO implement `loop_switch_br`", .{}),1986 .loop_switch_br => return cg.fail("TODO implement `loop_switch_br`", .{}),
1995 .trunc => func.airTrunc(inst),1987 .trunc => cg.airTrunc(inst),
1996 .unreach => func.airUnreachable(inst),1988 .unreach => cg.airUnreachable(inst),
19971989
1998 .wrap_optional => func.airWrapOptional(inst),1990 .wrap_optional => cg.airWrapOptional(inst),
1999 .unwrap_errunion_payload => func.airUnwrapErrUnionPayload(inst, false),1991 .unwrap_errunion_payload => cg.airUnwrapErrUnionPayload(inst, false),
2000 .unwrap_errunion_payload_ptr => func.airUnwrapErrUnionPayload(inst, true),1992 .unwrap_errunion_payload_ptr => cg.airUnwrapErrUnionPayload(inst, true),
2001 .unwrap_errunion_err => func.airUnwrapErrUnionError(inst, false),1993 .unwrap_errunion_err => cg.airUnwrapErrUnionError(inst, false),
2002 .unwrap_errunion_err_ptr => func.airUnwrapErrUnionError(inst, true),1994 .unwrap_errunion_err_ptr => cg.airUnwrapErrUnionError(inst, true),
2003 .wrap_errunion_payload => func.airWrapErrUnionPayload(inst),1995 .wrap_errunion_payload => cg.airWrapErrUnionPayload(inst),
2004 .wrap_errunion_err => func.airWrapErrUnionErr(inst),1996 .wrap_errunion_err => cg.airWrapErrUnionErr(inst),
2005 .errunion_payload_ptr_set => func.airErrUnionPayloadPtrSet(inst),1997 .errunion_payload_ptr_set => cg.airErrUnionPayloadPtrSet(inst),
2006 .error_name => func.airErrorName(inst),1998 .error_name => cg.airErrorName(inst),
20071999
2008 .wasm_memory_size => func.airWasmMemorySize(inst),2000 .wasm_memory_size => cg.airWasmMemorySize(inst),
2009 .wasm_memory_grow => func.airWasmMemoryGrow(inst),2001 .wasm_memory_grow => cg.airWasmMemoryGrow(inst),
20102002
2011 .memcpy => func.airMemcpy(inst),2003 .memcpy => cg.airMemcpy(inst),
20122004
2013 .ret_addr => func.airRetAddr(inst),2005 .ret_addr => cg.airRetAddr(inst),
2014 .tag_name => func.airTagName(inst),2006 .tag_name => cg.airTagName(inst),
20152007
2016 .error_set_has_value => func.airErrorSetHasValue(inst),2008 .error_set_has_value => cg.airErrorSetHasValue(inst),
2017 .frame_addr => func.airFrameAddress(inst),2009 .frame_addr => cg.airFrameAddress(inst),
20182010
2019 .assembly,2011 .assembly,
2020 .is_err_ptr,2012 .is_err_ptr,
...@@ -2030,18 +2022,18 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2030,18 +2022,18 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2030 .c_va_copy,2022 .c_va_copy,
2031 .c_va_end,2023 .c_va_end,
2032 .c_va_start,2024 .c_va_start,
2033 => |tag| return func.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),2025 => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
20342026
2035 .atomic_load => func.airAtomicLoad(inst),2027 .atomic_load => cg.airAtomicLoad(inst),
2036 .atomic_store_unordered,2028 .atomic_store_unordered,
2037 .atomic_store_monotonic,2029 .atomic_store_monotonic,
2038 .atomic_store_release,2030 .atomic_store_release,
2039 .atomic_store_seq_cst,2031 .atomic_store_seq_cst,
2040 // in WebAssembly, all atomic instructions are sequentially ordered.2032 // in WebAssembly, all atomic instructions are sequentially ordered.
2041 => func.airAtomicStore(inst),2033 => cg.airAtomicStore(inst),
2042 .atomic_rmw => func.airAtomicRmw(inst),2034 .atomic_rmw => cg.airAtomicRmw(inst),
2043 .cmpxchg_weak => func.airCmpxchg(inst),2035 .cmpxchg_weak => cg.airCmpxchg(inst),
2044 .cmpxchg_strong => func.airCmpxchg(inst),2036 .cmpxchg_strong => cg.airCmpxchg(inst),
20452037
2046 .add_optimized,2038 .add_optimized,
2047 .sub_optimized,2039 .sub_optimized,
...@@ -2062,12 +2054,12 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2062,12 +2054,12 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2062 .cmp_vector_optimized,2054 .cmp_vector_optimized,
2063 .reduce_optimized,2055 .reduce_optimized,
2064 .int_from_float_optimized,2056 .int_from_float_optimized,
2065 => return func.fail("TODO implement optimized float mode", .{}),2057 => return cg.fail("TODO implement optimized float mode", .{}),
20662058
2067 .add_safe,2059 .add_safe,
2068 .sub_safe,2060 .sub_safe,
2069 .mul_safe,2061 .mul_safe,
2070 => return func.fail("TODO implement safety_checked_instructions", .{}),2062 => return cg.fail("TODO implement safety_checked_instructions", .{}),
20712063
2072 .work_item_id,2064 .work_item_id,
2073 .work_group_size,2065 .work_group_size,
...@@ -2076,123 +2068,120 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2076,123 +2068,120 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2076 };2068 };
2077}2069}
20782070
2079fn genBody(func: *CodeGen, body: []const Air.Inst.Index) InnerError!void {2071fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
2080 const pt = func.pt;2072 const zcu = cg.pt.zcu;
2081 const zcu = pt.zcu;
2082 const ip = &zcu.intern_pool;2073 const ip = &zcu.intern_pool;
20832074
2084 for (body) |inst| {2075 for (body) |inst| {
2085 if (func.liveness.isUnused(inst) and !func.air.mustLower(inst, ip)) {2076 if (cg.liveness.isUnused(inst) and !cg.air.mustLower(inst, ip)) {
2086 continue;2077 continue;
2087 }2078 }
2088 const old_bookkeeping_value = func.air_bookkeeping;2079 const old_bookkeeping_value = cg.air_bookkeeping;
2089 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, Liveness.bpi);2080 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, Liveness.bpi);
2090 try func.genInst(inst);2081 try cg.genInst(inst);
20912082
2092 if (std.debug.runtime_safety and func.air_bookkeeping < old_bookkeeping_value + 1) {2083 if (std.debug.runtime_safety and cg.air_bookkeeping < old_bookkeeping_value + 1) {
2093 std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{}')", .{2084 std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{}')", .{
2094 inst,2085 inst,
2095 func.air.instructions.items(.tag)[@intFromEnum(inst)],2086 cg.air.instructions.items(.tag)[@intFromEnum(inst)],
2096 });2087 });
2097 }2088 }
2098 }2089 }
2099}2090}
21002091
2101fn airRet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {2092fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2102 const pt = func.pt;2093 const zcu = cg.pt.zcu;
2103 const zcu = pt.zcu;2094 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2104 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;2095 const operand = try cg.resolveInst(un_op);
2105 const operand = try func.resolveInst(un_op);2096 const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?;
2106 const fn_info = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?;
2107 const ret_ty = Type.fromInterned(fn_info.return_type);2097 const ret_ty = Type.fromInterned(fn_info.return_type);
21082098
2109 // result must be stored in the stack and we return a pointer2099 // result must be stored in the stack and we return a pointer
2110 // to the stack instead2100 // to the stack instead
2111 if (func.return_value != .none) {2101 if (cg.return_value != .none) {
2112 try func.store(func.return_value, operand, ret_ty, 0);2102 try cg.store(cg.return_value, operand, ret_ty, 0);
2113 } else if (fn_info.cc == .wasm_watc and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {2103 } else if (fn_info.cc == .wasm_watc and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2114 switch (ret_ty.zigTypeTag(zcu)) {2104 switch (ret_ty.zigTypeTag(zcu)) {
2115 // Aggregate types can be lowered as a singular value2105 // Aggregate types can be lowered as a singular value
2116 .@"struct", .@"union" => {2106 .@"struct", .@"union" => {
2117 const scalar_type = abi.scalarType(ret_ty, zcu);2107 const scalar_type = abi.scalarType(ret_ty, zcu);
2118 try func.emitWValue(operand);2108 try cg.emitWValue(operand);
2119 const opcode = buildOpcode(.{2109 const opcode = buildOpcode(.{
2120 .op = .load,2110 .op = .load,
2121 .width = @as(u8, @intCast(scalar_type.abiSize(zcu) * 8)),2111 .width = @as(u8, @intCast(scalar_type.abiSize(zcu) * 8)),
2122 .signedness = if (scalar_type.isSignedInt(zcu)) .signed else .unsigned,2112 .signedness = if (scalar_type.isSignedInt(zcu)) .signed else .unsigned,
2123 .valtype1 = typeToValtype(scalar_type, pt, func.target.*),2113 .valtype1 = typeToValtype(scalar_type, zcu, cg.target),
2124 });2114 });
2125 try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{2115 try cg.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
2126 .offset = operand.offset(),2116 .offset = operand.offset(),
2127 .alignment = @intCast(scalar_type.abiAlignment(zcu).toByteUnits().?),2117 .alignment = @intCast(scalar_type.abiAlignment(zcu).toByteUnits().?),
2128 });2118 });
2129 },2119 },
2130 else => try func.emitWValue(operand),2120 else => try cg.emitWValue(operand),
2131 }2121 }
2132 } else {2122 } else {
2133 if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and ret_ty.isError(zcu)) {2123 if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and ret_ty.isError(zcu)) {
2134 try func.addImm32(0);2124 try cg.addImm32(0);
2135 } else {2125 } else {
2136 try func.emitWValue(operand);2126 try cg.emitWValue(operand);
2137 }2127 }
2138 }2128 }
2139 try func.restoreStackPointer();2129 try cg.restoreStackPointer();
2140 try func.addTag(.@"return");2130 try cg.addTag(.@"return");
21412131
2142 return func.finishAir(inst, .none, &.{un_op});2132 return cg.finishAir(inst, .none, &.{un_op});
2143}2133}
21442134
2145fn airRetPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {2135fn airRetPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2146 const pt = func.pt;2136 const zcu = cg.pt.zcu;
2147 const zcu = pt.zcu;2137 const child_type = cg.typeOfIndex(inst).childType(zcu);
2148 const child_type = func.typeOfIndex(inst).childType(zcu);
21492138
2150 const result = result: {2139 const result = result: {
2151 if (!child_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {2140 if (!child_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {
2152 break :result try func.allocStack(Type.usize); // create pointer to void2141 break :result try cg.allocStack(Type.usize); // create pointer to void
2153 }2142 }
21542143
2155 const fn_info = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?;2144 const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?;
2156 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) {2145 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, cg.target)) {
2157 break :result func.return_value;2146 break :result cg.return_value;
2158 }2147 }
21592148
2160 break :result try func.allocStackPtr(inst);2149 break :result try cg.allocStackPtr(inst);
2161 };2150 };
21622151
2163 return func.finishAir(inst, result, &.{});2152 return cg.finishAir(inst, result, &.{});
2164}2153}
21652154
2166fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {2155fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2167 const pt = func.pt;2156 const zcu = cg.pt.zcu;
2168 const zcu = pt.zcu;2157 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2169 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;2158 const operand = try cg.resolveInst(un_op);
2170 const operand = try func.resolveInst(un_op);2159 const ret_ty = cg.typeOf(un_op).childType(zcu);
2171 const ret_ty = func.typeOf(un_op).childType(zcu);
21722160
2173 const fn_info = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?;2161 const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?;
2174 if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {2162 if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2175 if (ret_ty.isError(zcu)) {2163 if (ret_ty.isError(zcu)) {
2176 try func.addImm32(0);2164 try cg.addImm32(0);
2177 }2165 }
2178 } else if (!firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) {2166 } else if (!firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, cg.target)) {
2179 // leave on the stack2167 // leave on the stack
2180 _ = try func.load(operand, ret_ty, 0);2168 _ = try cg.load(operand, ret_ty, 0);
2181 }2169 }
21822170
2183 try func.restoreStackPointer();2171 try cg.restoreStackPointer();
2184 try func.addTag(.@"return");2172 try cg.addTag(.@"return");
2185 return func.finishAir(inst, .none, &.{un_op});2173 return cg.finishAir(inst, .none, &.{un_op});
2186}2174}
21872175
2188fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {2176fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {
2189 if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{});2177 const wasm = cg.wasm;
2190 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;2178 if (modifier == .always_tail) return cg.fail("TODO implement tail calls for wasm", .{});
2191 const extra = func.air.extraData(Air.Call, pl_op.payload);2179 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
2192 const args = @as([]const Air.Inst.Ref, @ptrCast(func.air.extra[extra.end..][0..extra.data.args_len]));2180 const extra = cg.air.extraData(Air.Call, pl_op.payload);
2193 const ty = func.typeOf(pl_op.operand);2181 const args: []const Air.Inst.Ref = @ptrCast(cg.air.extra[extra.end..][0..extra.data.args_len]);
2182 const ty = cg.typeOf(pl_op.operand);
21942183
2195 const pt = func.pt;2184 const pt = cg.pt;
2196 const zcu = pt.zcu;2185 const zcu = pt.zcu;
2197 const ip = &zcu.intern_pool;2186 const ip = &zcu.intern_pool;
2198 const fn_ty = switch (ty.zigTypeTag(zcu)) {2187 const fn_ty = switch (ty.zigTypeTag(zcu)) {
...@@ -2202,142 +2191,109 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif...@@ -2202,142 +2191,109 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
2202 };2191 };
2203 const ret_ty = fn_ty.fnReturnType(zcu);2192 const ret_ty = fn_ty.fnReturnType(zcu);
2204 const fn_info = zcu.typeToFunc(fn_ty).?;2193 const fn_info = zcu.typeToFunc(fn_ty).?;
2205 const first_param_sret = firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*);2194 const first_param_sret = firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, cg.target);
22062195
2207 const callee: ?InternPool.Nav.Index = blk: {2196 const callee: ?InternPool.Nav.Index = blk: {
2208 const func_val = (try func.air.value(pl_op.operand, pt)) orelse break :blk null;2197 const func_val = (try cg.air.value(pl_op.operand, pt)) orelse break :blk null;
22092198
2210 switch (ip.indexToKey(func_val.toIntern())) {2199 switch (ip.indexToKey(func_val.toIntern())) {
2211 .func => |function| {2200 inline .func, .@"extern" => |x| break :blk x.owner_nav,
2212 _ = try func.bin_file.getOrCreateAtomForNav(pt, function.owner_nav);
2213 break :blk function.owner_nav;
2214 },
2215 .@"extern" => |@"extern"| {
2216 const ext_nav = ip.getNav(@"extern".owner_nav);
2217 const ext_info = zcu.typeToFunc(Type.fromInterned(@"extern".ty)).?;
2218 var func_type = try genFunctype(
2219 func.gpa,
2220 ext_info.cc,
2221 ext_info.param_types.get(ip),
2222 Type.fromInterned(ext_info.return_type),
2223 pt,
2224 func.target.*,
2225 );
2226 defer func_type.deinit(func.gpa);
2227 const atom_index = try func.bin_file.getOrCreateAtomForNav(pt, @"extern".owner_nav);
2228 const atom = func.bin_file.getAtomPtr(atom_index);
2229 const type_index = try func.bin_file.storeNavType(@"extern".owner_nav, func_type);
2230 try func.bin_file.addOrUpdateImport(
2231 ext_nav.name.toSlice(ip),
2232 atom.sym_index,
2233 @"extern".lib_name.toSlice(ip),
2234 type_index,
2235 );
2236 break :blk @"extern".owner_nav;
2237 },
2238 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {2201 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
2239 .nav => |nav| {2202 .nav => |nav| break :blk nav,
2240 _ = try func.bin_file.getOrCreateAtomForNav(pt, nav);
2241 break :blk nav;
2242 },
2243 else => {},2203 else => {},
2244 },2204 },
2245 else => {},2205 else => {},
2246 }2206 }
2247 return func.fail("Expected a function, but instead found '{s}'", .{@tagName(ip.indexToKey(func_val.toIntern()))});2207 return cg.fail("unable to lower callee to a function index", .{});
2248 };2208 };
22492209
2250 const sret: WValue = if (first_param_sret) blk: {2210 const sret: WValue = if (first_param_sret) blk: {
2251 const sret_local = try func.allocStack(ret_ty);2211 const sret_local = try cg.allocStack(ret_ty);
2252 try func.lowerToStack(sret_local);2212 try cg.lowerToStack(sret_local);
2253 break :blk sret_local;2213 break :blk sret_local;
2254 } else .none;2214 } else .none;
22552215
2256 for (args) |arg| {2216 for (args) |arg| {
2257 const arg_val = try func.resolveInst(arg);2217 const arg_val = try cg.resolveInst(arg);
22582218
2259 const arg_ty = func.typeOf(arg);2219 const arg_ty = cg.typeOf(arg);
2260 if (!arg_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;2220 if (!arg_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
22612221
2262 try func.lowerArg(zcu.typeToFunc(fn_ty).?.cc, arg_ty, arg_val);2222 try cg.lowerArg(zcu.typeToFunc(fn_ty).?.cc, arg_ty, arg_val);
2263 }2223 }
22642224
2265 if (callee) |direct| {2225 if (callee) |nav_index| {
2266 const atom_index = func.bin_file.zig_object.?.navs.get(direct).?.atom;2226 try cg.addInst(.{ .tag = .call_nav, .data = .{ .nav_index = nav_index } });
2267 try func.addLabel(.call, @intFromEnum(func.bin_file.getAtom(atom_index).sym_index));
2268 } else {2227 } else {
2269 // in this case we call a function pointer2228 // in this case we call a function pointer
2270 // so load its value onto the stack2229 // so load its value onto the stack
2271 std.debug.assert(ty.zigTypeTag(zcu) == .pointer);2230 assert(ty.zigTypeTag(zcu) == .pointer);
2272 const operand = try func.resolveInst(pl_op.operand);2231 const operand = try cg.resolveInst(pl_op.operand);
2273 try func.emitWValue(operand);2232 try cg.emitWValue(operand);
2274
2275 var fn_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target.*);
2276 defer fn_type.deinit(func.gpa);
22772233
2278 const fn_type_index = try func.bin_file.zig_object.?.putOrGetFuncType(func.gpa, fn_type);2234 const fn_type_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), cg.target);
2279 try func.addLabel(.call_indirect, fn_type_index);2235 try cg.addFuncTy(.call_indirect, fn_type_index);
2280 }2236 }
22812237
2282 const result_value = result_value: {2238 const result_value = result_value: {
2283 if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) {2239 if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) {
2284 break :result_value .none;2240 break :result_value .none;
2285 } else if (ret_ty.isNoReturn(zcu)) {2241 } else if (ret_ty.isNoReturn(zcu)) {
2286 try func.addTag(.@"unreachable");2242 try cg.addTag(.@"unreachable");
2287 break :result_value .none;2243 break :result_value .none;
2288 } else if (first_param_sret) {2244 } else if (first_param_sret) {
2289 break :result_value sret;2245 break :result_value sret;
2290 // TODO: Make this less fragile and optimize2246 // TODO: Make this less fragile and optimize
2291 } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_watc and ret_ty.zigTypeTag(zcu) == .@"struct" or ret_ty.zigTypeTag(zcu) == .@"union") {2247 } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_watc and ret_ty.zigTypeTag(zcu) == .@"struct" or ret_ty.zigTypeTag(zcu) == .@"union") {
2292 const result_local = try func.allocLocal(ret_ty);2248 const result_local = try cg.allocLocal(ret_ty);
2293 try func.addLabel(.local_set, result_local.local.value);2249 try cg.addLocal(.local_set, result_local.local.value);
2294 const scalar_type = abi.scalarType(ret_ty, zcu);2250 const scalar_type = abi.scalarType(ret_ty, zcu);
2295 const result = try func.allocStack(scalar_type);2251 const result = try cg.allocStack(scalar_type);
2296 try func.store(result, result_local, scalar_type, 0);2252 try cg.store(result, result_local, scalar_type, 0);
2297 break :result_value result;2253 break :result_value result;
2298 } else {2254 } else {
2299 const result_local = try func.allocLocal(ret_ty);2255 const result_local = try cg.allocLocal(ret_ty);
2300 try func.addLabel(.local_set, result_local.local.value);2256 try cg.addLocal(.local_set, result_local.local.value);
2301 break :result_value result_local;2257 break :result_value result_local;
2302 }2258 }
2303 };2259 };
23042260
2305 var bt = try func.iterateBigTomb(inst, 1 + args.len);2261 var bt = try cg.iterateBigTomb(inst, 1 + args.len);
2306 bt.feed(pl_op.operand);2262 bt.feed(pl_op.operand);
2307 for (args) |arg| bt.feed(arg);2263 for (args) |arg| bt.feed(arg);
2308 return bt.finishAir(result_value);2264 return bt.finishAir(result_value);
2309}2265}
23102266
2311fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {2267fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2312 const value = try func.allocStackPtr(inst);2268 const value = try cg.allocStackPtr(inst);
2313 return func.finishAir(inst, value, &.{});2269 return cg.finishAir(inst, value, &.{});
2314}2270}
23152271
2316fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {2272fn airStore(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
2317 const pt = func.pt;2273 const pt = cg.pt;
2318 const zcu = pt.zcu;2274 const zcu = pt.zcu;
2319 if (safety) {2275 if (safety) {
2320 // TODO if the value is undef, write 0xaa bytes to dest2276 // TODO if the value is undef, write 0xaa bytes to dest
2321 } else {2277 } else {
2322 // TODO if the value is undef, don't lower this instruction2278 // TODO if the value is undef, don't lower this instruction
2323 }2279 }
2324 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2280 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
23252281
2326 const lhs = try func.resolveInst(bin_op.lhs);2282 const lhs = try cg.resolveInst(bin_op.lhs);
2327 const rhs = try func.resolveInst(bin_op.rhs);2283 const rhs = try cg.resolveInst(bin_op.rhs);
2328 const ptr_ty = func.typeOf(bin_op.lhs);2284 const ptr_ty = cg.typeOf(bin_op.lhs);
2329 const ptr_info = ptr_ty.ptrInfo(zcu);2285 const ptr_info = ptr_ty.ptrInfo(zcu);
2330 const ty = ptr_ty.childType(zcu);2286 const ty = ptr_ty.childType(zcu);
23312287
2332 if (ptr_info.packed_offset.host_size == 0) {2288 if (ptr_info.packed_offset.host_size == 0) {
2333 try func.store(lhs, rhs, ty, 0);2289 try cg.store(lhs, rhs, ty, 0);
2334 } else {2290 } else {
2335 // at this point we have a non-natural alignment, we must2291 // at this point we have a non-natural alignment, we must
2336 // load the value, and then shift+or the rhs into the result location.2292 // load the value, and then shift+or the rhs into the result location.
2337 const int_elem_ty = try pt.intType(.unsigned, ptr_info.packed_offset.host_size * 8);2293 const int_elem_ty = try pt.intType(.unsigned, ptr_info.packed_offset.host_size * 8);
23382294
2339 if (isByRef(int_elem_ty, pt, func.target.*)) {2295 if (isByRef(int_elem_ty, zcu, cg.target)) {
2340 return func.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{});2296 return cg.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{});
2341 }2297 }
23422298
2343 var mask = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(ty.bitSize(zcu)))) - 1));2299 var mask = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(ty.bitSize(zcu)))) - 1));
...@@ -2356,115 +2312,115 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void...@@ -2356,115 +2312,115 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void
2356 else2312 else
2357 .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - ty.bitSize(zcu)) };2313 .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - ty.bitSize(zcu)) };
23582314
2359 try func.emitWValue(lhs);2315 try cg.emitWValue(lhs);
2360 const loaded = try func.load(lhs, int_elem_ty, 0);2316 const loaded = try cg.load(lhs, int_elem_ty, 0);
2361 const anded = try func.binOp(loaded, mask_val, int_elem_ty, .@"and");2317 const anded = try cg.binOp(loaded, mask_val, int_elem_ty, .@"and");
2362 const extended_value = try func.intcast(rhs, ty, int_elem_ty);2318 const extended_value = try cg.intcast(rhs, ty, int_elem_ty);
2363 const masked_value = try func.binOp(extended_value, wrap_mask_val, int_elem_ty, .@"and");2319 const masked_value = try cg.binOp(extended_value, wrap_mask_val, int_elem_ty, .@"and");
2364 const shifted_value = if (ptr_info.packed_offset.bit_offset > 0) shifted: {2320 const shifted_value = if (ptr_info.packed_offset.bit_offset > 0) shifted: {
2365 break :shifted try func.binOp(masked_value, shift_val, int_elem_ty, .shl);2321 break :shifted try cg.binOp(masked_value, shift_val, int_elem_ty, .shl);
2366 } else masked_value;2322 } else masked_value;
2367 const result = try func.binOp(anded, shifted_value, int_elem_ty, .@"or");2323 const result = try cg.binOp(anded, shifted_value, int_elem_ty, .@"or");
2368 // lhs is still on the stack2324 // lhs is still on the stack
2369 try func.store(.stack, result, int_elem_ty, lhs.offset());2325 try cg.store(.stack, result, int_elem_ty, lhs.offset());
2370 }2326 }
23712327
2372 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });2328 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
2373}2329}
23742330
2375fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {2331fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
2376 assert(!(lhs != .stack and rhs == .stack));2332 assert(!(lhs != .stack and rhs == .stack));
2377 const pt = func.pt;2333 const pt = cg.pt;
2378 const zcu = pt.zcu;2334 const zcu = pt.zcu;
2379 const abi_size = ty.abiSize(zcu);2335 const abi_size = ty.abiSize(zcu);
2380 switch (ty.zigTypeTag(zcu)) {2336 switch (ty.zigTypeTag(zcu)) {
2381 .error_union => {2337 .error_union => {
2382 const pl_ty = ty.errorUnionPayload(zcu);2338 const pl_ty = ty.errorUnionPayload(zcu);
2383 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {2339 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2384 return func.store(lhs, rhs, Type.anyerror, 0);2340 return cg.store(lhs, rhs, Type.anyerror, 0);
2385 }2341 }
23862342
2387 const len = @as(u32, @intCast(abi_size));2343 const len = @as(u32, @intCast(abi_size));
2388 return func.memcpy(lhs, rhs, .{ .imm32 = len });2344 return cg.memcpy(lhs, rhs, .{ .imm32 = len });
2389 },2345 },
2390 .optional => {2346 .optional => {
2391 if (ty.isPtrLikeOptional(zcu)) {2347 if (ty.isPtrLikeOptional(zcu)) {
2392 return func.store(lhs, rhs, Type.usize, 0);2348 return cg.store(lhs, rhs, Type.usize, 0);
2393 }2349 }
2394 const pl_ty = ty.optionalChild(zcu);2350 const pl_ty = ty.optionalChild(zcu);
2395 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {2351 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2396 return func.store(lhs, rhs, Type.u8, 0);2352 return cg.store(lhs, rhs, Type.u8, 0);
2397 }2353 }
2398 if (pl_ty.zigTypeTag(zcu) == .error_set) {2354 if (pl_ty.zigTypeTag(zcu) == .error_set) {
2399 return func.store(lhs, rhs, Type.anyerror, 0);2355 return cg.store(lhs, rhs, Type.anyerror, 0);
2400 }2356 }
24012357
2402 const len = @as(u32, @intCast(abi_size));2358 const len = @as(u32, @intCast(abi_size));
2403 return func.memcpy(lhs, rhs, .{ .imm32 = len });2359 return cg.memcpy(lhs, rhs, .{ .imm32 = len });
2404 },2360 },
2405 .@"struct", .array, .@"union" => if (isByRef(ty, pt, func.target.*)) {2361 .@"struct", .array, .@"union" => if (isByRef(ty, zcu, cg.target)) {
2406 const len = @as(u32, @intCast(abi_size));2362 const len = @as(u32, @intCast(abi_size));
2407 return func.memcpy(lhs, rhs, .{ .imm32 = len });2363 return cg.memcpy(lhs, rhs, .{ .imm32 = len });
2408 },2364 },
2409 .vector => switch (determineSimdStoreStrategy(ty, zcu, func.target.*)) {2365 .vector => switch (determineSimdStoreStrategy(ty, zcu, cg.target)) {
2410 .unrolled => {2366 .unrolled => {
2411 const len: u32 = @intCast(abi_size);2367 const len: u32 = @intCast(abi_size);
2412 return func.memcpy(lhs, rhs, .{ .imm32 = len });2368 return cg.memcpy(lhs, rhs, .{ .imm32 = len });
2413 },2369 },
2414 .direct => {2370 .direct => {
2415 try func.emitWValue(lhs);2371 try cg.emitWValue(lhs);
2416 try func.lowerToStack(rhs);2372 try cg.lowerToStack(rhs);
2417 // TODO: Add helper functions for simd opcodes2373 // TODO: Add helper functions for simd opcodes
2418 const extra_index: u32 = @intCast(func.mir_extra.items.len);2374 const extra_index = cg.extraLen();
2419 // stores as := opcode, offset, alignment (opcode::memarg)2375 // stores as := opcode, offset, alignment (opcode::memarg)
2420 try func.mir_extra.appendSlice(func.gpa, &[_]u32{2376 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
2421 std.wasm.simdOpcode(.v128_store),2377 @intFromEnum(std.wasm.SimdOpcode.v128_store),
2422 offset + lhs.offset(),2378 offset + lhs.offset(),
2423 @intCast(ty.abiAlignment(zcu).toByteUnits() orelse 0),2379 @intCast(ty.abiAlignment(zcu).toByteUnits() orelse 0),
2424 });2380 });
2425 return func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });2381 return cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
2426 },2382 },
2427 },2383 },
2428 .pointer => {2384 .pointer => {
2429 if (ty.isSlice(zcu)) {2385 if (ty.isSlice(zcu)) {
2430 // store pointer first2386 // store pointer first
2431 // lower it to the stack so we do not have to store rhs into a local first2387 // lower it to the stack so we do not have to store rhs into a local first
2432 try func.emitWValue(lhs);2388 try cg.emitWValue(lhs);
2433 const ptr_local = try func.load(rhs, Type.usize, 0);2389 const ptr_local = try cg.load(rhs, Type.usize, 0);
2434 try func.store(.stack, ptr_local, Type.usize, 0 + lhs.offset());2390 try cg.store(.stack, ptr_local, Type.usize, 0 + lhs.offset());
24352391
2436 // retrieve length from rhs, and store that alongside lhs as well2392 // retrieve length from rhs, and store that alongside lhs as well
2437 try func.emitWValue(lhs);2393 try cg.emitWValue(lhs);
2438 const len_local = try func.load(rhs, Type.usize, func.ptrSize());2394 const len_local = try cg.load(rhs, Type.usize, cg.ptrSize());
2439 try func.store(.stack, len_local, Type.usize, func.ptrSize() + lhs.offset());2395 try cg.store(.stack, len_local, Type.usize, cg.ptrSize() + lhs.offset());
2440 return;2396 return;
2441 }2397 }
2442 },2398 },
2443 .int, .@"enum", .float => if (abi_size > 8 and abi_size <= 16) {2399 .int, .@"enum", .float => if (abi_size > 8 and abi_size <= 16) {
2444 try func.emitWValue(lhs);2400 try cg.emitWValue(lhs);
2445 const lsb = try func.load(rhs, Type.u64, 0);2401 const lsb = try cg.load(rhs, Type.u64, 0);
2446 try func.store(.stack, lsb, Type.u64, 0 + lhs.offset());2402 try cg.store(.stack, lsb, Type.u64, 0 + lhs.offset());
24472403
2448 try func.emitWValue(lhs);2404 try cg.emitWValue(lhs);
2449 const msb = try func.load(rhs, Type.u64, 8);2405 const msb = try cg.load(rhs, Type.u64, 8);
2450 try func.store(.stack, msb, Type.u64, 8 + lhs.offset());2406 try cg.store(.stack, msb, Type.u64, 8 + lhs.offset());
2451 return;2407 return;
2452 } else if (abi_size > 16) {2408 } else if (abi_size > 16) {
2453 try func.memcpy(lhs, rhs, .{ .imm32 = @as(u32, @intCast(ty.abiSize(zcu))) });2409 try cg.memcpy(lhs, rhs, .{ .imm32 = @as(u32, @intCast(ty.abiSize(zcu))) });
2454 },2410 },
2455 else => if (abi_size > 8) {2411 else => if (abi_size > 8) {
2456 return func.fail("TODO: `store` for type `{}` with abisize `{d}`", .{2412 return cg.fail("TODO: `store` for type `{}` with abisize `{d}`", .{
2457 ty.fmt(pt),2413 ty.fmt(pt),
2458 abi_size,2414 abi_size,
2459 });2415 });
2460 },2416 },
2461 }2417 }
2462 try func.emitWValue(lhs);2418 try cg.emitWValue(lhs);
2463 // In this case we're actually interested in storing the stack position2419 // In this case we're actually interested in storing the stack position
2464 // into lhs, so we calculate that and emit that instead2420 // into lhs, so we calculate that and emit that instead
2465 try func.lowerToStack(rhs);2421 try cg.lowerToStack(rhs);
24662422
2467 const valtype = typeToValtype(ty, pt, func.target.*);2423 const valtype = typeToValtype(ty, zcu, cg.target);
2468 const opcode = buildOpcode(.{2424 const opcode = buildOpcode(.{
2469 .valtype1 = valtype,2425 .valtype1 = valtype,
2470 .width = @as(u8, @intCast(abi_size * 8)),2426 .width = @as(u8, @intCast(abi_size * 8)),
...@@ -2472,7 +2428,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE...@@ -2472,7 +2428,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
2472 });2428 });
24732429
2474 // store rhs value at stack pointer's location in memory2430 // store rhs value at stack pointer's location in memory
2475 try func.addMemArg(2431 try cg.addMemArg(
2476 Mir.Inst.Tag.fromOpcode(opcode),2432 Mir.Inst.Tag.fromOpcode(opcode),
2477 .{2433 .{
2478 .offset = offset + lhs.offset(),2434 .offset = offset + lhs.offset(),
...@@ -2481,26 +2437,26 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE...@@ -2481,26 +2437,26 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
2481 );2437 );
2482}2438}
24832439
2484fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {2440fn airLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2485 const pt = func.pt;2441 const pt = cg.pt;
2486 const zcu = pt.zcu;2442 const zcu = pt.zcu;
2487 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;2443 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2488 const operand = try func.resolveInst(ty_op.operand);2444 const operand = try cg.resolveInst(ty_op.operand);
2489 const ty = ty_op.ty.toType();2445 const ty = ty_op.ty.toType();
2490 const ptr_ty = func.typeOf(ty_op.operand);2446 const ptr_ty = cg.typeOf(ty_op.operand);
2491 const ptr_info = ptr_ty.ptrInfo(zcu);2447 const ptr_info = ptr_ty.ptrInfo(zcu);
24922448
2493 if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return func.finishAir(inst, .none, &.{ty_op.operand});2449 if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return cg.finishAir(inst, .none, &.{ty_op.operand});
24942450
2495 const result = result: {2451 const result = result: {
2496 if (isByRef(ty, pt, func.target.*)) {2452 if (isByRef(ty, zcu, cg.target)) {
2497 const new_local = try func.allocStack(ty);2453 const new_local = try cg.allocStack(ty);
2498 try func.store(new_local, operand, ty, 0);2454 try cg.store(new_local, operand, ty, 0);
2499 break :result new_local;2455 break :result new_local;
2500 }2456 }
25012457
2502 if (ptr_info.packed_offset.host_size == 0) {2458 if (ptr_info.packed_offset.host_size == 0) {
2503 break :result try func.load(operand, ty, 0);2459 break :result try cg.load(operand, ty, 0);
2504 }2460 }
25052461
2506 // at this point we have a non-natural alignment, we must2462 // at this point we have a non-natural alignment, we must
...@@ -2511,45 +2467,44 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2511,45 +2467,44 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2511 else if (ptr_info.packed_offset.host_size <= 8)2467 else if (ptr_info.packed_offset.host_size <= 8)
2512 .{ .imm64 = ptr_info.packed_offset.bit_offset }2468 .{ .imm64 = ptr_info.packed_offset.bit_offset }
2513 else2469 else
2514 return func.fail("TODO: airLoad where ptr to bitfield exceeds 64 bits", .{});2470 return cg.fail("TODO: airLoad where ptr to bitfield exceeds 64 bits", .{});
25152471
2516 const stack_loaded = try func.load(operand, int_elem_ty, 0);2472 const stack_loaded = try cg.load(operand, int_elem_ty, 0);
2517 const shifted = try func.binOp(stack_loaded, shift_val, int_elem_ty, .shr);2473 const shifted = try cg.binOp(stack_loaded, shift_val, int_elem_ty, .shr);
2518 break :result try func.trunc(shifted, ty, int_elem_ty);2474 break :result try cg.trunc(shifted, ty, int_elem_ty);
2519 };2475 };
2520 return func.finishAir(inst, result, &.{ty_op.operand});2476 return cg.finishAir(inst, result, &.{ty_op.operand});
2521}2477}
25222478
2523/// Loads an operand from the linear memory section.2479/// Loads an operand from the linear memory section.
2524/// NOTE: Leaves the value on the stack.2480/// NOTE: Leaves the value on the stack.
2525fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue {2481fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
2526 const pt = func.pt;2482 const zcu = cg.pt.zcu;
2527 const zcu = pt.zcu;
2528 // load local's value from memory by its stack position2483 // load local's value from memory by its stack position
2529 try func.emitWValue(operand);2484 try cg.emitWValue(operand);
25302485
2531 if (ty.zigTypeTag(zcu) == .vector) {2486 if (ty.zigTypeTag(zcu) == .vector) {
2532 // TODO: Add helper functions for simd opcodes2487 // TODO: Add helper functions for simd opcodes
2533 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));2488 const extra_index = cg.extraLen();
2534 // stores as := opcode, offset, alignment (opcode::memarg)2489 // stores as := opcode, offset, alignment (opcode::memarg)
2535 try func.mir_extra.appendSlice(func.gpa, &[_]u32{2490 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
2536 std.wasm.simdOpcode(.v128_load),2491 @intFromEnum(std.wasm.SimdOpcode.v128_load),
2537 offset + operand.offset(),2492 offset + operand.offset(),
2538 @intCast(ty.abiAlignment(zcu).toByteUnits().?),2493 @intCast(ty.abiAlignment(zcu).toByteUnits().?),
2539 });2494 });
2540 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });2495 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
2541 return .stack;2496 return .stack;
2542 }2497 }
25432498
2544 const abi_size: u8 = @intCast(ty.abiSize(zcu));2499 const abi_size: u8 = @intCast(ty.abiSize(zcu));
2545 const opcode = buildOpcode(.{2500 const opcode = buildOpcode(.{
2546 .valtype1 = typeToValtype(ty, pt, func.target.*),2501 .valtype1 = typeToValtype(ty, zcu, cg.target),
2547 .width = abi_size * 8,2502 .width = abi_size * 8,
2548 .op = .load,2503 .op = .load,
2549 .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned,2504 .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned,
2550 });2505 });
25512506
2552 try func.addMemArg(2507 try cg.addMemArg(
2553 Mir.Inst.Tag.fromOpcode(opcode),2508 Mir.Inst.Tag.fromOpcode(opcode),
2554 .{2509 .{
2555 .offset = offset + operand.offset(),2510 .offset = offset + operand.offset(),
...@@ -2560,18 +2515,18 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu...@@ -2560,18 +2515,18 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
2560 return .stack;2515 return .stack;
2561}2516}
25622517
2563fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {2518fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2564 const pt = func.pt;2519 const pt = cg.pt;
2565 const zcu = pt.zcu;2520 const zcu = pt.zcu;
2566 const arg_index = func.arg_index;2521 const arg_index = cg.arg_index;
2567 const arg = func.args[arg_index];2522 const arg = cg.args[arg_index];
2568 const cc = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?.cc;2523 const cc = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?.cc;
2569 const arg_ty = func.typeOfIndex(inst);2524 const arg_ty = cg.typeOfIndex(inst);
2570 if (cc == .wasm_watc) {2525 if (cc == .wasm_watc) {
2571 const arg_classes = abi.classifyType(arg_ty, zcu);2526 const arg_classes = abi.classifyType(arg_ty, zcu);
2572 for (arg_classes) |class| {2527 for (arg_classes) |class| {
2573 if (class != .none) {2528 if (class != .none) {
2574 func.arg_index += 1;2529 cg.arg_index += 1;
2575 }2530 }
2576 }2531 }
25772532
...@@ -2579,44 +2534,30 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2579,44 +2534,30 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2579 // we combine them into a single stack value2534 // we combine them into a single stack value
2580 if (arg_classes[0] == .direct and arg_classes[1] == .direct) {2535 if (arg_classes[0] == .direct and arg_classes[1] == .direct) {
2581 if (arg_ty.zigTypeTag(zcu) != .int and arg_ty.zigTypeTag(zcu) != .float) {2536 if (arg_ty.zigTypeTag(zcu) != .int and arg_ty.zigTypeTag(zcu) != .float) {
2582 return func.fail(2537 return cg.fail(
2583 "TODO: Implement C-ABI argument for type '{}'",2538 "TODO: Implement C-ABI argument for type '{}'",
2584 .{arg_ty.fmt(pt)},2539 .{arg_ty.fmt(pt)},
2585 );2540 );
2586 }2541 }
2587 const result = try func.allocStack(arg_ty);2542 const result = try cg.allocStack(arg_ty);
2588 try func.store(result, arg, Type.u64, 0);2543 try cg.store(result, arg, Type.u64, 0);
2589 try func.store(result, func.args[arg_index + 1], Type.u64, 8);2544 try cg.store(result, cg.args[arg_index + 1], Type.u64, 8);
2590 return func.finishAir(inst, result, &.{});2545 return cg.finishAir(inst, result, &.{});
2591 }2546 }
2592 } else {2547 } else {
2593 func.arg_index += 1;2548 cg.arg_index += 1;
2594 }
2595
2596 switch (func.debug_output) {
2597 .dwarf => |dwarf| {
2598 const name = func.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
2599 if (name != .none) try dwarf.genLocalDebugInfo(
2600 .local_arg,
2601 name.toSlice(func.air),
2602 arg_ty,
2603 .{ .wasm_ext = .{ .local = arg.local.value } },
2604 );
2605 },
2606 else => {},
2607 }2549 }
26082550
2609 return func.finishAir(inst, arg, &.{});2551 return cg.finishAir(inst, arg, &.{});
2610}2552}
26112553
2612fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {2554fn airBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
2613 const pt = func.pt;2555 const zcu = cg.pt.zcu;
2614 const zcu = pt.zcu;2556 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2615 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2557 const lhs = try cg.resolveInst(bin_op.lhs);
2616 const lhs = try func.resolveInst(bin_op.lhs);2558 const rhs = try cg.resolveInst(bin_op.rhs);
2617 const rhs = try func.resolveInst(bin_op.rhs);2559 const lhs_ty = cg.typeOf(bin_op.lhs);
2618 const lhs_ty = func.typeOf(bin_op.lhs);2560 const rhs_ty = cg.typeOf(bin_op.rhs);
2619 const rhs_ty = func.typeOf(bin_op.rhs);
26202561
2621 // For certain operations, such as shifting, the types are different.2562 // For certain operations, such as shifting, the types are different.
2622 // When converting this to a WebAssembly type, they *must* match to perform2563 // When converting this to a WebAssembly type, they *must* match to perform
...@@ -2626,122 +2567,121 @@ fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {...@@ -2626,122 +2567,121 @@ fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
2626 const result = switch (op) {2567 const result = switch (op) {
2627 .shr, .shl => result: {2568 .shr, .shl => result: {
2628 const lhs_wasm_bits = toWasmBits(@intCast(lhs_ty.bitSize(zcu))) orelse {2569 const lhs_wasm_bits = toWasmBits(@intCast(lhs_ty.bitSize(zcu))) orelse {
2629 return func.fail("TODO: implement '{s}' for types larger than 128 bits", .{@tagName(op)});2570 return cg.fail("TODO: implement '{s}' for types larger than 128 bits", .{@tagName(op)});
2630 };2571 };
2631 const rhs_wasm_bits = toWasmBits(@intCast(rhs_ty.bitSize(zcu))).?;2572 const rhs_wasm_bits = toWasmBits(@intCast(rhs_ty.bitSize(zcu))).?;
2632 const new_rhs = if (lhs_wasm_bits != rhs_wasm_bits and lhs_wasm_bits != 128)2573 const new_rhs = if (lhs_wasm_bits != rhs_wasm_bits and lhs_wasm_bits != 128)
2633 try (try func.intcast(rhs, rhs_ty, lhs_ty)).toLocal(func, lhs_ty)2574 try (try cg.intcast(rhs, rhs_ty, lhs_ty)).toLocal(cg, lhs_ty)
2634 else2575 else
2635 rhs;2576 rhs;
2636 break :result try func.binOp(lhs, new_rhs, lhs_ty, op);2577 break :result try cg.binOp(lhs, new_rhs, lhs_ty, op);
2637 },2578 },
2638 else => try func.binOp(lhs, rhs, lhs_ty, op),2579 else => try cg.binOp(lhs, rhs, lhs_ty, op),
2639 };2580 };
26402581
2641 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });2582 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
2642}2583}
26432584
2644/// Performs a binary operation on the given `WValue`'s2585/// Performs a binary operation on the given `WValue`'s
2645/// NOTE: THis leaves the value on top of the stack.2586/// NOTE: THis leaves the value on top of the stack.
2646fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {2587fn binOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2647 const pt = func.pt;2588 const pt = cg.pt;
2648 const zcu = pt.zcu;2589 const zcu = pt.zcu;
2649 assert(!(lhs != .stack and rhs == .stack));2590 assert(!(lhs != .stack and rhs == .stack));
26502591
2651 if (ty.isAnyFloat()) {2592 if (ty.isAnyFloat()) {
2652 const float_op = FloatOp.fromOp(op);2593 const float_op = FloatOp.fromOp(op);
2653 return func.floatOp(float_op, ty, &.{ lhs, rhs });2594 return cg.floatOp(float_op, ty, &.{ lhs, rhs });
2654 }2595 }
26552596
2656 if (isByRef(ty, pt, func.target.*)) {2597 if (isByRef(ty, zcu, cg.target)) {
2657 if (ty.zigTypeTag(zcu) == .int) {2598 if (ty.zigTypeTag(zcu) == .int) {
2658 return func.binOpBigInt(lhs, rhs, ty, op);2599 return cg.binOpBigInt(lhs, rhs, ty, op);
2659 } else {2600 } else {
2660 return func.fail(2601 return cg.fail(
2661 "TODO: Implement binary operation for type: {}",2602 "TODO: Implement binary operation for type: {}",
2662 .{ty.fmt(pt)},2603 .{ty.fmt(pt)},
2663 );2604 );
2664 }2605 }
2665 }2606 }
26662607
2667 const opcode: wasm.Opcode = buildOpcode(.{2608 const opcode: std.wasm.Opcode = buildOpcode(.{
2668 .op = op,2609 .op = op,
2669 .valtype1 = typeToValtype(ty, pt, func.target.*),2610 .valtype1 = typeToValtype(ty, zcu, cg.target),
2670 .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned,2611 .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned,
2671 });2612 });
2672 try func.emitWValue(lhs);2613 try cg.emitWValue(lhs);
2673 try func.emitWValue(rhs);2614 try cg.emitWValue(rhs);
26742615
2675 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));2616 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
26762617
2677 return .stack;2618 return .stack;
2678}2619}
26792620
2680fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {2621fn binOpBigInt(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2681 const pt = func.pt;2622 const zcu = cg.pt.zcu;
2682 const zcu = pt.zcu;
2683 const int_info = ty.intInfo(zcu);2623 const int_info = ty.intInfo(zcu);
2684 if (int_info.bits > 128) {2624 if (int_info.bits > 128) {
2685 return func.fail("TODO: Implement binary operation for big integers larger than 128 bits", .{});2625 return cg.fail("TODO: Implement binary operation for big integers larger than 128 bits", .{});
2686 }2626 }
26872627
2688 switch (op) {2628 switch (op) {
2689 .mul => return func.callIntrinsic("__multi3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),2629 .mul => return cg.callIntrinsic(.__multi3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2690 .div => switch (int_info.signedness) {2630 .div => switch (int_info.signedness) {
2691 .signed => return func.callIntrinsic("__divti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),2631 .signed => return cg.callIntrinsic(.__divti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2692 .unsigned => return func.callIntrinsic("__udivti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),2632 .unsigned => return cg.callIntrinsic(.__udivti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2693 },2633 },
2694 .rem => switch (int_info.signedness) {2634 .rem => switch (int_info.signedness) {
2695 .signed => return func.callIntrinsic("__modti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),2635 .signed => return cg.callIntrinsic(.__modti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2696 .unsigned => return func.callIntrinsic("__umodti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),2636 .unsigned => return cg.callIntrinsic(.__umodti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2697 },2637 },
2698 .shr => switch (int_info.signedness) {2638 .shr => switch (int_info.signedness) {
2699 .signed => return func.callIntrinsic("__ashrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),2639 .signed => return cg.callIntrinsic(.__ashrti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2700 .unsigned => return func.callIntrinsic("__lshrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),2640 .unsigned => return cg.callIntrinsic(.__lshrti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2701 },2641 },
2702 .shl => return func.callIntrinsic("__ashlti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),2642 .shl => return cg.callIntrinsic(.__ashlti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2703 .@"and", .@"or", .xor => {2643 .@"and", .@"or", .xor => {
2704 const result = try func.allocStack(ty);2644 const result = try cg.allocStack(ty);
2705 try func.emitWValue(result);2645 try cg.emitWValue(result);
2706 const lhs_lsb = try func.load(lhs, Type.u64, 0);2646 const lhs_lsb = try cg.load(lhs, Type.u64, 0);
2707 const rhs_lsb = try func.load(rhs, Type.u64, 0);2647 const rhs_lsb = try cg.load(rhs, Type.u64, 0);
2708 const op_lsb = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op);2648 const op_lsb = try cg.binOp(lhs_lsb, rhs_lsb, Type.u64, op);
2709 try func.store(.stack, op_lsb, Type.u64, result.offset());2649 try cg.store(.stack, op_lsb, Type.u64, result.offset());
27102650
2711 try func.emitWValue(result);2651 try cg.emitWValue(result);
2712 const lhs_msb = try func.load(lhs, Type.u64, 8);2652 const lhs_msb = try cg.load(lhs, Type.u64, 8);
2713 const rhs_msb = try func.load(rhs, Type.u64, 8);2653 const rhs_msb = try cg.load(rhs, Type.u64, 8);
2714 const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op);2654 const op_msb = try cg.binOp(lhs_msb, rhs_msb, Type.u64, op);
2715 try func.store(.stack, op_msb, Type.u64, result.offset() + 8);2655 try cg.store(.stack, op_msb, Type.u64, result.offset() + 8);
2716 return result;2656 return result;
2717 },2657 },
2718 .add, .sub => {2658 .add, .sub => {
2719 const result = try func.allocStack(ty);2659 const result = try cg.allocStack(ty);
2720 var lhs_lsb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);2660 var lhs_lsb = try (try cg.load(lhs, Type.u64, 0)).toLocal(cg, Type.u64);
2721 defer lhs_lsb.free(func);2661 defer lhs_lsb.free(cg);
2722 var rhs_lsb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);2662 var rhs_lsb = try (try cg.load(rhs, Type.u64, 0)).toLocal(cg, Type.u64);
2723 defer rhs_lsb.free(func);2663 defer rhs_lsb.free(cg);
2724 var op_lsb = try (try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op)).toLocal(func, Type.u64);2664 var op_lsb = try (try cg.binOp(lhs_lsb, rhs_lsb, Type.u64, op)).toLocal(cg, Type.u64);
2725 defer op_lsb.free(func);2665 defer op_lsb.free(cg);
27262666
2727 const lhs_msb = try func.load(lhs, Type.u64, 8);2667 const lhs_msb = try cg.load(lhs, Type.u64, 8);
2728 const rhs_msb = try func.load(rhs, Type.u64, 8);2668 const rhs_msb = try cg.load(rhs, Type.u64, 8);
2729 const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op);2669 const op_msb = try cg.binOp(lhs_msb, rhs_msb, Type.u64, op);
27302670
2731 const lt = if (op == .add) blk: {2671 const lt = if (op == .add) blk: {
2732 break :blk try func.cmp(op_lsb, rhs_lsb, Type.u64, .lt);2672 break :blk try cg.cmp(op_lsb, rhs_lsb, Type.u64, .lt);
2733 } else if (op == .sub) blk: {2673 } else if (op == .sub) blk: {
2734 break :blk try func.cmp(lhs_lsb, rhs_lsb, Type.u64, .lt);2674 break :blk try cg.cmp(lhs_lsb, rhs_lsb, Type.u64, .lt);
2735 } else unreachable;2675 } else unreachable;
2736 const tmp = try func.intcast(lt, Type.u32, Type.u64);2676 const tmp = try cg.intcast(lt, Type.u32, Type.u64);
2737 var tmp_op = try (try func.binOp(op_msb, tmp, Type.u64, op)).toLocal(func, Type.u64);2677 var tmp_op = try (try cg.binOp(op_msb, tmp, Type.u64, op)).toLocal(cg, Type.u64);
2738 defer tmp_op.free(func);2678 defer tmp_op.free(cg);
27392679
2740 try func.store(result, op_lsb, Type.u64, 0);2680 try cg.store(result, op_lsb, Type.u64, 0);
2741 try func.store(result, tmp_op, Type.u64, 8);2681 try cg.store(result, tmp_op, Type.u64, 8);
2742 return result;2682 return result;
2743 },2683 },
2744 else => return func.fail("TODO: Implement binary operation for big integers: '{s}'", .{@tagName(op)}),2684 else => return cg.fail("TODO: Implement binary operation for big integers: '{s}'", .{@tagName(op)}),
2745 }2685 }
2746}2686}
27472687
...@@ -2819,199 +2759,214 @@ const FloatOp = enum {...@@ -2819,199 +2759,214 @@ const FloatOp = enum {
2819 => null,2759 => null,
2820 };2760 };
2821 }2761 }
2762
2763 fn intrinsic(op: FloatOp, bits: u16) Mir.Intrinsic {
2764 return switch (op) {
2765 inline .add, .sub, .div, .mul => |ct_op| switch (bits) {
2766 inline 16, 80, 128 => |ct_bits| @field(
2767 Mir.Intrinsic,
2768 "__" ++ @tagName(ct_op) ++ compilerRtFloatAbbrev(ct_bits) ++ "f3",
2769 ),
2770 else => unreachable,
2771 },
2772
2773 inline .ceil,
2774 .fabs,
2775 .floor,
2776 .fmax,
2777 .fmin,
2778 .round,
2779 .sqrt,
2780 .trunc,
2781 => |ct_op| switch (bits) {
2782 inline 16, 80, 128 => |ct_bits| @field(
2783 Mir.Intrinsic,
2784 libcFloatPrefix(ct_bits) ++ @tagName(ct_op) ++ libcFloatSuffix(ct_bits),
2785 ),
2786 else => unreachable,
2787 },
2788
2789 inline .cos,
2790 .exp,
2791 .exp2,
2792 .fma,
2793 .fmod,
2794 .log,
2795 .log10,
2796 .log2,
2797 .sin,
2798 .tan,
2799 => |ct_op| switch (bits) {
2800 inline 16, 32, 64, 80, 128 => |ct_bits| @field(
2801 Mir.Intrinsic,
2802 libcFloatPrefix(ct_bits) ++ @tagName(ct_op) ++ libcFloatSuffix(ct_bits),
2803 ),
2804 else => unreachable,
2805 },
2806
2807 .neg => unreachable,
2808 };
2809 }
2822};2810};
28232811
2824fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {2812fn airAbs(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2825 const pt = func.pt;2813 const pt = cg.pt;
2826 const zcu = pt.zcu;2814 const zcu = pt.zcu;
2827 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;2815 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2828 const operand = try func.resolveInst(ty_op.operand);2816 const operand = try cg.resolveInst(ty_op.operand);
2829 const ty = func.typeOf(ty_op.operand);2817 const ty = cg.typeOf(ty_op.operand);
2830 const scalar_ty = ty.scalarType(zcu);2818 const scalar_ty = ty.scalarType(zcu);
28312819
2832 switch (scalar_ty.zigTypeTag(zcu)) {2820 switch (scalar_ty.zigTypeTag(zcu)) {
2833 .int => if (ty.zigTypeTag(zcu) == .vector) {2821 .int => if (ty.zigTypeTag(zcu) == .vector) {
2834 return func.fail("TODO implement airAbs for {}", .{ty.fmt(pt)});2822 return cg.fail("TODO implement airAbs for {}", .{ty.fmt(pt)});
2835 } else {2823 } else {
2836 const int_bits = ty.intInfo(zcu).bits;2824 const int_bits = ty.intInfo(zcu).bits;
2837 const wasm_bits = toWasmBits(int_bits) orelse {2825 const wasm_bits = toWasmBits(int_bits) orelse {
2838 return func.fail("TODO: airAbs for signed integers larger than '{d}' bits", .{int_bits});2826 return cg.fail("TODO: airAbs for signed integers larger than '{d}' bits", .{int_bits});
2839 };2827 };
28402828
2841 switch (wasm_bits) {2829 switch (wasm_bits) {
2842 32 => {2830 32 => {
2843 try func.emitWValue(operand);2831 try cg.emitWValue(operand);
28442832
2845 try func.addImm32(31);2833 try cg.addImm32(31);
2846 try func.addTag(.i32_shr_s);2834 try cg.addTag(.i32_shr_s);
28472835
2848 var tmp = try func.allocLocal(ty);2836 var tmp = try cg.allocLocal(ty);
2849 defer tmp.free(func);2837 defer tmp.free(cg);
2850 try func.addLabel(.local_tee, tmp.local.value);2838 try cg.addLocal(.local_tee, tmp.local.value);
28512839
2852 try func.emitWValue(operand);2840 try cg.emitWValue(operand);
2853 try func.addTag(.i32_xor);2841 try cg.addTag(.i32_xor);
2854 try func.emitWValue(tmp);2842 try cg.emitWValue(tmp);
2855 try func.addTag(.i32_sub);2843 try cg.addTag(.i32_sub);
2856 return func.finishAir(inst, .stack, &.{ty_op.operand});2844 return cg.finishAir(inst, .stack, &.{ty_op.operand});
2857 },2845 },
2858 64 => {2846 64 => {
2859 try func.emitWValue(operand);2847 try cg.emitWValue(operand);
28602848
2861 try func.addImm64(63);2849 try cg.addImm64(63);
2862 try func.addTag(.i64_shr_s);2850 try cg.addTag(.i64_shr_s);
28632851
2864 var tmp = try func.allocLocal(ty);2852 var tmp = try cg.allocLocal(ty);
2865 defer tmp.free(func);2853 defer tmp.free(cg);
2866 try func.addLabel(.local_tee, tmp.local.value);2854 try cg.addLocal(.local_tee, tmp.local.value);
28672855
2868 try func.emitWValue(operand);2856 try cg.emitWValue(operand);
2869 try func.addTag(.i64_xor);2857 try cg.addTag(.i64_xor);
2870 try func.emitWValue(tmp);2858 try cg.emitWValue(tmp);
2871 try func.addTag(.i64_sub);2859 try cg.addTag(.i64_sub);
2872 return func.finishAir(inst, .stack, &.{ty_op.operand});2860 return cg.finishAir(inst, .stack, &.{ty_op.operand});
2873 },2861 },
2874 128 => {2862 128 => {
2875 const mask = try func.allocStack(Type.u128);2863 const mask = try cg.allocStack(Type.u128);
2876 try func.emitWValue(mask);2864 try cg.emitWValue(mask);
2877 try func.emitWValue(mask);2865 try cg.emitWValue(mask);
28782866
2879 _ = try func.load(operand, Type.u64, 8);2867 _ = try cg.load(operand, Type.u64, 8);
2880 try func.addImm64(63);2868 try cg.addImm64(63);
2881 try func.addTag(.i64_shr_s);2869 try cg.addTag(.i64_shr_s);
28822870
2883 var tmp = try func.allocLocal(Type.u64);2871 var tmp = try cg.allocLocal(Type.u64);
2884 defer tmp.free(func);2872 defer tmp.free(cg);
2885 try func.addLabel(.local_tee, tmp.local.value);2873 try cg.addLocal(.local_tee, tmp.local.value);
2886 try func.store(.stack, .stack, Type.u64, mask.offset() + 0);2874 try cg.store(.stack, .stack, Type.u64, mask.offset() + 0);
2887 try func.emitWValue(tmp);2875 try cg.emitWValue(tmp);
2888 try func.store(.stack, .stack, Type.u64, mask.offset() + 8);2876 try cg.store(.stack, .stack, Type.u64, mask.offset() + 8);
28892877
2890 const a = try func.binOpBigInt(operand, mask, Type.u128, .xor);2878 const a = try cg.binOpBigInt(operand, mask, Type.u128, .xor);
2891 const b = try func.binOpBigInt(a, mask, Type.u128, .sub);2879 const b = try cg.binOpBigInt(a, mask, Type.u128, .sub);
28922880
2893 return func.finishAir(inst, b, &.{ty_op.operand});2881 return cg.finishAir(inst, b, &.{ty_op.operand});
2894 },2882 },
2895 else => unreachable,2883 else => unreachable,
2896 }2884 }
2897 },2885 },
2898 .float => {2886 .float => {
2899 const result = try func.floatOp(.fabs, ty, &.{operand});2887 const result = try cg.floatOp(.fabs, ty, &.{operand});
2900 return func.finishAir(inst, result, &.{ty_op.operand});2888 return cg.finishAir(inst, result, &.{ty_op.operand});
2901 },2889 },
2902 else => unreachable,2890 else => unreachable,
2903 }2891 }
2904}2892}
29052893
2906fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void {2894fn airUnaryFloatOp(cg: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void {
2907 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;2895 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2908 const operand = try func.resolveInst(un_op);2896 const operand = try cg.resolveInst(un_op);
2909 const ty = func.typeOf(un_op);2897 const ty = cg.typeOf(un_op);
29102898
2911 const result = try func.floatOp(op, ty, &.{operand});2899 const result = try cg.floatOp(op, ty, &.{operand});
2912 return func.finishAir(inst, result, &.{un_op});2900 return cg.finishAir(inst, result, &.{un_op});
2913}2901}
29142902
2915fn floatOp(func: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) InnerError!WValue {2903fn floatOp(cg: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) InnerError!WValue {
2916 const pt = func.pt;2904 const zcu = cg.pt.zcu;
2917 const zcu = pt.zcu;
2918 if (ty.zigTypeTag(zcu) == .vector) {2905 if (ty.zigTypeTag(zcu) == .vector) {
2919 return func.fail("TODO: Implement floatOps for vectors", .{});2906 return cg.fail("TODO: Implement floatOps for vectors", .{});
2920 }2907 }
29212908
2922 const float_bits = ty.floatBits(func.target.*);2909 const float_bits = ty.floatBits(cg.target.*);
29232910
2924 if (float_op == .neg) {2911 if (float_op == .neg) {
2925 return func.floatNeg(ty, args[0]);2912 return cg.floatNeg(ty, args[0]);
2926 }2913 }
29272914
2928 if (float_bits == 32 or float_bits == 64) {2915 if (float_bits == 32 or float_bits == 64) {
2929 if (float_op.toOp()) |op| {2916 if (float_op.toOp()) |op| {
2930 for (args) |operand| {2917 for (args) |operand| {
2931 try func.emitWValue(operand);2918 try cg.emitWValue(operand);
2932 }2919 }
2933 const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, pt, func.target.*) });2920 const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, zcu, cg.target) });
2934 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));2921 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
2935 return .stack;2922 return .stack;
2936 }2923 }
2937 }2924 }
29382925
2939 var fn_name_buf: [64]u8 = undefined;2926 const intrinsic = float_op.intrinsic(float_bits);
2940 const fn_name = switch (float_op) {
2941 .add,
2942 .sub,
2943 .div,
2944 .mul,
2945 => std.fmt.bufPrint(&fn_name_buf, "__{s}{s}f3", .{
2946 @tagName(float_op), target_util.compilerRtFloatAbbrev(float_bits),
2947 }) catch unreachable,
2948
2949 .ceil,
2950 .cos,
2951 .exp,
2952 .exp2,
2953 .fabs,
2954 .floor,
2955 .fma,
2956 .fmax,
2957 .fmin,
2958 .fmod,
2959 .log,
2960 .log10,
2961 .log2,
2962 .round,
2963 .sin,
2964 .sqrt,
2965 .tan,
2966 .trunc,
2967 => std.fmt.bufPrint(&fn_name_buf, "{s}{s}{s}", .{
2968 target_util.libcFloatPrefix(float_bits), @tagName(float_op), target_util.libcFloatSuffix(float_bits),
2969 }) catch unreachable,
2970 .neg => unreachable, // handled above
2971 };
29722927
2973 // fma requires three operands2928 // fma requires three operands
2974 var param_types_buffer: [3]InternPool.Index = .{ ty.ip_index, ty.ip_index, ty.ip_index };2929 var param_types_buffer: [3]InternPool.Index = .{ ty.ip_index, ty.ip_index, ty.ip_index };
2975 const param_types = param_types_buffer[0..args.len];2930 const param_types = param_types_buffer[0..args.len];
2976 return func.callIntrinsic(fn_name, param_types, ty, args);2931 return cg.callIntrinsic(intrinsic, param_types, ty, args);
2977}2932}
29782933
2979/// NOTE: The result value remains on top of the stack.2934/// NOTE: The result value remains on top of the stack.
2980fn floatNeg(func: *CodeGen, ty: Type, arg: WValue) InnerError!WValue {2935fn floatNeg(cg: *CodeGen, ty: Type, arg: WValue) InnerError!WValue {
2981 const float_bits = ty.floatBits(func.target.*);2936 const float_bits = ty.floatBits(cg.target.*);
2982 switch (float_bits) {2937 switch (float_bits) {
2983 16 => {2938 16 => {
2984 try func.emitWValue(arg);2939 try cg.emitWValue(arg);
2985 try func.addImm32(0x8000);2940 try cg.addImm32(0x8000);
2986 try func.addTag(.i32_xor);2941 try cg.addTag(.i32_xor);
2987 return .stack;2942 return .stack;
2988 },2943 },
2989 32, 64 => {2944 32, 64 => {
2990 try func.emitWValue(arg);2945 try cg.emitWValue(arg);
2991 const val_type: wasm.Valtype = if (float_bits == 32) .f32 else .f64;2946 const val_type: std.wasm.Valtype = if (float_bits == 32) .f32 else .f64;
2992 const opcode = buildOpcode(.{ .op = .neg, .valtype1 = val_type });2947 const opcode = buildOpcode(.{ .op = .neg, .valtype1 = val_type });
2993 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));2948 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
2994 return .stack;2949 return .stack;
2995 },2950 },
2996 80, 128 => {2951 80, 128 => {
2997 const result = try func.allocStack(ty);2952 const result = try cg.allocStack(ty);
2998 try func.emitWValue(result);2953 try cg.emitWValue(result);
2999 try func.emitWValue(arg);2954 try cg.emitWValue(arg);
3000 try func.addMemArg(.i64_load, .{ .offset = 0 + arg.offset(), .alignment = 2 });2955 try cg.addMemArg(.i64_load, .{ .offset = 0 + arg.offset(), .alignment = 2 });
3001 try func.addMemArg(.i64_store, .{ .offset = 0 + result.offset(), .alignment = 2 });2956 try cg.addMemArg(.i64_store, .{ .offset = 0 + result.offset(), .alignment = 2 });
30022957
3003 try func.emitWValue(result);2958 try cg.emitWValue(result);
3004 try func.emitWValue(arg);2959 try cg.emitWValue(arg);
3005 try func.addMemArg(.i64_load, .{ .offset = 8 + arg.offset(), .alignment = 2 });2960 try cg.addMemArg(.i64_load, .{ .offset = 8 + arg.offset(), .alignment = 2 });
30062961
3007 if (float_bits == 80) {2962 if (float_bits == 80) {
3008 try func.addImm64(0x8000);2963 try cg.addImm64(0x8000);
3009 try func.addTag(.i64_xor);2964 try cg.addTag(.i64_xor);
3010 try func.addMemArg(.i64_store16, .{ .offset = 8 + result.offset(), .alignment = 2 });2965 try cg.addMemArg(.i64_store16, .{ .offset = 8 + result.offset(), .alignment = 2 });
3011 } else {2966 } else {
3012 try func.addImm64(0x8000000000000000);2967 try cg.addImm64(0x8000000000000000);
3013 try func.addTag(.i64_xor);2968 try cg.addTag(.i64_xor);
3014 try func.addMemArg(.i64_store, .{ .offset = 8 + result.offset(), .alignment = 2 });2969 try cg.addMemArg(.i64_store, .{ .offset = 8 + result.offset(), .alignment = 2 });
3015 }2970 }
3016 return result;2971 return result;
3017 },2972 },
...@@ -3019,18 +2974,17 @@ fn floatNeg(func: *CodeGen, ty: Type, arg: WValue) InnerError!WValue {...@@ -3019,18 +2974,17 @@ fn floatNeg(func: *CodeGen, ty: Type, arg: WValue) InnerError!WValue {
3019 }2974 }
3020}2975}
30212976
3022fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {2977fn airWrapBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
3023 const pt = func.pt;2978 const zcu = cg.pt.zcu;
3024 const zcu = pt.zcu;2979 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3025 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
30262980
3027 const lhs = try func.resolveInst(bin_op.lhs);2981 const lhs = try cg.resolveInst(bin_op.lhs);
3028 const rhs = try func.resolveInst(bin_op.rhs);2982 const rhs = try cg.resolveInst(bin_op.rhs);
3029 const lhs_ty = func.typeOf(bin_op.lhs);2983 const lhs_ty = cg.typeOf(bin_op.lhs);
3030 const rhs_ty = func.typeOf(bin_op.rhs);2984 const rhs_ty = cg.typeOf(bin_op.rhs);
30312985
3032 if (lhs_ty.zigTypeTag(zcu) == .vector or rhs_ty.zigTypeTag(zcu) == .vector) {2986 if (lhs_ty.zigTypeTag(zcu) == .vector or rhs_ty.zigTypeTag(zcu) == .vector) {
3033 return func.fail("TODO: Implement wrapping arithmetic for vectors", .{});2987 return cg.fail("TODO: Implement wrapping arithmetic for vectors", .{});
3034 }2988 }
30352989
3036 // For certain operations, such as shifting, the types are different.2990 // For certain operations, such as shifting, the types are different.
...@@ -3041,90 +2995,89 @@ fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {...@@ -3041,90 +2995,89 @@ fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
3041 const result = switch (op) {2995 const result = switch (op) {
3042 .shr, .shl => result: {2996 .shr, .shl => result: {
3043 const lhs_wasm_bits = toWasmBits(@intCast(lhs_ty.bitSize(zcu))) orelse {2997 const lhs_wasm_bits = toWasmBits(@intCast(lhs_ty.bitSize(zcu))) orelse {
3044 return func.fail("TODO: implement '{s}' for types larger than 128 bits", .{@tagName(op)});2998 return cg.fail("TODO: implement '{s}' for types larger than 128 bits", .{@tagName(op)});
3045 };2999 };
3046 const rhs_wasm_bits = toWasmBits(@intCast(rhs_ty.bitSize(zcu))).?;3000 const rhs_wasm_bits = toWasmBits(@intCast(rhs_ty.bitSize(zcu))).?;
3047 const new_rhs = if (lhs_wasm_bits != rhs_wasm_bits and lhs_wasm_bits != 128)3001 const new_rhs = if (lhs_wasm_bits != rhs_wasm_bits and lhs_wasm_bits != 128)
3048 try (try func.intcast(rhs, rhs_ty, lhs_ty)).toLocal(func, lhs_ty)3002 try (try cg.intcast(rhs, rhs_ty, lhs_ty)).toLocal(cg, lhs_ty)
3049 else3003 else
3050 rhs;3004 rhs;
3051 break :result try func.wrapBinOp(lhs, new_rhs, lhs_ty, op);3005 break :result try cg.wrapBinOp(lhs, new_rhs, lhs_ty, op);
3052 },3006 },
3053 else => try func.wrapBinOp(lhs, rhs, lhs_ty, op),3007 else => try cg.wrapBinOp(lhs, rhs, lhs_ty, op),
3054 };3008 };
30553009
3056 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });3010 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
3057}3011}
30583012
3059/// Performs a wrapping binary operation.3013/// Performs a wrapping binary operation.
3060/// Asserts rhs is not a stack value when lhs also isn't.3014/// Asserts rhs is not a stack value when lhs also isn't.
3061/// NOTE: Leaves the result on the stack when its Type is <= 64 bits3015/// NOTE: Leaves the result on the stack when its Type is <= 64 bits
3062fn wrapBinOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {3016fn wrapBinOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
3063 const bin_local = try func.binOp(lhs, rhs, ty, op);3017 const bin_local = try cg.binOp(lhs, rhs, ty, op);
3064 return func.wrapOperand(bin_local, ty);3018 return cg.wrapOperand(bin_local, ty);
3065}3019}
30663020
3067/// Wraps an operand based on a given type's bitsize.3021/// Wraps an operand based on a given type's bitsize.
3068/// Asserts `Type` is <= 128 bits.3022/// Asserts `Type` is <= 128 bits.
3069/// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack, if wrapping was needed.3023/// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack, if wrapping was needed.
3070fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {3024fn wrapOperand(cg: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
3071 const pt = func.pt;3025 const zcu = cg.pt.zcu;
3072 const zcu = pt.zcu;
3073 assert(ty.abiSize(zcu) <= 16);3026 assert(ty.abiSize(zcu) <= 16);
3074 const int_bits: u16 = @intCast(ty.bitSize(zcu)); // TODO use ty.intInfo(zcu).bits3027 const int_bits: u16 = @intCast(ty.bitSize(zcu)); // TODO use ty.intInfo(zcu).bits
3075 const wasm_bits = toWasmBits(int_bits) orelse {3028 const wasm_bits = toWasmBits(int_bits) orelse {
3076 return func.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{int_bits});3029 return cg.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{int_bits});
3077 };3030 };
30783031
3079 if (wasm_bits == int_bits) return operand;3032 if (wasm_bits == int_bits) return operand;
30803033
3081 switch (wasm_bits) {3034 switch (wasm_bits) {
3082 32 => {3035 32 => {
3083 try func.emitWValue(operand);3036 try cg.emitWValue(operand);
3084 if (ty.isSignedInt(zcu)) {3037 if (ty.isSignedInt(zcu)) {
3085 try func.addImm32(32 - int_bits);3038 try cg.addImm32(32 - int_bits);
3086 try func.addTag(.i32_shl);3039 try cg.addTag(.i32_shl);
3087 try func.addImm32(32 - int_bits);3040 try cg.addImm32(32 - int_bits);
3088 try func.addTag(.i32_shr_s);3041 try cg.addTag(.i32_shr_s);
3089 } else {3042 } else {
3090 try func.addImm32(~@as(u32, 0) >> @intCast(32 - int_bits));3043 try cg.addImm32(~@as(u32, 0) >> @intCast(32 - int_bits));
3091 try func.addTag(.i32_and);3044 try cg.addTag(.i32_and);
3092 }3045 }
3093 return .stack;3046 return .stack;
3094 },3047 },
3095 64 => {3048 64 => {
3096 try func.emitWValue(operand);3049 try cg.emitWValue(operand);
3097 if (ty.isSignedInt(zcu)) {3050 if (ty.isSignedInt(zcu)) {
3098 try func.addImm64(64 - int_bits);3051 try cg.addImm64(64 - int_bits);
3099 try func.addTag(.i64_shl);3052 try cg.addTag(.i64_shl);
3100 try func.addImm64(64 - int_bits);3053 try cg.addImm64(64 - int_bits);
3101 try func.addTag(.i64_shr_s);3054 try cg.addTag(.i64_shr_s);
3102 } else {3055 } else {
3103 try func.addImm64(~@as(u64, 0) >> @intCast(64 - int_bits));3056 try cg.addImm64(~@as(u64, 0) >> @intCast(64 - int_bits));
3104 try func.addTag(.i64_and);3057 try cg.addTag(.i64_and);
3105 }3058 }
3106 return .stack;3059 return .stack;
3107 },3060 },
3108 128 => {3061 128 => {
3109 assert(operand != .stack);3062 assert(operand != .stack);
3110 const result = try func.allocStack(ty);3063 const result = try cg.allocStack(ty);
31113064
3112 try func.emitWValue(result);3065 try cg.emitWValue(result);
3113 _ = try func.load(operand, Type.u64, 0);3066 _ = try cg.load(operand, Type.u64, 0);
3114 try func.store(.stack, .stack, Type.u64, result.offset());3067 try cg.store(.stack, .stack, Type.u64, result.offset());
31153068
3116 try func.emitWValue(result);3069 try cg.emitWValue(result);
3117 _ = try func.load(operand, Type.u64, 8);3070 _ = try cg.load(operand, Type.u64, 8);
3118 if (ty.isSignedInt(zcu)) {3071 if (ty.isSignedInt(zcu)) {
3119 try func.addImm64(128 - int_bits);3072 try cg.addImm64(128 - int_bits);
3120 try func.addTag(.i64_shl);3073 try cg.addTag(.i64_shl);
3121 try func.addImm64(128 - int_bits);3074 try cg.addImm64(128 - int_bits);
3122 try func.addTag(.i64_shr_s);3075 try cg.addTag(.i64_shr_s);
3123 } else {3076 } else {
3124 try func.addImm64(~@as(u64, 0) >> @intCast(128 - int_bits));3077 try cg.addImm64(~@as(u64, 0) >> @intCast(128 - int_bits));
3125 try func.addTag(.i64_and);3078 try cg.addTag(.i64_and);
3126 }3079 }
3127 try func.store(.stack, .stack, Type.u64, result.offset() + 8);3080 try cg.store(.stack, .stack, Type.u64, result.offset() + 8);
31283081
3129 return result;3082 return result;
3130 },3083 },
...@@ -3132,17 +3085,17 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {...@@ -3132,17 +3085,17 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
3132 }3085 }
3133}3086}
31343087
3135fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerError!WValue {3088fn lowerPtr(cg: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerError!WValue {
3136 const pt = func.pt;3089 const pt = cg.pt;
3137 const zcu = pt.zcu;3090 const zcu = pt.zcu;
3138 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;3091 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;
3139 const offset: u64 = prev_offset + ptr.byte_offset;3092 const offset: u64 = prev_offset + ptr.byte_offset;
3140 return switch (ptr.base_addr) {3093 return switch (ptr.base_addr) {
3141 .nav => |nav| return func.lowerNavRef(nav, @intCast(offset)),3094 .nav => |nav| return .{ .nav_ref = .{ .nav_index = nav, .offset = @intCast(offset) } },
3142 .uav => |uav| return func.lowerUavRef(uav, @intCast(offset)),3095 .uav => |uav| return .{ .uav_ref = .{ .ip_index = uav.val, .offset = @intCast(offset), .orig_ptr_ty = uav.orig_ty } },
3143 .int => return func.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize),3096 .int => return cg.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize),
3144 .eu_payload => return func.fail("Wasm TODO: lower error union payload pointer", .{}),3097 .eu_payload => return cg.fail("Wasm TODO: lower error union payload pointer", .{}),
3145 .opt_payload => |opt_ptr| return func.lowerPtr(opt_ptr, offset),3098 .opt_payload => |opt_ptr| return cg.lowerPtr(opt_ptr, offset),
3146 .field => |field| {3099 .field => |field| {
3147 const base_ptr = Value.fromInterned(field.base);3100 const base_ptr = Value.fromInterned(field.base);
3148 const base_ty = base_ptr.typeOf(zcu).childType(zcu);3101 const base_ty = base_ptr.typeOf(zcu).childType(zcu);
...@@ -3151,7 +3104,7 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr...@@ -3151,7 +3104,7 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr
3151 assert(base_ty.isSlice(zcu));3104 assert(base_ty.isSlice(zcu));
3152 break :off switch (field.index) {3105 break :off switch (field.index) {
3153 Value.slice_ptr_index => 0,3106 Value.slice_ptr_index => 0,
3154 Value.slice_len_index => @divExact(func.target.ptrBitWidth(), 8),3107 Value.slice_len_index => @divExact(cg.target.ptrBitWidth(), 8),
3155 else => unreachable,3108 else => unreachable,
3156 };3109 };
3157 },3110 },
...@@ -3177,70 +3130,19 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr...@@ -3177,70 +3130,19 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr
3177 },3130 },
3178 else => unreachable,3131 else => unreachable,
3179 };3132 };
3180 return func.lowerPtr(field.base, offset + field_off);3133 return cg.lowerPtr(field.base, offset + field_off);
3181 },3134 },
3182 .arr_elem, .comptime_field, .comptime_alloc => unreachable,3135 .arr_elem, .comptime_field, .comptime_alloc => unreachable,
3183 };3136 };
3184}3137}
31853138
3186fn lowerUavRef(
3187 func: *CodeGen,
3188 uav: InternPool.Key.Ptr.BaseAddr.Uav,
3189 offset: u32,
3190) InnerError!WValue {
3191 const pt = func.pt;
3192 const zcu = pt.zcu;
3193 const ty = Type.fromInterned(zcu.intern_pool.typeOf(uav.val));
3194
3195 const is_fn_body = ty.zigTypeTag(zcu) == .@"fn";
3196 if (!is_fn_body and !ty.hasRuntimeBitsIgnoreComptime(zcu)) {
3197 return .{ .imm32 = 0xaaaaaaaa };
3198 }
3199
3200 const decl_align = zcu.intern_pool.indexToKey(uav.orig_ty).ptr_type.flags.alignment;
3201 const res = try func.bin_file.lowerUav(pt, uav.val, decl_align, func.src_loc);
3202 const target_sym_index = switch (res) {
3203 .mcv => |mcv| mcv.load_symbol,
3204 .fail => |err_msg| {
3205 func.err_msg = err_msg;
3206 return error.CodegenFail;
3207 },
3208 };
3209 if (is_fn_body) {
3210 return .{ .function_index = target_sym_index };
3211 } else if (offset == 0) {
3212 return .{ .memory = target_sym_index };
3213 } else return .{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } };
3214}
3215
3216fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) InnerError!WValue {
3217 const pt = func.pt;
3218 const zcu = pt.zcu;
3219 const ip = &zcu.intern_pool;
3220
3221 const nav_ty = ip.getNav(nav_index).typeOf(ip);
3222 if (!ip.isFunctionType(nav_ty) and !Type.fromInterned(nav_ty).hasRuntimeBitsIgnoreComptime(zcu)) {
3223 return .{ .imm32 = 0xaaaaaaaa };
3224 }
3225
3226 const atom_index = try func.bin_file.getOrCreateAtomForNav(pt, nav_index);
3227 const atom = func.bin_file.getAtom(atom_index);
3228
3229 const target_sym_index = @intFromEnum(atom.sym_index);
3230 if (ip.isFunctionType(nav_ty)) {
3231 return .{ .function_index = target_sym_index };
3232 } else if (offset == 0) {
3233 return .{ .memory = target_sym_index };
3234 } else return .{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } };
3235}
3236
3237/// Asserts that `isByRef` returns `false` for `ty`.3139/// Asserts that `isByRef` returns `false` for `ty`.
3238fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {3140fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3239 const pt = func.pt;3141 const pt = cg.pt;
3240 const zcu = pt.zcu;3142 const zcu = pt.zcu;
3241 assert(!isByRef(ty, pt, func.target.*));3143 assert(!isByRef(ty, zcu, cg.target));
3242 const ip = &zcu.intern_pool;3144 const ip = &zcu.intern_pool;
3243 if (val.isUndefDeep(zcu)) return func.emitUndefined(ty);3145 if (val.isUndefDeep(zcu)) return cg.emitUndefined(ty);
32443146
3245 switch (ip.indexToKey(val.ip_index)) {3147 switch (ip.indexToKey(val.ip_index)) {
3246 .int_type,3148 .int_type,
...@@ -3319,14 +3221,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {...@@ -3319,14 +3221,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3319 const payload_type = ty.errorUnionPayload(zcu);3221 const payload_type = ty.errorUnionPayload(zcu);
3320 if (!payload_type.hasRuntimeBitsIgnoreComptime(zcu)) {3222 if (!payload_type.hasRuntimeBitsIgnoreComptime(zcu)) {
3321 // We use the error type directly as the type.3223 // We use the error type directly as the type.
3322 return func.lowerConstant(err_val, err_ty);3224 return cg.lowerConstant(err_val, err_ty);
3323 }3225 }
33243226
3325 return func.fail("Wasm TODO: lowerConstant error union with non-zero-bit payload type", .{});3227 return cg.fail("Wasm TODO: lowerConstant error union with non-zero-bit payload type", .{});
3326 },3228 },
3327 .enum_tag => |enum_tag| {3229 .enum_tag => |enum_tag| {
3328 const int_tag_ty = ip.typeOf(enum_tag.int);3230 const int_tag_ty = ip.typeOf(enum_tag.int);
3329 return func.lowerConstant(Value.fromInterned(enum_tag.int), Type.fromInterned(int_tag_ty));3231 return cg.lowerConstant(Value.fromInterned(enum_tag.int), Type.fromInterned(int_tag_ty));
3330 },3232 },
3331 .float => |float| switch (float.storage) {3233 .float => |float| switch (float.storage) {
3332 .f16 => |f16_val| return .{ .imm32 = @as(u16, @bitCast(f16_val)) },3234 .f16 => |f16_val| return .{ .imm32 = @as(u16, @bitCast(f16_val)) },
...@@ -3334,18 +3236,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {...@@ -3334,18 +3236,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3334 .f64 => |f64_val| return .{ .float64 = f64_val },3236 .f64 => |f64_val| return .{ .float64 = f64_val },
3335 else => unreachable,3237 else => unreachable,
3336 },3238 },
3337 .slice => switch (try func.bin_file.lowerUav(pt, val.toIntern(), .none, func.src_loc)) {3239 .slice => unreachable, // isByRef == true
3338 .mcv => |mcv| return .{ .memory = mcv.load_symbol },3240 .ptr => return cg.lowerPtr(val.toIntern(), 0),
3339 .fail => |err_msg| {
3340 func.err_msg = err_msg;
3341 return error.CodegenFail;
3342 },
3343 },
3344 .ptr => return func.lowerPtr(val.toIntern(), 0),
3345 .opt => if (ty.optionalReprIsPayload(zcu)) {3241 .opt => if (ty.optionalReprIsPayload(zcu)) {
3346 const pl_ty = ty.optionalChild(zcu);3242 const pl_ty = ty.optionalChild(zcu);
3347 if (val.optionalValue(zcu)) |payload| {3243 if (val.optionalValue(zcu)) |payload| {
3348 return func.lowerConstant(payload, pl_ty);3244 return cg.lowerConstant(payload, pl_ty);
3349 } else {3245 } else {
3350 return .{ .imm32 = 0 };3246 return .{ .imm32 = 0 };
3351 }3247 }
...@@ -3353,12 +3249,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {...@@ -3353,12 +3249,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3353 return .{ .imm32 = @intFromBool(!val.isNull(zcu)) };3249 return .{ .imm32 = @intFromBool(!val.isNull(zcu)) };
3354 },3250 },
3355 .aggregate => switch (ip.indexToKey(ty.ip_index)) {3251 .aggregate => switch (ip.indexToKey(ty.ip_index)) {
3356 .array_type => return func.fail("Wasm TODO: LowerConstant for {}", .{ty.fmt(pt)}),3252 .array_type => return cg.fail("Wasm TODO: LowerConstant for {}", .{ty.fmt(pt)}),
3357 .vector_type => {3253 .vector_type => {
3358 assert(determineSimdStoreStrategy(ty, zcu, func.target.*) == .direct);3254 assert(determineSimdStoreStrategy(ty, zcu, cg.target) == .direct);
3359 var buf: [16]u8 = undefined;3255 var buf: [16]u8 = undefined;
3360 val.writeToMemory(pt, &buf) catch unreachable;3256 val.writeToMemory(pt, &buf) catch unreachable;
3361 return func.storeSimdImmd(buf);3257 return cg.storeSimdImmd(buf);
3362 },3258 },
3363 .struct_type => {3259 .struct_type => {
3364 const struct_type = ip.loadStructType(ty.toIntern());3260 const struct_type = ip.loadStructType(ty.toIntern());
...@@ -3372,7 +3268,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {...@@ -3372,7 +3268,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3372 backing_int_ty,3268 backing_int_ty,
3373 mem.readInt(u64, &buf, .little),3269 mem.readInt(u64, &buf, .little),
3374 );3270 );
3375 return func.lowerConstant(int_val, backing_int_ty);3271 return cg.lowerConstant(int_val, backing_int_ty);
3376 },3272 },
3377 else => unreachable,3273 else => unreachable,
3378 },3274 },
...@@ -3385,7 +3281,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {...@@ -3385,7 +3281,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3385 const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?;3281 const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?;
3386 break :field_ty Type.fromInterned(union_obj.field_types.get(ip)[field_index]);3282 break :field_ty Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
3387 };3283 };
3388 return func.lowerConstant(Value.fromInterned(un.val), constant_ty);3284 return cg.lowerConstant(Value.fromInterned(un.val), constant_ty);
3389 },3285 },
3390 .memoized_call => unreachable,3286 .memoized_call => unreachable,
3391 }3287 }
...@@ -3393,15 +3289,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {...@@ -3393,15 +3289,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
33933289
3394/// Stores the value as a 128bit-immediate value by storing it inside3290/// Stores the value as a 128bit-immediate value by storing it inside
3395/// the list and returning the index into this list as `WValue`.3291/// the list and returning the index into this list as `WValue`.
3396fn storeSimdImmd(func: *CodeGen, value: [16]u8) !WValue {3292fn storeSimdImmd(cg: *CodeGen, value: [16]u8) !WValue {
3397 const index = @as(u32, @intCast(func.simd_immediates.items.len));3293 const index = @as(u32, @intCast(cg.simd_immediates.items.len));
3398 try func.simd_immediates.append(func.gpa, value);3294 try cg.simd_immediates.append(cg.gpa, value);
3399 return .{ .imm128 = index };3295 return .{ .imm128 = index };
3400}3296}
34013297
3402fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {3298fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {
3403 const pt = func.pt;3299 const zcu = cg.pt.zcu;
3404 const zcu = pt.zcu;
3405 const ip = &zcu.intern_pool;3300 const ip = &zcu.intern_pool;
3406 switch (ty.zigTypeTag(zcu)) {3301 switch (ty.zigTypeTag(zcu)) {
3407 .bool, .error_set => return .{ .imm32 = 0xaaaaaaaa },3302 .bool, .error_set => return .{ .imm32 = 0xaaaaaaaa },
...@@ -3410,21 +3305,20 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {...@@ -3410,21 +3305,20 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {
3410 33...64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa },3305 33...64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa },
3411 else => unreachable,3306 else => unreachable,
3412 },3307 },
3413 .float => switch (ty.floatBits(func.target.*)) {3308 .float => switch (ty.floatBits(cg.target.*)) {
3414 16 => return .{ .imm32 = 0xaaaaaaaa },3309 16 => return .{ .imm32 = 0xaaaaaaaa },
3415 32 => return .{ .float32 = @as(f32, @bitCast(@as(u32, 0xaaaaaaaa))) },3310 32 => return .{ .float32 = @as(f32, @bitCast(@as(u32, 0xaaaaaaaa))) },
3416 64 => return .{ .float64 = @as(f64, @bitCast(@as(u64, 0xaaaaaaaaaaaaaaaa))) },3311 64 => return .{ .float64 = @as(f64, @bitCast(@as(u64, 0xaaaaaaaaaaaaaaaa))) },
3417 else => unreachable,3312 else => unreachable,
3418 },3313 },
3419 .pointer => switch (func.arch()) {3314 .pointer => switch (cg.ptr_size) {
3420 .wasm32 => return .{ .imm32 = 0xaaaaaaaa },3315 .wasm32 => return .{ .imm32 = 0xaaaaaaaa },
3421 .wasm64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa },3316 .wasm64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa },
3422 else => unreachable,
3423 },3317 },
3424 .optional => {3318 .optional => {
3425 const pl_ty = ty.optionalChild(zcu);3319 const pl_ty = ty.optionalChild(zcu);
3426 if (ty.optionalReprIsPayload(zcu)) {3320 if (ty.optionalReprIsPayload(zcu)) {
3427 return func.emitUndefined(pl_ty);3321 return cg.emitUndefined(pl_ty);
3428 }3322 }
3429 return .{ .imm32 = 0xaaaaaaaa };3323 return .{ .imm32 = 0xaaaaaaaa };
3430 },3324 },
...@@ -3433,26 +3327,25 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {...@@ -3433,26 +3327,25 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {
3433 },3327 },
3434 .@"struct" => {3328 .@"struct" => {
3435 const packed_struct = zcu.typeToPackedStruct(ty).?;3329 const packed_struct = zcu.typeToPackedStruct(ty).?;
3436 return func.emitUndefined(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)));3330 return cg.emitUndefined(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)));
3437 },3331 },
3438 else => return func.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag(zcu)}),3332 else => return cg.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag(zcu)}),
3439 }3333 }
3440}3334}
34413335
3442/// Returns a `Value` as a signed 32 bit value.3336/// Returns a `Value` as a signed 32 bit value.
3443/// It's illegal to provide a value with a type that cannot be represented3337/// It's illegal to provide a value with a type that cannot be represented
3444/// as an integer value.3338/// as an integer value.
3445fn valueAsI32(func: *const CodeGen, val: Value) i32 {3339fn valueAsI32(cg: *const CodeGen, val: Value) i32 {
3446 const pt = func.pt;3340 const zcu = cg.pt.zcu;
3447 const zcu = pt.zcu;
3448 const ip = &zcu.intern_pool;3341 const ip = &zcu.intern_pool;
34493342
3450 switch (val.toIntern()) {3343 switch (val.toIntern()) {
3451 .bool_true => return 1,3344 .bool_true => return 1,
3452 .bool_false => return 0,3345 .bool_false => return 0,
3453 else => return switch (ip.indexToKey(val.ip_index)) {3346 else => return switch (ip.indexToKey(val.ip_index)) {
3454 .enum_tag => |enum_tag| intIndexAsI32(ip, enum_tag.int, pt),3347 .enum_tag => |enum_tag| intIndexAsI32(ip, enum_tag.int, zcu),
3455 .int => |int| intStorageAsI32(int.storage, pt),3348 .int => |int| intStorageAsI32(int.storage, zcu),
3456 .ptr => |ptr| {3349 .ptr => |ptr| {
3457 assert(ptr.base_addr == .int);3350 assert(ptr.base_addr == .int);
3458 return @intCast(ptr.byte_offset);3351 return @intCast(ptr.byte_offset);
...@@ -3463,12 +3356,11 @@ fn valueAsI32(func: *const CodeGen, val: Value) i32 {...@@ -3463,12 +3356,11 @@ fn valueAsI32(func: *const CodeGen, val: Value) i32 {
3463 }3356 }
3464}3357}
34653358
3466fn intIndexAsI32(ip: *const InternPool, int: InternPool.Index, pt: Zcu.PerThread) i32 {3359fn intIndexAsI32(ip: *const InternPool, int: InternPool.Index, zcu: *const Zcu) i32 {
3467 return intStorageAsI32(ip.indexToKey(int).int.storage, pt);3360 return intStorageAsI32(ip.indexToKey(int).int.storage, zcu);
3468}3361}
34693362
3470fn intStorageAsI32(storage: InternPool.Key.Int.Storage, pt: Zcu.PerThread) i32 {3363fn intStorageAsI32(storage: InternPool.Key.Int.Storage, zcu: *const Zcu) i32 {
3471 const zcu = pt.zcu;
3472 return switch (storage) {3364 return switch (storage) {
3473 .i64 => |x| @as(i32, @intCast(x)),3365 .i64 => |x| @as(i32, @intCast(x)),
3474 .u64 => |x| @as(i32, @bitCast(@as(u32, @intCast(x)))),3366 .u64 => |x| @as(i32, @bitCast(@as(u32, @intCast(x)))),
...@@ -3478,145 +3370,144 @@ fn intStorageAsI32(storage: InternPool.Key.Int.Storage, pt: Zcu.PerThread) i32 {...@@ -3478,145 +3370,144 @@ fn intStorageAsI32(storage: InternPool.Key.Int.Storage, pt: Zcu.PerThread) i32 {
3478 };3370 };
3479}3371}
34803372
3481fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3373fn airBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3482 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3374 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3483 const extra = func.air.extraData(Air.Block, ty_pl.payload);3375 const extra = cg.air.extraData(Air.Block, ty_pl.payload);
3484 try func.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]));3376 try cg.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len]));
3485}3377}
34863378
3487fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void {3379fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void {
3488 const pt = func.pt;3380 const zcu = cg.pt.zcu;
3489 const wasm_block_ty = genBlockType(block_ty, pt, func.target.*);3381 const wasm_block_ty = genBlockType(block_ty, zcu, cg.target);
34903382
3491 // if wasm_block_ty is non-empty, we create a register to store the temporary value3383 // if wasm_block_ty is non-empty, we create a register to store the temporary value
3492 const block_result: WValue = if (wasm_block_ty != wasm.block_empty) blk: {3384 const block_result: WValue = if (wasm_block_ty != .empty) blk: {
3493 const ty: Type = if (isByRef(block_ty, pt, func.target.*)) Type.u32 else block_ty;3385 const ty: Type = if (isByRef(block_ty, zcu, cg.target)) Type.u32 else block_ty;
3494 break :blk try func.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten3386 break :blk try cg.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten
3495 } else .none;3387 } else .none;
34963388
3497 try func.startBlock(.block, wasm.block_empty);3389 try cg.startBlock(.block, .empty);
3498 // Here we set the current block idx, so breaks know the depth to jump3390 // Here we set the current block idx, so breaks know the depth to jump
3499 // to when breaking out.3391 // to when breaking out.
3500 try func.blocks.putNoClobber(func.gpa, inst, .{3392 try cg.blocks.putNoClobber(cg.gpa, inst, .{
3501 .label = func.block_depth,3393 .label = cg.block_depth,
3502 .value = block_result,3394 .value = block_result,
3503 });3395 });
35043396
3505 try func.genBody(body);3397 try cg.genBody(body);
3506 try func.endBlock();3398 try cg.endBlock();
35073399
3508 const liveness = func.liveness.getBlock(inst);3400 const liveness = cg.liveness.getBlock(inst);
3509 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths.len);3401 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, liveness.deaths.len);
35103402
3511 return func.finishAir(inst, block_result, &.{});3403 return cg.finishAir(inst, block_result, &.{});
3512}3404}
35133405
3514/// appends a new wasm block to the code section and increases the `block_depth` by 13406/// appends a new wasm block to the code section and increases the `block_depth` by 1
3515fn startBlock(func: *CodeGen, block_tag: wasm.Opcode, valtype: u8) !void {3407fn startBlock(cg: *CodeGen, block_tag: std.wasm.Opcode, block_type: std.wasm.BlockType) !void {
3516 func.block_depth += 1;3408 cg.block_depth += 1;
3517 try func.addInst(.{3409 try cg.addInst(.{
3518 .tag = Mir.Inst.Tag.fromOpcode(block_tag),3410 .tag = Mir.Inst.Tag.fromOpcode(block_tag),
3519 .data = .{ .block_type = valtype },3411 .data = .{ .block_type = block_type },
3520 });3412 });
3521}3413}
35223414
3523/// Ends the current wasm block and decreases the `block_depth` by 13415/// Ends the current wasm block and decreases the `block_depth` by 1
3524fn endBlock(func: *CodeGen) !void {3416fn endBlock(cg: *CodeGen) !void {
3525 try func.addTag(.end);3417 try cg.addTag(.end);
3526 func.block_depth -= 1;3418 cg.block_depth -= 1;
3527}3419}
35283420
3529fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3421fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3530 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3422 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3531 const loop = func.air.extraData(Air.Block, ty_pl.payload);3423 const loop = cg.air.extraData(Air.Block, ty_pl.payload);
3532 const body: []const Air.Inst.Index = @ptrCast(func.air.extra[loop.end..][0..loop.data.body_len]);3424 const body: []const Air.Inst.Index = @ptrCast(cg.air.extra[loop.end..][0..loop.data.body_len]);
35333425
3534 // result type of loop is always 'noreturn', meaning we can always3426 // result type of loop is always 'noreturn', meaning we can always
3535 // emit the wasm type 'block_empty'.3427 // emit the wasm type 'block_empty'.
3536 try func.startBlock(.loop, wasm.block_empty);3428 try cg.startBlock(.loop, .empty);
35373429
3538 try func.loops.putNoClobber(func.gpa, inst, func.block_depth);3430 try cg.loops.putNoClobber(cg.gpa, inst, cg.block_depth);
3539 defer assert(func.loops.remove(inst));3431 defer assert(cg.loops.remove(inst));
35403432
3541 try func.genBody(body);3433 try cg.genBody(body);
3542 try func.endBlock();3434 try cg.endBlock();
35433435
3544 return func.finishAir(inst, .none, &.{});3436 return cg.finishAir(inst, .none, &.{});
3545}3437}
35463438
3547fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3439fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3548 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;3440 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
3549 const condition = try func.resolveInst(pl_op.operand);3441 const condition = try cg.resolveInst(pl_op.operand);
3550 const extra = func.air.extraData(Air.CondBr, pl_op.payload);3442 const extra = cg.air.extraData(Air.CondBr, pl_op.payload);
3551 const then_body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end..][0..extra.data.then_body_len]);3443 const then_body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end..][0..extra.data.then_body_len]);
3552 const else_body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]);3444 const else_body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]);
3553 const liveness_condbr = func.liveness.getCondBr(inst);3445 const liveness_condbr = cg.liveness.getCondBr(inst);
35543446
3555 // result type is always noreturn, so use `block_empty` as type.3447 // result type is always noreturn, so use `block_empty` as type.
3556 try func.startBlock(.block, wasm.block_empty);3448 try cg.startBlock(.block, .empty);
3557 // emit the conditional value3449 // emit the conditional value
3558 try func.emitWValue(condition);3450 try cg.emitWValue(condition);
35593451
3560 // we inserted the block in front of the condition3452 // we inserted the block in front of the condition
3561 // so now check if condition matches. If not, break outside this block3453 // so now check if condition matches. If not, break outside this block
3562 // and continue with the then codepath3454 // and continue with the then codepath
3563 try func.addLabel(.br_if, 0);3455 try cg.addLabel(.br_if, 0);
35643456
3565 try func.branches.ensureUnusedCapacity(func.gpa, 2);3457 try cg.branches.ensureUnusedCapacity(cg.gpa, 2);
3566 {3458 {
3567 func.branches.appendAssumeCapacity(.{});3459 cg.branches.appendAssumeCapacity(.{});
3568 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @as(u32, @intCast(liveness_condbr.else_deaths.len)));3460 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, @as(u32, @intCast(liveness_condbr.else_deaths.len)));
3569 defer {3461 defer {
3570 var else_stack = func.branches.pop();3462 var else_stack = cg.branches.pop();
3571 else_stack.deinit(func.gpa);3463 else_stack.deinit(cg.gpa);
3572 }3464 }
3573 try func.genBody(else_body);3465 try cg.genBody(else_body);
3574 try func.endBlock();3466 try cg.endBlock();
3575 }3467 }
35763468
3577 // Outer block that matches the condition3469 // Outer block that matches the condition
3578 {3470 {
3579 func.branches.appendAssumeCapacity(.{});3471 cg.branches.appendAssumeCapacity(.{});
3580 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @as(u32, @intCast(liveness_condbr.then_deaths.len)));3472 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, @as(u32, @intCast(liveness_condbr.then_deaths.len)));
3581 defer {3473 defer {
3582 var then_stack = func.branches.pop();3474 var then_stack = cg.branches.pop();
3583 then_stack.deinit(func.gpa);3475 then_stack.deinit(cg.gpa);
3584 }3476 }
3585 try func.genBody(then_body);3477 try cg.genBody(then_body);
3586 }3478 }
35873479
3588 return func.finishAir(inst, .none, &.{});3480 return cg.finishAir(inst, .none, &.{});
3589}3481}
35903482
3591fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {3483fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {
3592 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3484 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
35933485
3594 const lhs = try func.resolveInst(bin_op.lhs);3486 const lhs = try cg.resolveInst(bin_op.lhs);
3595 const rhs = try func.resolveInst(bin_op.rhs);3487 const rhs = try cg.resolveInst(bin_op.rhs);
3596 const operand_ty = func.typeOf(bin_op.lhs);3488 const operand_ty = cg.typeOf(bin_op.lhs);
3597 const result = try func.cmp(lhs, rhs, operand_ty, op);3489 const result = try cg.cmp(lhs, rhs, operand_ty, op);
3598 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });3490 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
3599}3491}
36003492
3601/// Compares two operands.3493/// Compares two operands.
3602/// Asserts rhs is not a stack value when the lhs isn't a stack value either3494/// Asserts rhs is not a stack value when the lhs isn't a stack value either
3603/// NOTE: This leaves the result on top of the stack, rather than a new local.3495/// NOTE: This leaves the result on top of the stack, rather than a new local.
3604fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue {3496fn cmp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue {
3605 assert(!(lhs != .stack and rhs == .stack));3497 assert(!(lhs != .stack and rhs == .stack));
3606 const pt = func.pt;3498 const zcu = cg.pt.zcu;
3607 const zcu = pt.zcu;
3608 if (ty.zigTypeTag(zcu) == .optional and !ty.optionalReprIsPayload(zcu)) {3499 if (ty.zigTypeTag(zcu) == .optional and !ty.optionalReprIsPayload(zcu)) {
3609 const payload_ty = ty.optionalChild(zcu);3500 const payload_ty = ty.optionalChild(zcu);
3610 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {3501 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
3611 // When we hit this case, we must check the value of optionals3502 // When we hit this case, we must check the value of optionals
3612 // that are not pointers. This means first checking against non-null for3503 // that are not pointers. This means first checking against non-null for
3613 // both lhs and rhs, as well as checking the payload are matching of lhs and rhs3504 // both lhs and rhs, as well as checking the payload are matching of lhs and rhs
3614 return func.cmpOptionals(lhs, rhs, ty, op);3505 return cg.cmpOptionals(lhs, rhs, ty, op);
3615 }3506 }
3616 } else if (ty.isAnyFloat()) {3507 } else if (ty.isAnyFloat()) {
3617 return func.cmpFloat(ty, lhs, rhs, op);3508 return cg.cmpFloat(ty, lhs, rhs, op);
3618 } else if (isByRef(ty, pt, func.target.*)) {3509 } else if (isByRef(ty, zcu, cg.target)) {
3619 return func.cmpBigInt(lhs, rhs, ty, op);3510 return cg.cmpBigInt(lhs, rhs, ty, op);
3620 }3511 }
36213512
3622 const signedness: std.builtin.Signedness = blk: {3513 const signedness: std.builtin.Signedness = blk: {
...@@ -3629,11 +3520,11 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO...@@ -3629,11 +3520,11 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO
36293520
3630 // ensure that when we compare pointers, we emit3521 // ensure that when we compare pointers, we emit
3631 // the true pointer of a stack value, rather than the stack pointer.3522 // the true pointer of a stack value, rather than the stack pointer.
3632 try func.lowerToStack(lhs);3523 try cg.lowerToStack(lhs);
3633 try func.lowerToStack(rhs);3524 try cg.lowerToStack(rhs);
36343525
3635 const opcode: wasm.Opcode = buildOpcode(.{3526 const opcode: std.wasm.Opcode = buildOpcode(.{
3636 .valtype1 = typeToValtype(ty, pt, func.target.*),3527 .valtype1 = typeToValtype(ty, zcu, cg.target),
3637 .op = switch (op) {3528 .op = switch (op) {
3638 .lt => .lt,3529 .lt => .lt,
3639 .lte => .le,3530 .lte => .le,
...@@ -3644,15 +3535,15 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO...@@ -3644,15 +3535,15 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO
3644 },3535 },
3645 .signedness = signedness,3536 .signedness = signedness,
3646 });3537 });
3647 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));3538 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
36483539
3649 return .stack;3540 return .stack;
3650}3541}
36513542
3652/// Compares two floats.3543/// Compares two floats.
3653/// NOTE: Leaves the result of the comparison on top of the stack.3544/// NOTE: Leaves the result of the comparison on top of the stack.
3654fn cmpFloat(func: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math.CompareOperator) InnerError!WValue {3545fn cmpFloat(cg: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math.CompareOperator) InnerError!WValue {
3655 const float_bits = ty.floatBits(func.target.*);3546 const float_bits = ty.floatBits(cg.target.*);
36563547
3657 const op: Op = switch (cmp_op) {3548 const op: Op = switch (cmp_op) {
3658 .lt => .lt,3549 .lt => .lt,
...@@ -3665,143 +3556,137 @@ fn cmpFloat(func: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math...@@ -3665,143 +3556,137 @@ fn cmpFloat(func: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math
36653556
3666 switch (float_bits) {3557 switch (float_bits) {
3667 16 => {3558 16 => {
3668 _ = try func.fpext(lhs, Type.f16, Type.f32);3559 _ = try cg.fpext(lhs, Type.f16, Type.f32);
3669 _ = try func.fpext(rhs, Type.f16, Type.f32);3560 _ = try cg.fpext(rhs, Type.f16, Type.f32);
3670 const opcode = buildOpcode(.{ .op = op, .valtype1 = .f32 });3561 const opcode = buildOpcode(.{ .op = op, .valtype1 = .f32 });
3671 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));3562 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3672 return .stack;3563 return .stack;
3673 },3564 },
3674 32, 64 => {3565 32, 64 => {
3675 try func.emitWValue(lhs);3566 try cg.emitWValue(lhs);
3676 try func.emitWValue(rhs);3567 try cg.emitWValue(rhs);
3677 const val_type: wasm.Valtype = if (float_bits == 32) .f32 else .f64;3568 const val_type: std.wasm.Valtype = if (float_bits == 32) .f32 else .f64;
3678 const opcode = buildOpcode(.{ .op = op, .valtype1 = val_type });3569 const opcode = buildOpcode(.{ .op = op, .valtype1 = val_type });
3679 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));3570 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3680 return .stack;3571 return .stack;
3681 },3572 },
3682 80, 128 => {3573 80, 128 => {
3683 var fn_name_buf: [32]u8 = undefined;3574 const intrinsic = floatCmpIntrinsic(cmp_op, float_bits);
3684 const fn_name = std.fmt.bufPrint(&fn_name_buf, "__{s}{s}f2", .{3575 const result = try cg.callIntrinsic(intrinsic, &.{ ty.ip_index, ty.ip_index }, Type.bool, &.{ lhs, rhs });
3685 @tagName(op), target_util.compilerRtFloatAbbrev(float_bits),3576 return cg.cmp(result, .{ .imm32 = 0 }, Type.i32, cmp_op);
3686 }) catch unreachable;
3687
3688 const result = try func.callIntrinsic(fn_name, &.{ ty.ip_index, ty.ip_index }, Type.bool, &.{ lhs, rhs });
3689 return func.cmp(result, .{ .imm32 = 0 }, Type.i32, cmp_op);
3690 },3577 },
3691 else => unreachable,3578 else => unreachable,
3692 }3579 }
3693}3580}
36943581
3695fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3582fn airCmpVector(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3696 _ = inst;3583 _ = inst;
3697 return func.fail("TODO implement airCmpVector for wasm", .{});3584 return cg.fail("TODO implement airCmpVector for wasm", .{});
3698}3585}
36993586
3700fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3587fn airCmpLtErrorsLen(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3701 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;3588 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3702 const operand = try func.resolveInst(un_op);3589 const operand = try cg.resolveInst(un_op);
3703 const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null);
3704 const errors_len: WValue = .{ .memory = @intFromEnum(sym_index) };
37053590
3706 try func.emitWValue(operand);3591 try cg.emitWValue(operand);
3707 const pt = func.pt;3592 const pt = cg.pt;
3708 const err_int_ty = try pt.errorIntType();3593 const err_int_ty = try pt.errorIntType();
3709 const errors_len_val = try func.load(errors_len, err_int_ty, 0);3594 try cg.addTag(.errors_len);
3710 const result = try func.cmp(.stack, errors_len_val, err_int_ty, .lt);3595 const result = try cg.cmp(.stack, .stack, err_int_ty, .lt);
37113596
3712 return func.finishAir(inst, result, &.{un_op});3597 return cg.finishAir(inst, result, &.{un_op});
3713}3598}
37143599
3715fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3600fn airBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3716 const zcu = func.pt.zcu;3601 const zcu = cg.pt.zcu;
3717 const br = func.air.instructions.items(.data)[@intFromEnum(inst)].br;3602 const br = cg.air.instructions.items(.data)[@intFromEnum(inst)].br;
3718 const block = func.blocks.get(br.block_inst).?;3603 const block = cg.blocks.get(br.block_inst).?;
37193604
3720 // if operand has codegen bits we should break with a value3605 // if operand has codegen bits we should break with a value
3721 if (func.typeOf(br.operand).hasRuntimeBitsIgnoreComptime(zcu)) {3606 if (cg.typeOf(br.operand).hasRuntimeBitsIgnoreComptime(zcu)) {
3722 const operand = try func.resolveInst(br.operand);3607 const operand = try cg.resolveInst(br.operand);
3723 try func.lowerToStack(operand);3608 try cg.lowerToStack(operand);
37243609
3725 if (block.value != .none) {3610 if (block.value != .none) {
3726 try func.addLabel(.local_set, block.value.local.value);3611 try cg.addLocal(.local_set, block.value.local.value);
3727 }3612 }
3728 }3613 }
37293614
3730 // We map every block to its block index.3615 // We map every block to its block index.
3731 // We then determine how far we have to jump to it by subtracting it from current block depth3616 // We then determine how far we have to jump to it by subtracting it from current block depth
3732 const idx: u32 = func.block_depth - block.label;3617 const idx: u32 = cg.block_depth - block.label;
3733 try func.addLabel(.br, idx);3618 try cg.addLabel(.br, idx);
37343619
3735 return func.finishAir(inst, .none, &.{br.operand});3620 return cg.finishAir(inst, .none, &.{br.operand});
3736}3621}
37373622
3738fn airRepeat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3623fn airRepeat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3739 const repeat = func.air.instructions.items(.data)[@intFromEnum(inst)].repeat;3624 const repeat = cg.air.instructions.items(.data)[@intFromEnum(inst)].repeat;
3740 const loop_label = func.loops.get(repeat.loop_inst).?;3625 const loop_label = cg.loops.get(repeat.loop_inst).?;
37413626
3742 const idx: u32 = func.block_depth - loop_label;3627 const idx: u32 = cg.block_depth - loop_label;
3743 try func.addLabel(.br, idx);3628 try cg.addLabel(.br, idx);
37443629
3745 return func.finishAir(inst, .none, &.{});3630 return cg.finishAir(inst, .none, &.{});
3746}3631}
37473632
3748fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3633fn airNot(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3749 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3634 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
37503635
3751 const operand = try func.resolveInst(ty_op.operand);3636 const operand = try cg.resolveInst(ty_op.operand);
3752 const operand_ty = func.typeOf(ty_op.operand);3637 const operand_ty = cg.typeOf(ty_op.operand);
3753 const pt = func.pt;3638 const pt = cg.pt;
3754 const zcu = pt.zcu;3639 const zcu = pt.zcu;
37553640
3756 const result = result: {3641 const result = result: {
3757 if (operand_ty.zigTypeTag(zcu) == .bool) {3642 if (operand_ty.zigTypeTag(zcu) == .bool) {
3758 try func.emitWValue(operand);3643 try cg.emitWValue(operand);
3759 try func.addTag(.i32_eqz);3644 try cg.addTag(.i32_eqz);
3760 const not_tmp = try func.allocLocal(operand_ty);3645 const not_tmp = try cg.allocLocal(operand_ty);
3761 try func.addLabel(.local_set, not_tmp.local.value);3646 try cg.addLocal(.local_set, not_tmp.local.value);
3762 break :result not_tmp;3647 break :result not_tmp;
3763 } else {3648 } else {
3764 const int_info = operand_ty.intInfo(zcu);3649 const int_info = operand_ty.intInfo(zcu);
3765 const wasm_bits = toWasmBits(int_info.bits) orelse {3650 const wasm_bits = toWasmBits(int_info.bits) orelse {
3766 return func.fail("TODO: Implement binary NOT for {}", .{operand_ty.fmt(pt)});3651 return cg.fail("TODO: Implement binary NOT for {}", .{operand_ty.fmt(pt)});
3767 };3652 };
37683653
3769 switch (wasm_bits) {3654 switch (wasm_bits) {
3770 32 => {3655 32 => {
3771 try func.emitWValue(operand);3656 try cg.emitWValue(operand);
3772 try func.addImm32(switch (int_info.signedness) {3657 try cg.addImm32(switch (int_info.signedness) {
3773 .unsigned => ~@as(u32, 0) >> @intCast(32 - int_info.bits),3658 .unsigned => ~@as(u32, 0) >> @intCast(32 - int_info.bits),
3774 .signed => ~@as(u32, 0),3659 .signed => ~@as(u32, 0),
3775 });3660 });
3776 try func.addTag(.i32_xor);3661 try cg.addTag(.i32_xor);
3777 break :result .stack;3662 break :result .stack;
3778 },3663 },
3779 64 => {3664 64 => {
3780 try func.emitWValue(operand);3665 try cg.emitWValue(operand);
3781 try func.addImm64(switch (int_info.signedness) {3666 try cg.addImm64(switch (int_info.signedness) {
3782 .unsigned => ~@as(u64, 0) >> @intCast(64 - int_info.bits),3667 .unsigned => ~@as(u64, 0) >> @intCast(64 - int_info.bits),
3783 .signed => ~@as(u64, 0),3668 .signed => ~@as(u64, 0),
3784 });3669 });
3785 try func.addTag(.i64_xor);3670 try cg.addTag(.i64_xor);
3786 break :result .stack;3671 break :result .stack;
3787 },3672 },
3788 128 => {3673 128 => {
3789 const ptr = try func.allocStack(operand_ty);3674 const ptr = try cg.allocStack(operand_ty);
37903675
3791 try func.emitWValue(ptr);3676 try cg.emitWValue(ptr);
3792 _ = try func.load(operand, Type.u64, 0);3677 _ = try cg.load(operand, Type.u64, 0);
3793 try func.addImm64(~@as(u64, 0));3678 try cg.addImm64(~@as(u64, 0));
3794 try func.addTag(.i64_xor);3679 try cg.addTag(.i64_xor);
3795 try func.store(.stack, .stack, Type.u64, ptr.offset());3680 try cg.store(.stack, .stack, Type.u64, ptr.offset());
37963681
3797 try func.emitWValue(ptr);3682 try cg.emitWValue(ptr);
3798 _ = try func.load(operand, Type.u64, 8);3683 _ = try cg.load(operand, Type.u64, 8);
3799 try func.addImm64(switch (int_info.signedness) {3684 try cg.addImm64(switch (int_info.signedness) {
3800 .unsigned => ~@as(u64, 0) >> @intCast(128 - int_info.bits),3685 .unsigned => ~@as(u64, 0) >> @intCast(128 - int_info.bits),
3801 .signed => ~@as(u64, 0),3686 .signed => ~@as(u64, 0),
3802 });3687 });
3803 try func.addTag(.i64_xor);3688 try cg.addTag(.i64_xor);
3804 try func.store(.stack, .stack, Type.u64, ptr.offset() + 8);3689 try cg.store(.stack, .stack, Type.u64, ptr.offset() + 8);
38053690
3806 break :result ptr;3691 break :result ptr;
3807 },3692 },
...@@ -3809,33 +3694,32 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3809,33 +3694,32 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3809 }3694 }
3810 }3695 }
3811 };3696 };
3812 return func.finishAir(inst, result, &.{ty_op.operand});3697 return cg.finishAir(inst, result, &.{ty_op.operand});
3813}3698}
38143699
3815fn airTrap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3700fn airTrap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3816 try func.addTag(.@"unreachable");3701 try cg.addTag(.@"unreachable");
3817 return func.finishAir(inst, .none, &.{});3702 return cg.finishAir(inst, .none, &.{});
3818}3703}
38193704
3820fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3705fn airBreakpoint(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3821 // unsupported by wasm itfunc. Can be implemented once we support DWARF3706 // unsupported by wasm itfunc. Can be implemented once we support DWARF
3822 // for wasm3707 // for wasm
3823 try func.addTag(.@"unreachable");3708 try cg.addTag(.@"unreachable");
3824 return func.finishAir(inst, .none, &.{});3709 return cg.finishAir(inst, .none, &.{});
3825}3710}
38263711
3827fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3712fn airUnreachable(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3828 try func.addTag(.@"unreachable");3713 try cg.addTag(.@"unreachable");
3829 return func.finishAir(inst, .none, &.{});3714 return cg.finishAir(inst, .none, &.{});
3830}3715}
38313716
3832fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3717fn airBitcast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3833 const pt = func.pt;3718 const zcu = cg.pt.zcu;
3834 const zcu = pt.zcu;3719 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3835 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3720 const operand = try cg.resolveInst(ty_op.operand);
3836 const operand = try func.resolveInst(ty_op.operand);3721 const wanted_ty = cg.typeOfIndex(inst);
3837 const wanted_ty = func.typeOfIndex(inst);3722 const given_ty = cg.typeOf(ty_op.operand);
3838 const given_ty = func.typeOf(ty_op.operand);
38393723
3840 const bit_size = given_ty.bitSize(zcu);3724 const bit_size = given_ty.bitSize(zcu);
3841 const needs_wrapping = (given_ty.isSignedInt(zcu) != wanted_ty.isSignedInt(zcu)) and3725 const needs_wrapping = (given_ty.isSignedInt(zcu) != wanted_ty.isSignedInt(zcu)) and
...@@ -3843,39 +3727,38 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3843,39 +3727,38 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
38433727
3844 const result = result: {3728 const result = result: {
3845 if (given_ty.isAnyFloat() or wanted_ty.isAnyFloat()) {3729 if (given_ty.isAnyFloat() or wanted_ty.isAnyFloat()) {
3846 break :result try func.bitcast(wanted_ty, given_ty, operand);3730 break :result try cg.bitcast(wanted_ty, given_ty, operand);
3847 }3731 }
38483732
3849 if (isByRef(given_ty, pt, func.target.*) and !isByRef(wanted_ty, pt, func.target.*)) {3733 if (isByRef(given_ty, zcu, cg.target) and !isByRef(wanted_ty, zcu, cg.target)) {
3850 const loaded_memory = try func.load(operand, wanted_ty, 0);3734 const loaded_memory = try cg.load(operand, wanted_ty, 0);
3851 if (needs_wrapping) {3735 if (needs_wrapping) {
3852 break :result try func.wrapOperand(loaded_memory, wanted_ty);3736 break :result try cg.wrapOperand(loaded_memory, wanted_ty);
3853 } else {3737 } else {
3854 break :result loaded_memory;3738 break :result loaded_memory;
3855 }3739 }
3856 }3740 }
3857 if (!isByRef(given_ty, pt, func.target.*) and isByRef(wanted_ty, pt, func.target.*)) {3741 if (!isByRef(given_ty, zcu, cg.target) and isByRef(wanted_ty, zcu, cg.target)) {
3858 const stack_memory = try func.allocStack(wanted_ty);3742 const stack_memory = try cg.allocStack(wanted_ty);
3859 try func.store(stack_memory, operand, given_ty, 0);3743 try cg.store(stack_memory, operand, given_ty, 0);
3860 if (needs_wrapping) {3744 if (needs_wrapping) {
3861 break :result try func.wrapOperand(stack_memory, wanted_ty);3745 break :result try cg.wrapOperand(stack_memory, wanted_ty);
3862 } else {3746 } else {
3863 break :result stack_memory;3747 break :result stack_memory;
3864 }3748 }
3865 }3749 }
38663750
3867 if (needs_wrapping) {3751 if (needs_wrapping) {
3868 break :result try func.wrapOperand(operand, wanted_ty);3752 break :result try cg.wrapOperand(operand, wanted_ty);
3869 }3753 }
38703754
3871 break :result func.reuseOperand(ty_op.operand, operand);3755 break :result cg.reuseOperand(ty_op.operand, operand);
3872 };3756 };
3873 return func.finishAir(inst, result, &.{ty_op.operand});3757 return cg.finishAir(inst, result, &.{ty_op.operand});
3874}3758}
38753759
3876fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue {3760fn bitcast(cg: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue {
3877 const pt = func.pt;3761 const zcu = cg.pt.zcu;
3878 const zcu = pt.zcu;
3879 // if we bitcast a float to or from an integer we must use the 'reinterpret' instruction3762 // if we bitcast a float to or from an integer we must use the 'reinterpret' instruction
3880 if (!(wanted_ty.isAnyFloat() or given_ty.isAnyFloat())) return operand;3763 if (!(wanted_ty.isAnyFloat() or given_ty.isAnyFloat())) return operand;
3881 if (wanted_ty.ip_index == .f16_type or given_ty.ip_index == .f16_type) return operand;3764 if (wanted_ty.ip_index == .f16_type or given_ty.ip_index == .f16_type) return operand;
...@@ -3884,41 +3767,39 @@ fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) Inn...@@ -3884,41 +3767,39 @@ fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) Inn
38843767
3885 const opcode = buildOpcode(.{3768 const opcode = buildOpcode(.{
3886 .op = .reinterpret,3769 .op = .reinterpret,
3887 .valtype1 = typeToValtype(wanted_ty, pt, func.target.*),3770 .valtype1 = typeToValtype(wanted_ty, zcu, cg.target),
3888 .valtype2 = typeToValtype(given_ty, pt, func.target.*),3771 .valtype2 = typeToValtype(given_ty, zcu, cg.target),
3889 });3772 });
3890 try func.emitWValue(operand);3773 try cg.emitWValue(operand);
3891 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));3774 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3892 return .stack;3775 return .stack;
3893}3776}
38943777
3895fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3778fn airStructFieldPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3896 const pt = func.pt;3779 const zcu = cg.pt.zcu;
3897 const zcu = pt.zcu;3780 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3898 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3781 const extra = cg.air.extraData(Air.StructField, ty_pl.payload);
3899 const extra = func.air.extraData(Air.StructField, ty_pl.payload);
39003782
3901 const struct_ptr = try func.resolveInst(extra.data.struct_operand);3783 const struct_ptr = try cg.resolveInst(extra.data.struct_operand);
3902 const struct_ptr_ty = func.typeOf(extra.data.struct_operand);3784 const struct_ptr_ty = cg.typeOf(extra.data.struct_operand);
3903 const struct_ty = struct_ptr_ty.childType(zcu);3785 const struct_ty = struct_ptr_ty.childType(zcu);
3904 const result = try func.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ptr_ty, struct_ty, extra.data.field_index);3786 const result = try cg.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ptr_ty, struct_ty, extra.data.field_index);
3905 return func.finishAir(inst, result, &.{extra.data.struct_operand});3787 return cg.finishAir(inst, result, &.{extra.data.struct_operand});
3906}3788}
39073789
3908fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void {3790fn airStructFieldPtrIndex(cg: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void {
3909 const pt = func.pt;3791 const zcu = cg.pt.zcu;
3910 const zcu = pt.zcu;3792 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3911 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3793 const struct_ptr = try cg.resolveInst(ty_op.operand);
3912 const struct_ptr = try func.resolveInst(ty_op.operand);3794 const struct_ptr_ty = cg.typeOf(ty_op.operand);
3913 const struct_ptr_ty = func.typeOf(ty_op.operand);
3914 const struct_ty = struct_ptr_ty.childType(zcu);3795 const struct_ty = struct_ptr_ty.childType(zcu);
39153796
3916 const result = try func.structFieldPtr(inst, ty_op.operand, struct_ptr, struct_ptr_ty, struct_ty, index);3797 const result = try cg.structFieldPtr(inst, ty_op.operand, struct_ptr, struct_ptr_ty, struct_ty, index);
3917 return func.finishAir(inst, result, &.{ty_op.operand});3798 return cg.finishAir(inst, result, &.{ty_op.operand});
3918}3799}
39193800
3920fn structFieldPtr(3801fn structFieldPtr(
3921 func: *CodeGen,3802 cg: *CodeGen,
3922 inst: Air.Inst.Index,3803 inst: Air.Inst.Index,
3923 ref: Air.Inst.Ref,3804 ref: Air.Inst.Ref,
3924 struct_ptr: WValue,3805 struct_ptr: WValue,
...@@ -3926,9 +3807,9 @@ fn structFieldPtr(...@@ -3926,9 +3807,9 @@ fn structFieldPtr(
3926 struct_ty: Type,3807 struct_ty: Type,
3927 index: u32,3808 index: u32,
3928) InnerError!WValue {3809) InnerError!WValue {
3929 const pt = func.pt;3810 const pt = cg.pt;
3930 const zcu = pt.zcu;3811 const zcu = pt.zcu;
3931 const result_ty = func.typeOfIndex(inst);3812 const result_ty = cg.typeOfIndex(inst);
3932 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu);3813 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu);
39333814
3934 const offset = switch (struct_ty.containerLayout(zcu)) {3815 const offset = switch (struct_ty.containerLayout(zcu)) {
...@@ -3947,28 +3828,28 @@ fn structFieldPtr(...@@ -3947,28 +3828,28 @@ fn structFieldPtr(
3947 };3828 };
3948 // save a load and store when we can simply reuse the operand3829 // save a load and store when we can simply reuse the operand
3949 if (offset == 0) {3830 if (offset == 0) {
3950 return func.reuseOperand(ref, struct_ptr);3831 return cg.reuseOperand(ref, struct_ptr);
3951 }3832 }
3952 switch (struct_ptr) {3833 switch (struct_ptr) {
3953 .stack_offset => |stack_offset| {3834 .stack_offset => |stack_offset| {
3954 return .{ .stack_offset = .{ .value = stack_offset.value + @as(u32, @intCast(offset)), .references = 1 } };3835 return .{ .stack_offset = .{ .value = stack_offset.value + @as(u32, @intCast(offset)), .references = 1 } };
3955 },3836 },
3956 else => return func.buildPointerOffset(struct_ptr, offset, .new),3837 else => return cg.buildPointerOffset(struct_ptr, offset, .new),
3957 }3838 }
3958}3839}
39593840
3960fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3841fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3961 const pt = func.pt;3842 const pt = cg.pt;
3962 const zcu = pt.zcu;3843 const zcu = pt.zcu;
3963 const ip = &zcu.intern_pool;3844 const ip = &zcu.intern_pool;
3964 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3845 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3965 const struct_field = func.air.extraData(Air.StructField, ty_pl.payload).data;3846 const struct_field = cg.air.extraData(Air.StructField, ty_pl.payload).data;
39663847
3967 const struct_ty = func.typeOf(struct_field.struct_operand);3848 const struct_ty = cg.typeOf(struct_field.struct_operand);
3968 const operand = try func.resolveInst(struct_field.struct_operand);3849 const operand = try cg.resolveInst(struct_field.struct_operand);
3969 const field_index = struct_field.field_index;3850 const field_index = struct_field.field_index;
3970 const field_ty = struct_ty.fieldType(field_index, zcu);3851 const field_ty = struct_ty.fieldType(field_index, zcu);
3971 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) return func.finishAir(inst, .none, &.{struct_field.struct_operand});3852 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) return cg.finishAir(inst, .none, &.{struct_field.struct_operand});
39723853
3973 const result: WValue = switch (struct_ty.containerLayout(zcu)) {3854 const result: WValue = switch (struct_ty.containerLayout(zcu)) {
3974 .@"packed" => switch (struct_ty.zigTypeTag(zcu)) {3855 .@"packed" => switch (struct_ty.zigTypeTag(zcu)) {
...@@ -3977,42 +3858,42 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3977,42 +3858,42 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3977 const offset = pt.structPackedFieldBitOffset(packed_struct, field_index);3858 const offset = pt.structPackedFieldBitOffset(packed_struct, field_index);
3978 const backing_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip));3859 const backing_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip));
3979 const wasm_bits = toWasmBits(backing_ty.intInfo(zcu).bits) orelse {3860 const wasm_bits = toWasmBits(backing_ty.intInfo(zcu).bits) orelse {
3980 return func.fail("TODO: airStructFieldVal for packed structs larger than 128 bits", .{});3861 return cg.fail("TODO: airStructFieldVal for packed structs larger than 128 bits", .{});
3981 };3862 };
3982 const const_wvalue: WValue = if (wasm_bits == 32)3863 const const_wvalue: WValue = if (wasm_bits == 32)
3983 .{ .imm32 = offset }3864 .{ .imm32 = offset }
3984 else if (wasm_bits == 64)3865 else if (wasm_bits == 64)
3985 .{ .imm64 = offset }3866 .{ .imm64 = offset }
3986 else3867 else
3987 return func.fail("TODO: airStructFieldVal for packed structs larger than 64 bits", .{});3868 return cg.fail("TODO: airStructFieldVal for packed structs larger than 64 bits", .{});
39883869
3989 // for first field we don't require any shifting3870 // for first field we don't require any shifting
3990 const shifted_value = if (offset == 0)3871 const shifted_value = if (offset == 0)
3991 operand3872 operand
3992 else3873 else
3993 try func.binOp(operand, const_wvalue, backing_ty, .shr);3874 try cg.binOp(operand, const_wvalue, backing_ty, .shr);
39943875
3995 if (field_ty.zigTypeTag(zcu) == .float) {3876 if (field_ty.zigTypeTag(zcu) == .float) {
3996 const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu))));3877 const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu))));
3997 const truncated = try func.trunc(shifted_value, int_type, backing_ty);3878 const truncated = try cg.trunc(shifted_value, int_type, backing_ty);
3998 break :result try func.bitcast(field_ty, int_type, truncated);3879 break :result try cg.bitcast(field_ty, int_type, truncated);
3999 } else if (field_ty.isPtrAtRuntime(zcu) and packed_struct.field_types.len == 1) {3880 } else if (field_ty.isPtrAtRuntime(zcu) and packed_struct.field_types.len == 1) {
4000 // In this case we do not have to perform any transformations,3881 // In this case we do not have to perform any transformations,
4001 // we can simply reuse the operand.3882 // we can simply reuse the operand.
4002 break :result func.reuseOperand(struct_field.struct_operand, operand);3883 break :result cg.reuseOperand(struct_field.struct_operand, operand);
4003 } else if (field_ty.isPtrAtRuntime(zcu)) {3884 } else if (field_ty.isPtrAtRuntime(zcu)) {
4004 const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu))));3885 const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu))));
4005 break :result try func.trunc(shifted_value, int_type, backing_ty);3886 break :result try cg.trunc(shifted_value, int_type, backing_ty);
4006 }3887 }
4007 break :result try func.trunc(shifted_value, field_ty, backing_ty);3888 break :result try cg.trunc(shifted_value, field_ty, backing_ty);
4008 },3889 },
4009 .@"union" => result: {3890 .@"union" => result: {
4010 if (isByRef(struct_ty, pt, func.target.*)) {3891 if (isByRef(struct_ty, zcu, cg.target)) {
4011 if (!isByRef(field_ty, pt, func.target.*)) {3892 if (!isByRef(field_ty, zcu, cg.target)) {
4012 break :result try func.load(operand, field_ty, 0);3893 break :result try cg.load(operand, field_ty, 0);
4013 } else {3894 } else {
4014 const new_stack_val = try func.allocStack(field_ty);3895 const new_stack_val = try cg.allocStack(field_ty);
4015 try func.store(new_stack_val, operand, field_ty, 0);3896 try cg.store(new_stack_val, operand, field_ty, 0);
4016 break :result new_stack_val;3897 break :result new_stack_val;
4017 }3898 }
4018 }3899 }
...@@ -4020,45 +3901,45 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4020,45 +3901,45 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4020 const union_int_type = try pt.intType(.unsigned, @as(u16, @intCast(struct_ty.bitSize(zcu))));3901 const union_int_type = try pt.intType(.unsigned, @as(u16, @intCast(struct_ty.bitSize(zcu))));
4021 if (field_ty.zigTypeTag(zcu) == .float) {3902 if (field_ty.zigTypeTag(zcu) == .float) {
4022 const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu))));3903 const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu))));
4023 const truncated = try func.trunc(operand, int_type, union_int_type);3904 const truncated = try cg.trunc(operand, int_type, union_int_type);
4024 break :result try func.bitcast(field_ty, int_type, truncated);3905 break :result try cg.bitcast(field_ty, int_type, truncated);
4025 } else if (field_ty.isPtrAtRuntime(zcu)) {3906 } else if (field_ty.isPtrAtRuntime(zcu)) {
4026 const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu))));3907 const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu))));
4027 break :result try func.trunc(operand, int_type, union_int_type);3908 break :result try cg.trunc(operand, int_type, union_int_type);
4028 }3909 }
4029 break :result try func.trunc(operand, field_ty, union_int_type);3910 break :result try cg.trunc(operand, field_ty, union_int_type);
4030 },3911 },
4031 else => unreachable,3912 else => unreachable,
4032 },3913 },
4033 else => result: {3914 else => result: {
4034 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, zcu)) orelse {3915 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, zcu)) orelse {
4035 return func.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(pt)});3916 return cg.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(pt)});
4036 };3917 };
4037 if (isByRef(field_ty, pt, func.target.*)) {3918 if (isByRef(field_ty, zcu, cg.target)) {
4038 switch (operand) {3919 switch (operand) {
4039 .stack_offset => |stack_offset| {3920 .stack_offset => |stack_offset| {
4040 break :result .{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };3921 break :result .{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
4041 },3922 },
4042 else => break :result try func.buildPointerOffset(operand, offset, .new),3923 else => break :result try cg.buildPointerOffset(operand, offset, .new),
4043 }3924 }
4044 }3925 }
4045 break :result try func.load(operand, field_ty, offset);3926 break :result try cg.load(operand, field_ty, offset);
4046 },3927 },
4047 };3928 };
40483929
4049 return func.finishAir(inst, result, &.{struct_field.struct_operand});3930 return cg.finishAir(inst, result, &.{struct_field.struct_operand});
4050}3931}
40513932
4052fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3933fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4053 const pt = func.pt;3934 const pt = cg.pt;
4054 const zcu = pt.zcu;3935 const zcu = pt.zcu;
4055 // result type is always 'noreturn'3936 // result type is always 'noreturn'
4056 const blocktype = wasm.block_empty;3937 const blocktype: std.wasm.BlockType = .empty;
4057 const switch_br = func.air.unwrapSwitch(inst);3938 const switch_br = cg.air.unwrapSwitch(inst);
4058 const target = try func.resolveInst(switch_br.operand);3939 const target = try cg.resolveInst(switch_br.operand);
4059 const target_ty = func.typeOf(switch_br.operand);3940 const target_ty = cg.typeOf(switch_br.operand);
4060 const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.cases_len + 1);3941 const liveness = try cg.liveness.getSwitchBr(cg.gpa, inst, switch_br.cases_len + 1);
4061 defer func.gpa.free(liveness.deaths);3942 defer cg.gpa.free(liveness.deaths);
40623943
4063 // a list that maps each value with its value and body based on the order inside the list.3944 // a list that maps each value with its value and body based on the order inside the list.
4064 const CaseValue = union(enum) {3945 const CaseValue = union(enum) {
...@@ -4068,21 +3949,21 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4068,21 +3949,21 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4068 var case_list = try std.ArrayList(struct {3949 var case_list = try std.ArrayList(struct {
4069 values: []const CaseValue,3950 values: []const CaseValue,
4070 body: []const Air.Inst.Index,3951 body: []const Air.Inst.Index,
4071 }).initCapacity(func.gpa, switch_br.cases_len);3952 }).initCapacity(cg.gpa, switch_br.cases_len);
4072 defer for (case_list.items) |case| {3953 defer for (case_list.items) |case| {
4073 func.gpa.free(case.values);3954 cg.gpa.free(case.values);
4074 } else case_list.deinit();3955 } else case_list.deinit();
40753956
4076 var lowest_maybe: ?i32 = null;3957 var lowest_maybe: ?i32 = null;
4077 var highest_maybe: ?i32 = null;3958 var highest_maybe: ?i32 = null;
4078 var it = switch_br.iterateCases();3959 var it = switch_br.iterateCases();
4079 while (it.next()) |case| {3960 while (it.next()) |case| {
4080 const values = try func.gpa.alloc(CaseValue, case.items.len + case.ranges.len);3961 const values = try cg.gpa.alloc(CaseValue, case.items.len + case.ranges.len);
4081 errdefer func.gpa.free(values);3962 errdefer cg.gpa.free(values);
40823963
4083 for (case.items, 0..) |ref, i| {3964 for (case.items, 0..) |ref, i| {
4084 const item_val = (try func.air.value(ref, pt)).?;3965 const item_val = (try cg.air.value(ref, pt)).?;
4085 const int_val = func.valueAsI32(item_val);3966 const int_val = cg.valueAsI32(item_val);
4086 if (lowest_maybe == null or int_val < lowest_maybe.?) {3967 if (lowest_maybe == null or int_val < lowest_maybe.?) {
4087 lowest_maybe = int_val;3968 lowest_maybe = int_val;
4088 }3969 }
...@@ -4093,15 +3974,15 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4093,15 +3974,15 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4093 }3974 }
40943975
4095 for (case.ranges, 0..) |range, i| {3976 for (case.ranges, 0..) |range, i| {
4096 const min_val = (try func.air.value(range[0], pt)).?;3977 const min_val = (try cg.air.value(range[0], pt)).?;
4097 const int_min_val = func.valueAsI32(min_val);3978 const int_min_val = cg.valueAsI32(min_val);
40983979
4099 if (lowest_maybe == null or int_min_val < lowest_maybe.?) {3980 if (lowest_maybe == null or int_min_val < lowest_maybe.?) {
4100 lowest_maybe = int_min_val;3981 lowest_maybe = int_min_val;
4101 }3982 }
41023983
4103 const max_val = (try func.air.value(range[1], pt)).?;3984 const max_val = (try cg.air.value(range[1], pt)).?;
4104 const int_max_val = func.valueAsI32(max_val);3985 const int_max_val = cg.valueAsI32(max_val);
41053986
4106 if (highest_maybe == null or int_max_val > highest_maybe.?) {3987 if (highest_maybe == null or int_max_val > highest_maybe.?) {
4107 highest_maybe = int_max_val;3988 highest_maybe = int_max_val;
...@@ -4116,7 +3997,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4116,7 +3997,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4116 }3997 }
41173998
4118 case_list.appendAssumeCapacity(.{ .values = values, .body = case.body });3999 case_list.appendAssumeCapacity(.{ .values = values, .body = case.body });
4119 try func.startBlock(.block, blocktype);4000 try cg.startBlock(.block, blocktype);
4120 }4001 }
41214002
4122 // When highest and lowest are null, we have no cases and can use a jump table4003 // When highest and lowest are null, we have no cases and can use a jump table
...@@ -4132,7 +4013,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4132,7 +4013,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4132 const else_body = it.elseBody();4013 const else_body = it.elseBody();
4133 const has_else_body = else_body.len != 0;4014 const has_else_body = else_body.len != 0;
4134 if (has_else_body) {4015 if (has_else_body) {
4135 try func.startBlock(.block, blocktype);4016 try cg.startBlock(.block, blocktype);
4136 }4017 }
41374018
4138 if (!is_sparse) {4019 if (!is_sparse) {
...@@ -4140,25 +4021,25 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4140,25 +4021,25 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4140 // The value 'target' represents the index into the table.4021 // The value 'target' represents the index into the table.
4141 // Each index in the table represents a label to the branch4022 // Each index in the table represents a label to the branch
4142 // to jump to.4023 // to jump to.
4143 try func.startBlock(.block, blocktype);4024 try cg.startBlock(.block, blocktype);
4144 try func.emitWValue(target);4025 try cg.emitWValue(target);
4145 if (lowest < 0) {4026 if (lowest < 0) {
4146 // since br_table works using indexes, starting from '0', we must ensure all values4027 // since br_table works using indexes, starting from '0', we must ensure all values
4147 // we put inside, are atleast 0.4028 // we put inside, are atleast 0.
4148 try func.addImm32(@bitCast(lowest * -1));4029 try cg.addImm32(@bitCast(lowest * -1));
4149 try func.addTag(.i32_add);4030 try cg.addTag(.i32_add);
4150 } else if (lowest > 0) {4031 } else if (lowest > 0) {
4151 // make the index start from 0 by substracting the lowest value4032 // make the index start from 0 by substracting the lowest value
4152 try func.addImm32(@bitCast(lowest));4033 try cg.addImm32(@bitCast(lowest));
4153 try func.addTag(.i32_sub);4034 try cg.addTag(.i32_sub);
4154 }4035 }
41554036
4156 // Account for default branch so always add '1'4037 // Account for default branch so always add '1'
4157 const depth = @as(u32, @intCast(highest - lowest + @intFromBool(has_else_body))) + 1;4038 const depth = @as(u32, @intCast(highest - lowest + @intFromBool(has_else_body))) + 1;
4158 const jump_table: Mir.JumpTable = .{ .length = depth };4039 const jump_table: Mir.JumpTable = .{ .length = depth };
4159 const table_extra_index = try func.addExtra(jump_table);4040 const table_extra_index = try cg.addExtra(jump_table);
4160 try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });4041 try cg.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
4161 try func.mir_extra.ensureUnusedCapacity(func.gpa, depth);4042 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, depth);
4162 var value = lowest;4043 var value = lowest;
4163 while (value <= highest) : (value += 1) {4044 while (value <= highest) : (value += 1) {
4164 // idx represents the branch we jump to4045 // idx represents the branch we jump to
...@@ -4179,78 +4060,77 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4179,78 +4060,77 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4179 // by using a jump table for this instead of if-else chains.4060 // by using a jump table for this instead of if-else chains.
4180 break :blk if (has_else_body or target_ty.zigTypeTag(zcu) == .error_set) switch_br.cases_len else unreachable;4061 break :blk if (has_else_body or target_ty.zigTypeTag(zcu) == .error_set) switch_br.cases_len else unreachable;
4181 };4062 };
4182 func.mir_extra.appendAssumeCapacity(idx);4063 cg.mir_extra.appendAssumeCapacity(idx);
4183 } else if (has_else_body) {4064 } else if (has_else_body) {
4184 func.mir_extra.appendAssumeCapacity(switch_br.cases_len); // default branch4065 cg.mir_extra.appendAssumeCapacity(switch_br.cases_len); // default branch
4185 }4066 }
4186 try func.endBlock();4067 try cg.endBlock();
4187 }4068 }
41884069
4189 try func.branches.ensureUnusedCapacity(func.gpa, case_list.items.len + @intFromBool(has_else_body));4070 try cg.branches.ensureUnusedCapacity(cg.gpa, case_list.items.len + @intFromBool(has_else_body));
4190 for (case_list.items, 0..) |case, index| {4071 for (case_list.items, 0..) |case, index| {
4191 // when sparse, we use if/else-chain, so emit conditional checks4072 // when sparse, we use if/else-chain, so emit conditional checks
4192 if (is_sparse) {4073 if (is_sparse) {
4193 // for single value prong we can emit a simple condition4074 // for single value prong we can emit a simple condition
4194 if (case.values.len == 1 and case.values[0] == .singular) {4075 if (case.values.len == 1 and case.values[0] == .singular) {
4195 const val = try func.lowerConstant(case.values[0].singular.value, target_ty);4076 const val = try cg.lowerConstant(case.values[0].singular.value, target_ty);
4196 // not equal, because we want to jump out of this block if it does not match the condition.4077 // not equal, because we want to jump out of this block if it does not match the condition.
4197 _ = try func.cmp(target, val, target_ty, .neq);4078 _ = try cg.cmp(target, val, target_ty, .neq);
4198 try func.addLabel(.br_if, 0);4079 try cg.addLabel(.br_if, 0);
4199 } else {4080 } else {
4200 // in multi-value prongs we must check if any prongs match the target value.4081 // in multi-value prongs we must check if any prongs match the target value.
4201 try func.startBlock(.block, blocktype);4082 try cg.startBlock(.block, blocktype);
4202 for (case.values) |value| {4083 for (case.values) |value| {
4203 switch (value) {4084 switch (value) {
4204 .singular => |single_val| {4085 .singular => |single_val| {
4205 const val = try func.lowerConstant(single_val.value, target_ty);4086 const val = try cg.lowerConstant(single_val.value, target_ty);
4206 _ = try func.cmp(target, val, target_ty, .eq);4087 _ = try cg.cmp(target, val, target_ty, .eq);
4207 },4088 },
4208 .range => |range| {4089 .range => |range| {
4209 const min_val = try func.lowerConstant(range.min_value, target_ty);4090 const min_val = try cg.lowerConstant(range.min_value, target_ty);
4210 const max_val = try func.lowerConstant(range.max_value, target_ty);4091 const max_val = try cg.lowerConstant(range.max_value, target_ty);
42114092
4212 const gte = try func.cmp(target, min_val, target_ty, .gte);4093 const gte = try cg.cmp(target, min_val, target_ty, .gte);
4213 const lte = try func.cmp(target, max_val, target_ty, .lte);4094 const lte = try cg.cmp(target, max_val, target_ty, .lte);
4214 _ = try func.binOp(gte, lte, Type.bool, .@"and");4095 _ = try cg.binOp(gte, lte, Type.bool, .@"and");
4215 },4096 },
4216 }4097 }
4217 try func.addLabel(.br_if, 0);4098 try cg.addLabel(.br_if, 0);
4218 }4099 }
4219 // value did not match any of the prong values4100 // value did not match any of the prong values
4220 try func.addLabel(.br, 1);4101 try cg.addLabel(.br, 1);
4221 try func.endBlock();4102 try cg.endBlock();
4222 }4103 }
4223 }4104 }
4224 func.branches.appendAssumeCapacity(.{});4105 cg.branches.appendAssumeCapacity(.{});
4225 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[index].len);4106 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, liveness.deaths[index].len);
4226 defer {4107 defer {
4227 var case_branch = func.branches.pop();4108 var case_branch = cg.branches.pop();
4228 case_branch.deinit(func.gpa);4109 case_branch.deinit(cg.gpa);
4229 }4110 }
4230 try func.genBody(case.body);4111 try cg.genBody(case.body);
4231 try func.endBlock();4112 try cg.endBlock();
4232 }4113 }
42334114
4234 if (has_else_body) {4115 if (has_else_body) {
4235 func.branches.appendAssumeCapacity(.{});4116 cg.branches.appendAssumeCapacity(.{});
4236 const else_deaths = liveness.deaths.len - 1;4117 const else_deaths = liveness.deaths.len - 1;
4237 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[else_deaths].len);4118 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, liveness.deaths[else_deaths].len);
4238 defer {4119 defer {
4239 var else_branch = func.branches.pop();4120 var else_branch = cg.branches.pop();
4240 else_branch.deinit(func.gpa);4121 else_branch.deinit(cg.gpa);
4241 }4122 }
4242 try func.genBody(else_body);4123 try cg.genBody(else_body);
4243 try func.endBlock();4124 try cg.endBlock();
4244 }4125 }
4245 return func.finishAir(inst, .none, &.{});4126 return cg.finishAir(inst, .none, &.{});
4246}4127}
42474128
4248fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!void {4129fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerError!void {
4249 const pt = func.pt;4130 const zcu = cg.pt.zcu;
4250 const zcu = pt.zcu;4131 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4251 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4132 const operand = try cg.resolveInst(un_op);
4252 const operand = try func.resolveInst(un_op);4133 const err_union_ty = cg.typeOf(un_op);
4253 const err_union_ty = func.typeOf(un_op);
4254 const pl_ty = err_union_ty.errorUnionPayload(zcu);4134 const pl_ty = err_union_ty.errorUnionPayload(zcu);
42554135
4256 const result: WValue = result: {4136 const result: WValue = result: {
...@@ -4262,57 +4142,55 @@ fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerErro...@@ -4262,57 +4142,55 @@ fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerErro
4262 }4142 }
4263 }4143 }
42644144
4265 try func.emitWValue(operand);4145 try cg.emitWValue(operand);
4266 if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {4146 if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4267 try func.addMemArg(.i32_load16_u, .{4147 try cg.addMemArg(.i32_load16_u, .{
4268 .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))),4148 .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))),
4269 .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),4149 .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),
4270 });4150 });
4271 }4151 }
42724152
4273 // Compare the error value with '0'4153 // Compare the error value with '0'
4274 try func.addImm32(0);4154 try cg.addImm32(0);
4275 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));4155 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
4276 break :result .stack;4156 break :result .stack;
4277 };4157 };
4278 return func.finishAir(inst, result, &.{un_op});4158 return cg.finishAir(inst, result, &.{un_op});
4279}4159}
42804160
4281fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {4161fn airUnwrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
4282 const pt = func.pt;4162 const zcu = cg.pt.zcu;
4283 const zcu = pt.zcu;4163 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4284 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
42854164
4286 const operand = try func.resolveInst(ty_op.operand);4165 const operand = try cg.resolveInst(ty_op.operand);
4287 const op_ty = func.typeOf(ty_op.operand);4166 const op_ty = cg.typeOf(ty_op.operand);
4288 const err_ty = if (op_is_ptr) op_ty.childType(zcu) else op_ty;4167 const err_ty = if (op_is_ptr) op_ty.childType(zcu) else op_ty;
4289 const payload_ty = err_ty.errorUnionPayload(zcu);4168 const payload_ty = err_ty.errorUnionPayload(zcu);
42904169
4291 const result: WValue = result: {4170 const result: WValue = result: {
4292 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {4171 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4293 if (op_is_ptr) {4172 if (op_is_ptr) {
4294 break :result func.reuseOperand(ty_op.operand, operand);4173 break :result cg.reuseOperand(ty_op.operand, operand);
4295 }4174 }
4296 break :result .none;4175 break :result .none;
4297 }4176 }
42984177
4299 const pl_offset = @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu)));4178 const pl_offset = @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu)));
4300 if (op_is_ptr or isByRef(payload_ty, pt, func.target.*)) {4179 if (op_is_ptr or isByRef(payload_ty, zcu, cg.target)) {
4301 break :result try func.buildPointerOffset(operand, pl_offset, .new);4180 break :result try cg.buildPointerOffset(operand, pl_offset, .new);
4302 }4181 }
43034182
4304 break :result try func.load(operand, payload_ty, pl_offset);4183 break :result try cg.load(operand, payload_ty, pl_offset);
4305 };4184 };
4306 return func.finishAir(inst, result, &.{ty_op.operand});4185 return cg.finishAir(inst, result, &.{ty_op.operand});
4307}4186}
43084187
4309fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {4188fn airUnwrapErrUnionError(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
4310 const pt = func.pt;4189 const zcu = cg.pt.zcu;
4311 const zcu = pt.zcu;4190 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4312 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
43134191
4314 const operand = try func.resolveInst(ty_op.operand);4192 const operand = try cg.resolveInst(ty_op.operand);
4315 const op_ty = func.typeOf(ty_op.operand);4193 const op_ty = cg.typeOf(ty_op.operand);
4316 const err_ty = if (op_is_ptr) op_ty.childType(zcu) else op_ty;4194 const err_ty = if (op_is_ptr) op_ty.childType(zcu) else op_ty;
4317 const payload_ty = err_ty.errorUnionPayload(zcu);4195 const payload_ty = err_ty.errorUnionPayload(zcu);
43184196
...@@ -4322,104 +4200,101 @@ fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool)...@@ -4322,104 +4200,101 @@ fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool)
4322 }4200 }
43234201
4324 if (op_is_ptr or !payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {4202 if (op_is_ptr or !payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4325 break :result func.reuseOperand(ty_op.operand, operand);4203 break :result cg.reuseOperand(ty_op.operand, operand);
4326 }4204 }
43274205
4328 break :result try func.load(operand, Type.anyerror, @intCast(errUnionErrorOffset(payload_ty, zcu)));4206 break :result try cg.load(operand, Type.anyerror, @intCast(errUnionErrorOffset(payload_ty, zcu)));
4329 };4207 };
4330 return func.finishAir(inst, result, &.{ty_op.operand});4208 return cg.finishAir(inst, result, &.{ty_op.operand});
4331}4209}
43324210
4333fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4211fn airWrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4334 const zcu = func.pt.zcu;4212 const zcu = cg.pt.zcu;
4335 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4213 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
43364214
4337 const operand = try func.resolveInst(ty_op.operand);4215 const operand = try cg.resolveInst(ty_op.operand);
4338 const err_ty = func.typeOfIndex(inst);4216 const err_ty = cg.typeOfIndex(inst);
43394217
4340 const pl_ty = func.typeOf(ty_op.operand);4218 const pl_ty = cg.typeOf(ty_op.operand);
4341 const result = result: {4219 const result = result: {
4342 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {4220 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4343 break :result func.reuseOperand(ty_op.operand, operand);4221 break :result cg.reuseOperand(ty_op.operand, operand);
4344 }4222 }
43454223
4346 const err_union = try func.allocStack(err_ty);4224 const err_union = try cg.allocStack(err_ty);
4347 const payload_ptr = try func.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new);4225 const payload_ptr = try cg.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new);
4348 try func.store(payload_ptr, operand, pl_ty, 0);4226 try cg.store(payload_ptr, operand, pl_ty, 0);
43494227
4350 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.4228 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
4351 try func.emitWValue(err_union);4229 try cg.emitWValue(err_union);
4352 try func.addImm32(0);4230 try cg.addImm32(0);
4353 const err_val_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));4231 const err_val_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
4354 try func.addMemArg(.i32_store16, .{4232 try cg.addMemArg(.i32_store16, .{
4355 .offset = err_union.offset() + err_val_offset,4233 .offset = err_union.offset() + err_val_offset,
4356 .alignment = 2,4234 .alignment = 2,
4357 });4235 });
4358 break :result err_union;4236 break :result err_union;
4359 };4237 };
4360 return func.finishAir(inst, result, &.{ty_op.operand});4238 return cg.finishAir(inst, result, &.{ty_op.operand});
4361}4239}
43624240
4363fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4241fn airWrapErrUnionErr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4364 const pt = func.pt;4242 const zcu = cg.pt.zcu;
4365 const zcu = pt.zcu;4243 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4366 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
43674244
4368 const operand = try func.resolveInst(ty_op.operand);4245 const operand = try cg.resolveInst(ty_op.operand);
4369 const err_ty = ty_op.ty.toType();4246 const err_ty = ty_op.ty.toType();
4370 const pl_ty = err_ty.errorUnionPayload(zcu);4247 const pl_ty = err_ty.errorUnionPayload(zcu);
43714248
4372 const result = result: {4249 const result = result: {
4373 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {4250 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4374 break :result func.reuseOperand(ty_op.operand, operand);4251 break :result cg.reuseOperand(ty_op.operand, operand);
4375 }4252 }
43764253
4377 const err_union = try func.allocStack(err_ty);4254 const err_union = try cg.allocStack(err_ty);
4378 // store error value4255 // store error value
4379 try func.store(err_union, operand, Type.anyerror, @intCast(errUnionErrorOffset(pl_ty, zcu)));4256 try cg.store(err_union, operand, Type.anyerror, @intCast(errUnionErrorOffset(pl_ty, zcu)));
43804257
4381 // write 'undefined' to the payload4258 // write 'undefined' to the payload
4382 const payload_ptr = try func.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new);4259 const payload_ptr = try cg.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new);
4383 const len = @as(u32, @intCast(err_ty.errorUnionPayload(zcu).abiSize(zcu)));4260 const len = @as(u32, @intCast(err_ty.errorUnionPayload(zcu).abiSize(zcu)));
4384 try func.memset(Type.u8, payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaa });4261 try cg.memset(Type.u8, payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaa });
43854262
4386 break :result err_union;4263 break :result err_union;
4387 };4264 };
4388 return func.finishAir(inst, result, &.{ty_op.operand});4265 return cg.finishAir(inst, result, &.{ty_op.operand});
4389}4266}
43904267
4391fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4268fn airIntcast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4392 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4269 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
43934270
4394 const ty = ty_op.ty.toType();4271 const ty = ty_op.ty.toType();
4395 const operand = try func.resolveInst(ty_op.operand);4272 const operand = try cg.resolveInst(ty_op.operand);
4396 const operand_ty = func.typeOf(ty_op.operand);4273 const operand_ty = cg.typeOf(ty_op.operand);
4397 const pt = func.pt;4274 const zcu = cg.pt.zcu;
4398 const zcu = pt.zcu;
4399 if (ty.zigTypeTag(zcu) == .vector or operand_ty.zigTypeTag(zcu) == .vector) {4275 if (ty.zigTypeTag(zcu) == .vector or operand_ty.zigTypeTag(zcu) == .vector) {
4400 return func.fail("todo Wasm intcast for vectors", .{});4276 return cg.fail("todo Wasm intcast for vectors", .{});
4401 }4277 }
4402 if (ty.abiSize(zcu) > 16 or operand_ty.abiSize(zcu) > 16) {4278 if (ty.abiSize(zcu) > 16 or operand_ty.abiSize(zcu) > 16) {
4403 return func.fail("todo Wasm intcast for bitsize > 128", .{});4279 return cg.fail("todo Wasm intcast for bitsize > 128", .{});
4404 }4280 }
44054281
4406 const op_bits = toWasmBits(@intCast(operand_ty.bitSize(zcu))).?;4282 const op_bits = toWasmBits(@intCast(operand_ty.bitSize(zcu))).?;
4407 const wanted_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?;4283 const wanted_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?;
4408 const result = if (op_bits == wanted_bits)4284 const result = if (op_bits == wanted_bits)
4409 func.reuseOperand(ty_op.operand, operand)4285 cg.reuseOperand(ty_op.operand, operand)
4410 else4286 else
4411 try func.intcast(operand, operand_ty, ty);4287 try cg.intcast(operand, operand_ty, ty);
44124288
4413 return func.finishAir(inst, result, &.{ty_op.operand});4289 return cg.finishAir(inst, result, &.{ty_op.operand});
4414}4290}
44154291
4416/// Upcasts or downcasts an integer based on the given and wanted types,4292/// Upcasts or downcasts an integer based on the given and wanted types,
4417/// and stores the result in a new operand.4293/// and stores the result in a new operand.
4418/// Asserts type's bitsize <= 1284294/// Asserts type's bitsize <= 128
4419/// NOTE: May leave the result on the top of the stack.4295/// NOTE: May leave the result on the top of the stack.
4420fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {4296fn intcast(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
4421 const pt = func.pt;4297 const zcu = cg.pt.zcu;
4422 const zcu = pt.zcu;
4423 const given_bitsize = @as(u16, @intCast(given.bitSize(zcu)));4298 const given_bitsize = @as(u16, @intCast(given.bitSize(zcu)));
4424 const wanted_bitsize = @as(u16, @intCast(wanted.bitSize(zcu)));4299 const wanted_bitsize = @as(u16, @intCast(wanted.bitSize(zcu)));
4425 assert(given_bitsize <= 128);4300 assert(given_bitsize <= 128);
...@@ -4432,470 +4307,456 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro...@@ -4432,470 +4307,456 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
4432 }4307 }
44334308
4434 if (op_bits == 64 and wanted_bits == 32) {4309 if (op_bits == 64 and wanted_bits == 32) {
4435 try func.emitWValue(operand);4310 try cg.emitWValue(operand);
4436 try func.addTag(.i32_wrap_i64);4311 try cg.addTag(.i32_wrap_i64);
4437 return .stack;4312 return .stack;
4438 } else if (op_bits == 32 and wanted_bits == 64) {4313 } else if (op_bits == 32 and wanted_bits == 64) {
4439 try func.emitWValue(operand);4314 try cg.emitWValue(operand);
4440 try func.addTag(if (wanted.isSignedInt(zcu)) .i64_extend_i32_s else .i64_extend_i32_u);4315 try cg.addTag(if (wanted.isSignedInt(zcu)) .i64_extend_i32_s else .i64_extend_i32_u);
4441 return .stack;4316 return .stack;
4442 } else if (wanted_bits == 128) {4317 } else if (wanted_bits == 128) {
4443 // for 128bit integers we store the integer in the virtual stack, rather than a local4318 // for 128bit integers we store the integer in the virtual stack, rather than a local
4444 const stack_ptr = try func.allocStack(wanted);4319 const stack_ptr = try cg.allocStack(wanted);
4445 try func.emitWValue(stack_ptr);4320 try cg.emitWValue(stack_ptr);
44464321
4447 // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it4322 // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it
4448 // meaning less store operations are required.4323 // meaning less store operations are required.
4449 const lhs = if (op_bits == 32) blk: {4324 const lhs = if (op_bits == 32) blk: {
4450 const sign_ty = if (wanted.isSignedInt(zcu)) Type.i64 else Type.u64;4325 const sign_ty = if (wanted.isSignedInt(zcu)) Type.i64 else Type.u64;
4451 break :blk try (try func.intcast(operand, given, sign_ty)).toLocal(func, sign_ty);4326 break :blk try (try cg.intcast(operand, given, sign_ty)).toLocal(cg, sign_ty);
4452 } else operand;4327 } else operand;
44534328
4454 // store lsb first4329 // store lsb first
4455 try func.store(.stack, lhs, Type.u64, 0 + stack_ptr.offset());4330 try cg.store(.stack, lhs, Type.u64, 0 + stack_ptr.offset());
44564331
4457 // For signed integers we shift lsb by 63 (64bit integer - 1 sign bit) and store remaining value4332 // For signed integers we shift lsb by 63 (64bit integer - 1 sign bit) and store remaining value
4458 if (wanted.isSignedInt(zcu)) {4333 if (wanted.isSignedInt(zcu)) {
4459 try func.emitWValue(stack_ptr);4334 try cg.emitWValue(stack_ptr);
4460 const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);4335 const shr = try cg.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
4461 try func.store(.stack, shr, Type.u64, 8 + stack_ptr.offset());4336 try cg.store(.stack, shr, Type.u64, 8 + stack_ptr.offset());
4462 } else {4337 } else {
4463 // Ensure memory of msb is zero'd4338 // Ensure memory of msb is zero'd
4464 try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);4339 try cg.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
4465 }4340 }
4466 return stack_ptr;4341 return stack_ptr;
4467 } else return func.load(operand, wanted, 0);4342 } else return cg.load(operand, wanted, 0);
4468}4343}
44694344
4470fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {4345fn airIsNull(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
4471 const pt = func.pt;4346 const zcu = cg.pt.zcu;
4472 const zcu = pt.zcu;4347 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4473 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4348 const operand = try cg.resolveInst(un_op);
4474 const operand = try func.resolveInst(un_op);
44754349
4476 const op_ty = func.typeOf(un_op);4350 const op_ty = cg.typeOf(un_op);
4477 const optional_ty = if (op_kind == .ptr) op_ty.childType(zcu) else op_ty;4351 const optional_ty = if (op_kind == .ptr) op_ty.childType(zcu) else op_ty;
4478 const result = try func.isNull(operand, optional_ty, opcode);4352 const result = try cg.isNull(operand, optional_ty, opcode);
4479 return func.finishAir(inst, result, &.{un_op});4353 return cg.finishAir(inst, result, &.{un_op});
4480}4354}
44814355
4482/// For a given type and operand, checks if it's considered `null`.4356/// For a given type and operand, checks if it's considered `null`.
4483/// NOTE: Leaves the result on the stack4357/// NOTE: Leaves the result on the stack
4484fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {4358fn isNull(cg: *CodeGen, operand: WValue, optional_ty: Type, opcode: std.wasm.Opcode) InnerError!WValue {
4485 const pt = func.pt;4359 const pt = cg.pt;
4486 const zcu = pt.zcu;4360 const zcu = pt.zcu;
4487 try func.emitWValue(operand);4361 try cg.emitWValue(operand);
4488 const payload_ty = optional_ty.optionalChild(zcu);4362 const payload_ty = optional_ty.optionalChild(zcu);
4489 if (!optional_ty.optionalReprIsPayload(zcu)) {4363 if (!optional_ty.optionalReprIsPayload(zcu)) {
4490 // When payload is zero-bits, we can treat operand as a value, rather than4364 // When payload is zero-bits, we can treat operand as a value, rather than
4491 // a pointer to the stack value4365 // a pointer to the stack value
4492 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {4366 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4493 const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse {4367 const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse {
4494 return func.fail("Optional type {} too big to fit into stack frame", .{optional_ty.fmt(pt)});4368 return cg.fail("Optional type {} too big to fit into stack frame", .{optional_ty.fmt(pt)});
4495 };4369 };
4496 try func.addMemArg(.i32_load8_u, .{ .offset = operand.offset() + offset, .alignment = 1 });4370 try cg.addMemArg(.i32_load8_u, .{ .offset = operand.offset() + offset, .alignment = 1 });
4497 }4371 }
4498 } else if (payload_ty.isSlice(zcu)) {4372 } else if (payload_ty.isSlice(zcu)) {
4499 switch (func.arch()) {4373 switch (cg.ptr_size) {
4500 .wasm32 => try func.addMemArg(.i32_load, .{ .offset = operand.offset(), .alignment = 4 }),4374 .wasm32 => try cg.addMemArg(.i32_load, .{ .offset = operand.offset(), .alignment = 4 }),
4501 .wasm64 => try func.addMemArg(.i64_load, .{ .offset = operand.offset(), .alignment = 8 }),4375 .wasm64 => try cg.addMemArg(.i64_load, .{ .offset = operand.offset(), .alignment = 8 }),
4502 else => unreachable,
4503 }4376 }
4504 }4377 }
45054378
4506 // Compare the null value with '0'4379 // Compare the null value with '0'
4507 try func.addImm32(0);4380 try cg.addImm32(0);
4508 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));4381 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
45094382
4510 return .stack;4383 return .stack;
4511}4384}
45124385
4513fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4386fn airOptionalPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4514 const pt = func.pt;4387 const zcu = cg.pt.zcu;
4515 const zcu = pt.zcu;4388 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4516 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4389 const opt_ty = cg.typeOf(ty_op.operand);
4517 const opt_ty = func.typeOf(ty_op.operand);4390 const payload_ty = cg.typeOfIndex(inst);
4518 const payload_ty = func.typeOfIndex(inst);
4519 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {4391 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4520 return func.finishAir(inst, .none, &.{ty_op.operand});4392 return cg.finishAir(inst, .none, &.{ty_op.operand});
4521 }4393 }
45224394
4523 const result = result: {4395 const result = result: {
4524 const operand = try func.resolveInst(ty_op.operand);4396 const operand = try cg.resolveInst(ty_op.operand);
4525 if (opt_ty.optionalReprIsPayload(zcu)) break :result func.reuseOperand(ty_op.operand, operand);4397 if (opt_ty.optionalReprIsPayload(zcu)) break :result cg.reuseOperand(ty_op.operand, operand);
45264398
4527 if (isByRef(payload_ty, pt, func.target.*)) {4399 if (isByRef(payload_ty, zcu, cg.target)) {
4528 break :result try func.buildPointerOffset(operand, 0, .new);4400 break :result try cg.buildPointerOffset(operand, 0, .new);
4529 }4401 }
45304402
4531 break :result try func.load(operand, payload_ty, 0);4403 break :result try cg.load(operand, payload_ty, 0);
4532 };4404 };
4533 return func.finishAir(inst, result, &.{ty_op.operand});4405 return cg.finishAir(inst, result, &.{ty_op.operand});
4534}4406}
45354407
4536fn airOptionalPayloadPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4408fn airOptionalPayloadPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4537 const pt = func.pt;4409 const zcu = cg.pt.zcu;
4538 const zcu = pt.zcu;4410 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4539 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4411 const operand = try cg.resolveInst(ty_op.operand);
4540 const operand = try func.resolveInst(ty_op.operand);4412 const opt_ty = cg.typeOf(ty_op.operand).childType(zcu);
4541 const opt_ty = func.typeOf(ty_op.operand).childType(zcu);
45424413
4543 const result = result: {4414 const result = result: {
4544 const payload_ty = opt_ty.optionalChild(zcu);4415 const payload_ty = opt_ty.optionalChild(zcu);
4545 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu) or opt_ty.optionalReprIsPayload(zcu)) {4416 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu) or opt_ty.optionalReprIsPayload(zcu)) {
4546 break :result func.reuseOperand(ty_op.operand, operand);4417 break :result cg.reuseOperand(ty_op.operand, operand);
4547 }4418 }
45484419
4549 break :result try func.buildPointerOffset(operand, 0, .new);4420 break :result try cg.buildPointerOffset(operand, 0, .new);
4550 };4421 };
4551 return func.finishAir(inst, result, &.{ty_op.operand});4422 return cg.finishAir(inst, result, &.{ty_op.operand});
4552}4423}
45534424
4554fn airOptionalPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4425fn airOptionalPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4555 const pt = func.pt;4426 const pt = cg.pt;
4556 const zcu = pt.zcu;4427 const zcu = pt.zcu;
4557 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4428 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4558 const operand = try func.resolveInst(ty_op.operand);4429 const operand = try cg.resolveInst(ty_op.operand);
4559 const opt_ty = func.typeOf(ty_op.operand).childType(zcu);4430 const opt_ty = cg.typeOf(ty_op.operand).childType(zcu);
4560 const payload_ty = opt_ty.optionalChild(zcu);4431 const payload_ty = opt_ty.optionalChild(zcu);
4561 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {4432 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4562 return func.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()});4433 return cg.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()});
4563 }4434 }
45644435
4565 if (opt_ty.optionalReprIsPayload(zcu)) {4436 if (opt_ty.optionalReprIsPayload(zcu)) {
4566 return func.finishAir(inst, operand, &.{ty_op.operand});4437 return cg.finishAir(inst, operand, &.{ty_op.operand});
4567 }4438 }
45684439
4569 const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse {4440 const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse {
4570 return func.fail("Optional type {} too big to fit into stack frame", .{opt_ty.fmt(pt)});4441 return cg.fail("Optional type {} too big to fit into stack frame", .{opt_ty.fmt(pt)});
4571 };4442 };
45724443
4573 try func.emitWValue(operand);4444 try cg.emitWValue(operand);
4574 try func.addImm32(1);4445 try cg.addImm32(1);
4575 try func.addMemArg(.i32_store8, .{ .offset = operand.offset() + offset, .alignment = 1 });4446 try cg.addMemArg(.i32_store8, .{ .offset = operand.offset() + offset, .alignment = 1 });
45764447
4577 const result = try func.buildPointerOffset(operand, 0, .new);4448 const result = try cg.buildPointerOffset(operand, 0, .new);
4578 return func.finishAir(inst, result, &.{ty_op.operand});4449 return cg.finishAir(inst, result, &.{ty_op.operand});
4579}4450}
45804451
4581fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4452fn airWrapOptional(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4582 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4453 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4583 const payload_ty = func.typeOf(ty_op.operand);4454 const payload_ty = cg.typeOf(ty_op.operand);
4584 const pt = func.pt;4455 const pt = cg.pt;
4585 const zcu = pt.zcu;4456 const zcu = pt.zcu;
45864457
4587 const result = result: {4458 const result = result: {
4588 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {4459 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4589 const non_null_bit = try func.allocStack(Type.u1);4460 const non_null_bit = try cg.allocStack(Type.u1);
4590 try func.emitWValue(non_null_bit);4461 try cg.emitWValue(non_null_bit);
4591 try func.addImm32(1);4462 try cg.addImm32(1);
4592 try func.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 });4463 try cg.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 });
4593 break :result non_null_bit;4464 break :result non_null_bit;
4594 }4465 }
45954466
4596 const operand = try func.resolveInst(ty_op.operand);4467 const operand = try cg.resolveInst(ty_op.operand);
4597 const op_ty = func.typeOfIndex(inst);4468 const op_ty = cg.typeOfIndex(inst);
4598 if (op_ty.optionalReprIsPayload(zcu)) {4469 if (op_ty.optionalReprIsPayload(zcu)) {
4599 break :result func.reuseOperand(ty_op.operand, operand);4470 break :result cg.reuseOperand(ty_op.operand, operand);
4600 }4471 }
4601 const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse {4472 const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse {
4602 return func.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(pt)});4473 return cg.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(pt)});
4603 };4474 };
46044475
4605 // Create optional type, set the non-null bit, and store the operand inside the optional type4476 // Create optional type, set the non-null bit, and store the operand inside the optional type
4606 const result_ptr = try func.allocStack(op_ty);4477 const result_ptr = try cg.allocStack(op_ty);
4607 try func.emitWValue(result_ptr);4478 try cg.emitWValue(result_ptr);
4608 try func.addImm32(1);4479 try cg.addImm32(1);
4609 try func.addMemArg(.i32_store8, .{ .offset = result_ptr.offset() + offset, .alignment = 1 });4480 try cg.addMemArg(.i32_store8, .{ .offset = result_ptr.offset() + offset, .alignment = 1 });
46104481
4611 const payload_ptr = try func.buildPointerOffset(result_ptr, 0, .new);4482 const payload_ptr = try cg.buildPointerOffset(result_ptr, 0, .new);
4612 try func.store(payload_ptr, operand, payload_ty, 0);4483 try cg.store(payload_ptr, operand, payload_ty, 0);
4613 break :result result_ptr;4484 break :result result_ptr;
4614 };4485 };
46154486
4616 return func.finishAir(inst, result, &.{ty_op.operand});4487 return cg.finishAir(inst, result, &.{ty_op.operand});
4617}4488}
46184489
4619fn airSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4490fn airSlice(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4620 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4491 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4621 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;4492 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
46224493
4623 const lhs = try func.resolveInst(bin_op.lhs);4494 const lhs = try cg.resolveInst(bin_op.lhs);
4624 const rhs = try func.resolveInst(bin_op.rhs);4495 const rhs = try cg.resolveInst(bin_op.rhs);
4625 const slice_ty = func.typeOfIndex(inst);4496 const slice_ty = cg.typeOfIndex(inst);
46264497
4627 const slice = try func.allocStack(slice_ty);4498 const slice = try cg.allocStack(slice_ty);
4628 try func.store(slice, lhs, Type.usize, 0);4499 try cg.store(slice, lhs, Type.usize, 0);
4629 try func.store(slice, rhs, Type.usize, func.ptrSize());4500 try cg.store(slice, rhs, Type.usize, cg.ptrSize());
46304501
4631 return func.finishAir(inst, slice, &.{ bin_op.lhs, bin_op.rhs });4502 return cg.finishAir(inst, slice, &.{ bin_op.lhs, bin_op.rhs });
4632}4503}
46334504
4634fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4505fn airSliceLen(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4635 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4506 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
46364507
4637 const operand = try func.resolveInst(ty_op.operand);4508 const operand = try cg.resolveInst(ty_op.operand);
4638 return func.finishAir(inst, try func.sliceLen(operand), &.{ty_op.operand});4509 return cg.finishAir(inst, try cg.sliceLen(operand), &.{ty_op.operand});
4639}4510}
46404511
4641fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4512fn airSliceElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4642 const pt = func.pt;4513 const zcu = cg.pt.zcu;
4643 const zcu = pt.zcu;4514 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
4644 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
46454515
4646 const slice_ty = func.typeOf(bin_op.lhs);4516 const slice_ty = cg.typeOf(bin_op.lhs);
4647 const slice = try func.resolveInst(bin_op.lhs);4517 const slice = try cg.resolveInst(bin_op.lhs);
4648 const index = try func.resolveInst(bin_op.rhs);4518 const index = try cg.resolveInst(bin_op.rhs);
4649 const elem_ty = slice_ty.childType(zcu);4519 const elem_ty = slice_ty.childType(zcu);
4650 const elem_size = elem_ty.abiSize(zcu);4520 const elem_size = elem_ty.abiSize(zcu);
46514521
4652 // load pointer onto stack4522 // load pointer onto stack
4653 _ = try func.load(slice, Type.usize, 0);4523 _ = try cg.load(slice, Type.usize, 0);
46544524
4655 // calculate index into slice4525 // calculate index into slice
4656 try func.emitWValue(index);4526 try cg.emitWValue(index);
4657 try func.addImm32(@intCast(elem_size));4527 try cg.addImm32(@intCast(elem_size));
4658 try func.addTag(.i32_mul);4528 try cg.addTag(.i32_mul);
4659 try func.addTag(.i32_add);4529 try cg.addTag(.i32_add);
46604530
4661 const elem_result = if (isByRef(elem_ty, pt, func.target.*))4531 const elem_result = if (isByRef(elem_ty, zcu, cg.target))
4662 .stack4532 .stack
4663 else4533 else
4664 try func.load(.stack, elem_ty, 0);4534 try cg.load(.stack, elem_ty, 0);
46654535
4666 return func.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });4536 return cg.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
4667}4537}
46684538
4669fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4539fn airSliceElemPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4670 const pt = func.pt;4540 const zcu = cg.pt.zcu;
4671 const zcu = pt.zcu;4541 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4672 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4542 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
4673 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
46744543
4675 const elem_ty = ty_pl.ty.toType().childType(zcu);4544 const elem_ty = ty_pl.ty.toType().childType(zcu);
4676 const elem_size = elem_ty.abiSize(zcu);4545 const elem_size = elem_ty.abiSize(zcu);
46774546
4678 const slice = try func.resolveInst(bin_op.lhs);4547 const slice = try cg.resolveInst(bin_op.lhs);
4679 const index = try func.resolveInst(bin_op.rhs);4548 const index = try cg.resolveInst(bin_op.rhs);
46804549
4681 _ = try func.load(slice, Type.usize, 0);4550 _ = try cg.load(slice, Type.usize, 0);
46824551
4683 // calculate index into slice4552 // calculate index into slice
4684 try func.emitWValue(index);4553 try cg.emitWValue(index);
4685 try func.addImm32(@intCast(elem_size));4554 try cg.addImm32(@intCast(elem_size));
4686 try func.addTag(.i32_mul);4555 try cg.addTag(.i32_mul);
4687 try func.addTag(.i32_add);4556 try cg.addTag(.i32_add);
46884557
4689 return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });4558 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
4690}4559}
46914560
4692fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4561fn airSlicePtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4693 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4562 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4694 const operand = try func.resolveInst(ty_op.operand);4563 const operand = try cg.resolveInst(ty_op.operand);
4695 return func.finishAir(inst, try func.slicePtr(operand), &.{ty_op.operand});4564 return cg.finishAir(inst, try cg.slicePtr(operand), &.{ty_op.operand});
4696}4565}
46974566
4698fn slicePtr(func: *CodeGen, operand: WValue) InnerError!WValue {4567fn slicePtr(cg: *CodeGen, operand: WValue) InnerError!WValue {
4699 const ptr = try func.load(operand, Type.usize, 0);4568 const ptr = try cg.load(operand, Type.usize, 0);
4700 return ptr.toLocal(func, Type.usize);4569 return ptr.toLocal(cg, Type.usize);
4701}4570}
47024571
4703fn sliceLen(func: *CodeGen, operand: WValue) InnerError!WValue {4572fn sliceLen(cg: *CodeGen, operand: WValue) InnerError!WValue {
4704 const len = try func.load(operand, Type.usize, func.ptrSize());4573 const len = try cg.load(operand, Type.usize, cg.ptrSize());
4705 return len.toLocal(func, Type.usize);4574 return len.toLocal(cg, Type.usize);
4706}4575}
47074576
4708fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4577fn airTrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4709 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4578 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
47104579
4711 const operand = try func.resolveInst(ty_op.operand);4580 const operand = try cg.resolveInst(ty_op.operand);
4712 const wanted_ty: Type = ty_op.ty.toType();4581 const wanted_ty: Type = ty_op.ty.toType();
4713 const op_ty = func.typeOf(ty_op.operand);4582 const op_ty = cg.typeOf(ty_op.operand);
4714 const pt = func.pt;4583 const zcu = cg.pt.zcu;
4715 const zcu = pt.zcu;
47164584
4717 if (wanted_ty.zigTypeTag(zcu) == .vector or op_ty.zigTypeTag(zcu) == .vector) {4585 if (wanted_ty.zigTypeTag(zcu) == .vector or op_ty.zigTypeTag(zcu) == .vector) {
4718 return func.fail("TODO: trunc for vectors", .{});4586 return cg.fail("TODO: trunc for vectors", .{});
4719 }4587 }
47204588
4721 const result = if (op_ty.bitSize(zcu) == wanted_ty.bitSize(zcu))4589 const result = if (op_ty.bitSize(zcu) == wanted_ty.bitSize(zcu))
4722 func.reuseOperand(ty_op.operand, operand)4590 cg.reuseOperand(ty_op.operand, operand)
4723 else4591 else
4724 try func.trunc(operand, wanted_ty, op_ty);4592 try cg.trunc(operand, wanted_ty, op_ty);
47254593
4726 return func.finishAir(inst, result, &.{ty_op.operand});4594 return cg.finishAir(inst, result, &.{ty_op.operand});
4727}4595}
47284596
4729/// Truncates a given operand to a given type, discarding any overflown bits.4597/// Truncates a given operand to a given type, discarding any overflown bits.
4730/// NOTE: Resulting value is left on the stack.4598/// NOTE: Resulting value is left on the stack.
4731fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) InnerError!WValue {4599fn trunc(cg: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) InnerError!WValue {
4732 const pt = func.pt;4600 const zcu = cg.pt.zcu;
4733 const zcu = pt.zcu;
4734 const given_bits = @as(u16, @intCast(given_ty.bitSize(zcu)));4601 const given_bits = @as(u16, @intCast(given_ty.bitSize(zcu)));
4735 if (toWasmBits(given_bits) == null) {4602 if (toWasmBits(given_bits) == null) {
4736 return func.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{given_bits});4603 return cg.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{given_bits});
4737 }4604 }
47384605
4739 var result = try func.intcast(operand, given_ty, wanted_ty);4606 var result = try cg.intcast(operand, given_ty, wanted_ty);
4740 const wanted_bits = @as(u16, @intCast(wanted_ty.bitSize(zcu)));4607 const wanted_bits = @as(u16, @intCast(wanted_ty.bitSize(zcu)));
4741 const wasm_bits = toWasmBits(wanted_bits).?;4608 const wasm_bits = toWasmBits(wanted_bits).?;
4742 if (wasm_bits != wanted_bits) {4609 if (wasm_bits != wanted_bits) {
4743 result = try func.wrapOperand(result, wanted_ty);4610 result = try cg.wrapOperand(result, wanted_ty);
4744 }4611 }
4745 return result;4612 return result;
4746}4613}
47474614
4748fn airIntFromBool(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4615fn airIntFromBool(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4749 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4616 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4750 const operand = try func.resolveInst(un_op);4617 const operand = try cg.resolveInst(un_op);
4751 const result = func.reuseOperand(un_op, operand);4618 const result = cg.reuseOperand(un_op, operand);
47524619
4753 return func.finishAir(inst, result, &.{un_op});4620 return cg.finishAir(inst, result, &.{un_op});
4754}4621}
47554622
4756fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4623fn airArrayToSlice(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4757 const pt = func.pt;4624 const zcu = cg.pt.zcu;
4758 const zcu = pt.zcu;4625 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4759 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
47604626
4761 const operand = try func.resolveInst(ty_op.operand);4627 const operand = try cg.resolveInst(ty_op.operand);
4762 const array_ty = func.typeOf(ty_op.operand).childType(zcu);4628 const array_ty = cg.typeOf(ty_op.operand).childType(zcu);
4763 const slice_ty = ty_op.ty.toType();4629 const slice_ty = ty_op.ty.toType();
47644630
4765 // create a slice on the stack4631 // create a slice on the stack
4766 const slice_local = try func.allocStack(slice_ty);4632 const slice_local = try cg.allocStack(slice_ty);
47674633
4768 // store the array ptr in the slice4634 // store the array ptr in the slice
4769 if (array_ty.hasRuntimeBitsIgnoreComptime(zcu)) {4635 if (array_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4770 try func.store(slice_local, operand, Type.usize, 0);4636 try cg.store(slice_local, operand, Type.usize, 0);
4771 }4637 }
47724638
4773 // store the length of the array in the slice4639 // store the length of the array in the slice
4774 const array_len: u32 = @intCast(array_ty.arrayLen(zcu));4640 const array_len: u32 = @intCast(array_ty.arrayLen(zcu));
4775 try func.store(slice_local, .{ .imm32 = array_len }, Type.usize, func.ptrSize());4641 try cg.store(slice_local, .{ .imm32 = array_len }, Type.usize, cg.ptrSize());
47764642
4777 return func.finishAir(inst, slice_local, &.{ty_op.operand});4643 return cg.finishAir(inst, slice_local, &.{ty_op.operand});
4778}4644}
47794645
4780fn airIntFromPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4646fn airIntFromPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4781 const pt = func.pt;4647 const zcu = cg.pt.zcu;
4782 const zcu = pt.zcu;4648 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4783 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4649 const operand = try cg.resolveInst(un_op);
4784 const operand = try func.resolveInst(un_op);4650 const ptr_ty = cg.typeOf(un_op);
4785 const ptr_ty = func.typeOf(un_op);
4786 const result = if (ptr_ty.isSlice(zcu))4651 const result = if (ptr_ty.isSlice(zcu))
4787 try func.slicePtr(operand)4652 try cg.slicePtr(operand)
4788 else switch (operand) {4653 else switch (operand) {
4789 // for stack offset, return a pointer to this offset.4654 // for stack offset, return a pointer to this offset.
4790 .stack_offset => try func.buildPointerOffset(operand, 0, .new),4655 .stack_offset => try cg.buildPointerOffset(operand, 0, .new),
4791 else => func.reuseOperand(un_op, operand),4656 else => cg.reuseOperand(un_op, operand),
4792 };4657 };
4793 return func.finishAir(inst, result, &.{un_op});4658 return cg.finishAir(inst, result, &.{un_op});
4794}4659}
47954660
4796fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4661fn airPtrElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4797 const pt = func.pt;4662 const zcu = cg.pt.zcu;
4798 const zcu = pt.zcu;4663 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
4799 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
48004664
4801 const ptr_ty = func.typeOf(bin_op.lhs);4665 const ptr_ty = cg.typeOf(bin_op.lhs);
4802 const ptr = try func.resolveInst(bin_op.lhs);4666 const ptr = try cg.resolveInst(bin_op.lhs);
4803 const index = try func.resolveInst(bin_op.rhs);4667 const index = try cg.resolveInst(bin_op.rhs);
4804 const elem_ty = ptr_ty.childType(zcu);4668 const elem_ty = ptr_ty.childType(zcu);
4805 const elem_size = elem_ty.abiSize(zcu);4669 const elem_size = elem_ty.abiSize(zcu);
48064670
4807 // load pointer onto the stack4671 // load pointer onto the stack
4808 if (ptr_ty.isSlice(zcu)) {4672 if (ptr_ty.isSlice(zcu)) {
4809 _ = try func.load(ptr, Type.usize, 0);4673 _ = try cg.load(ptr, Type.usize, 0);
4810 } else {4674 } else {
4811 try func.lowerToStack(ptr);4675 try cg.lowerToStack(ptr);
4812 }4676 }
48134677
4814 // calculate index into slice4678 // calculate index into slice
4815 try func.emitWValue(index);4679 try cg.emitWValue(index);
4816 try func.addImm32(@intCast(elem_size));4680 try cg.addImm32(@intCast(elem_size));
4817 try func.addTag(.i32_mul);4681 try cg.addTag(.i32_mul);
4818 try func.addTag(.i32_add);4682 try cg.addTag(.i32_add);
48194683
4820 const elem_result = if (isByRef(elem_ty, pt, func.target.*))4684 const elem_result = if (isByRef(elem_ty, zcu, cg.target))
4821 .stack4685 .stack
4822 else4686 else
4823 try func.load(.stack, elem_ty, 0);4687 try cg.load(.stack, elem_ty, 0);
48244688
4825 return func.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });4689 return cg.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
4826}4690}
48274691
4828fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4692fn airPtrElemPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4829 const pt = func.pt;4693 const zcu = cg.pt.zcu;
4830 const zcu = pt.zcu;4694 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4831 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4695 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
4832 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
48334696
4834 const ptr_ty = func.typeOf(bin_op.lhs);4697 const ptr_ty = cg.typeOf(bin_op.lhs);
4835 const elem_ty = ty_pl.ty.toType().childType(zcu);4698 const elem_ty = ty_pl.ty.toType().childType(zcu);
4836 const elem_size = elem_ty.abiSize(zcu);4699 const elem_size = elem_ty.abiSize(zcu);
48374700
4838 const ptr = try func.resolveInst(bin_op.lhs);4701 const ptr = try cg.resolveInst(bin_op.lhs);
4839 const index = try func.resolveInst(bin_op.rhs);4702 const index = try cg.resolveInst(bin_op.rhs);
48404703
4841 // load pointer onto the stack4704 // load pointer onto the stack
4842 if (ptr_ty.isSlice(zcu)) {4705 if (ptr_ty.isSlice(zcu)) {
4843 _ = try func.load(ptr, Type.usize, 0);4706 _ = try cg.load(ptr, Type.usize, 0);
4844 } else {4707 } else {
4845 try func.lowerToStack(ptr);4708 try cg.lowerToStack(ptr);
4846 }4709 }
48474710
4848 // calculate index into ptr4711 // calculate index into ptr
4849 try func.emitWValue(index);4712 try cg.emitWValue(index);
4850 try func.addImm32(@intCast(elem_size));4713 try cg.addImm32(@intCast(elem_size));
4851 try func.addTag(.i32_mul);4714 try cg.addTag(.i32_mul);
4852 try func.addTag(.i32_add);4715 try cg.addTag(.i32_add);
48534716
4854 return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });4717 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
4855}4718}
48564719
4857fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {4720fn airPtrBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
4858 const pt = func.pt;4721 const zcu = cg.pt.zcu;
4859 const zcu = pt.zcu;4722 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4860 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4723 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
4861 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
48624724
4863 const ptr = try func.resolveInst(bin_op.lhs);4725 const ptr = try cg.resolveInst(bin_op.lhs);
4864 const offset = try func.resolveInst(bin_op.rhs);4726 const offset = try cg.resolveInst(bin_op.rhs);
4865 const ptr_ty = func.typeOf(bin_op.lhs);4727 const ptr_ty = cg.typeOf(bin_op.lhs);
4866 const pointee_ty = switch (ptr_ty.ptrSize(zcu)) {4728 const pointee_ty = switch (ptr_ty.ptrSize(zcu)) {
4867 .One => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type4729 .One => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type
4868 else => ptr_ty.childType(zcu),4730 else => ptr_ty.childType(zcu),
4869 };4731 };
48704732
4871 const valtype = typeToValtype(Type.usize, pt, func.target.*);4733 const valtype = typeToValtype(Type.usize, zcu, cg.target);
4872 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });4734 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });
4873 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });4735 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });
48744736
4875 try func.lowerToStack(ptr);4737 try cg.lowerToStack(ptr);
4876 try func.emitWValue(offset);4738 try cg.emitWValue(offset);
4877 try func.addImm32(@intCast(pointee_ty.abiSize(zcu)));4739 try cg.addImm32(@intCast(pointee_ty.abiSize(zcu)));
4878 try func.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode));4740 try cg.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode));
4879 try func.addTag(Mir.Inst.Tag.fromOpcode(bin_opcode));4741 try cg.addTag(Mir.Inst.Tag.fromOpcode(bin_opcode));
48804742
4881 return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });4743 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
4882}4744}
48834745
4884fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {4746fn airMemset(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
4885 const pt = func.pt;4747 const zcu = cg.pt.zcu;
4886 const zcu = pt.zcu;
4887 if (safety) {4748 if (safety) {
4888 // TODO if the value is undef, write 0xaa bytes to dest4749 // TODO if the value is undef, write 0xaa bytes to dest
4889 } else {4750 } else {
4890 // TODO if the value is undef, don't lower this instruction4751 // TODO if the value is undef, don't lower this instruction
4891 }4752 }
4892 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;4753 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
48934754
4894 const ptr = try func.resolveInst(bin_op.lhs);4755 const ptr = try cg.resolveInst(bin_op.lhs);
4895 const ptr_ty = func.typeOf(bin_op.lhs);4756 const ptr_ty = cg.typeOf(bin_op.lhs);
4896 const value = try func.resolveInst(bin_op.rhs);4757 const value = try cg.resolveInst(bin_op.rhs);
4897 const len = switch (ptr_ty.ptrSize(zcu)) {4758 const len = switch (ptr_ty.ptrSize(zcu)) {
4898 .Slice => try func.sliceLen(ptr),4759 .Slice => try cg.sliceLen(ptr),
4899 .One => @as(WValue, .{ .imm32 = @as(u32, @intCast(ptr_ty.childType(zcu).arrayLen(zcu))) }),4760 .One => @as(WValue, .{ .imm32 = @as(u32, @intCast(ptr_ty.childType(zcu).arrayLen(zcu))) }),
4900 .C, .Many => unreachable,4761 .C, .Many => unreachable,
4901 };4762 };
...@@ -4905,27 +4766,27 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void...@@ -4905,27 +4766,27 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void
4905 else4766 else
4906 ptr_ty.childType(zcu);4767 ptr_ty.childType(zcu);
49074768
4908 const dst_ptr = try func.sliceOrArrayPtr(ptr, ptr_ty);4769 const dst_ptr = try cg.sliceOrArrayPtr(ptr, ptr_ty);
4909 try func.memset(elem_ty, dst_ptr, len, value);4770 try cg.memset(elem_ty, dst_ptr, len, value);
49104771
4911 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });4772 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
4912}4773}
49134774
4914/// Sets a region of memory at `ptr` to the value of `value`4775/// Sets a region of memory at `ptr` to the value of `value`
4915/// When the user has enabled the bulk_memory feature, we lower4776/// When the user has enabled the bulk_memory feature, we lower
4916/// this to wasm's memset instruction. When the feature is not present,4777/// this to wasm's memset instruction. When the feature is not present,
4917/// we implement it manually.4778/// we implement it manually.
4918fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) InnerError!void {4779fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) InnerError!void {
4919 const pt = func.pt;4780 const zcu = cg.pt.zcu;
4920 const abi_size = @as(u32, @intCast(elem_ty.abiSize(pt.zcu)));4781 const abi_size = @as(u32, @intCast(elem_ty.abiSize(zcu)));
49214782
4922 // When bulk_memory is enabled, we lower it to wasm's memset instruction.4783 // When bulk_memory is enabled, we lower it to wasm's memset instruction.
4923 // If not, we lower it ourselves.4784 // If not, we lower it ourselves.
4924 if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory) and abi_size == 1) {4785 if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory) and abi_size == 1) {
4925 try func.lowerToStack(ptr);4786 try cg.lowerToStack(ptr);
4926 try func.emitWValue(value);4787 try cg.emitWValue(value);
4927 try func.emitWValue(len);4788 try cg.emitWValue(len);
4928 try func.addExtended(.memory_fill);4789 try cg.addExtended(.memory_fill);
4929 return;4790 return;
4930 }4791 }
49314792
...@@ -4933,100 +4794,95 @@ fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue...@@ -4933,100 +4794,95 @@ fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue
4933 .imm32 => |val| .{ .imm32 = val * abi_size },4794 .imm32 => |val| .{ .imm32 = val * abi_size },
4934 .imm64 => |val| .{ .imm64 = val * abi_size },4795 .imm64 => |val| .{ .imm64 = val * abi_size },
4935 else => if (abi_size != 1) blk: {4796 else => if (abi_size != 1) blk: {
4936 const new_len = try func.ensureAllocLocal(Type.usize);4797 const new_len = try cg.ensureAllocLocal(Type.usize);
4937 try func.emitWValue(len);4798 try cg.emitWValue(len);
4938 switch (func.arch()) {4799 switch (cg.ptr_size) {
4939 .wasm32 => {4800 .wasm32 => {
4940 try func.emitWValue(.{ .imm32 = abi_size });4801 try cg.emitWValue(.{ .imm32 = abi_size });
4941 try func.addTag(.i32_mul);4802 try cg.addTag(.i32_mul);
4942 },4803 },
4943 .wasm64 => {4804 .wasm64 => {
4944 try func.emitWValue(.{ .imm64 = abi_size });4805 try cg.emitWValue(.{ .imm64 = abi_size });
4945 try func.addTag(.i64_mul);4806 try cg.addTag(.i64_mul);
4946 },4807 },
4947 else => unreachable,
4948 }4808 }
4949 try func.addLabel(.local_set, new_len.local.value);4809 try cg.addLocal(.local_set, new_len.local.value);
4950 break :blk new_len;4810 break :blk new_len;
4951 } else len,4811 } else len,
4952 };4812 };
49534813
4954 var end_ptr = try func.allocLocal(Type.usize);4814 var end_ptr = try cg.allocLocal(Type.usize);
4955 defer end_ptr.free(func);4815 defer end_ptr.free(cg);
4956 var new_ptr = try func.buildPointerOffset(ptr, 0, .new);4816 var new_ptr = try cg.buildPointerOffset(ptr, 0, .new);
4957 defer new_ptr.free(func);4817 defer new_ptr.free(cg);
49584818
4959 // get the loop conditional: if current pointer address equals final pointer's address4819 // get the loop conditional: if current pointer address equals final pointer's address
4960 try func.lowerToStack(ptr);4820 try cg.lowerToStack(ptr);
4961 try func.emitWValue(final_len);4821 try cg.emitWValue(final_len);
4962 switch (func.arch()) {4822 switch (cg.ptr_size) {
4963 .wasm32 => try func.addTag(.i32_add),4823 .wasm32 => try cg.addTag(.i32_add),
4964 .wasm64 => try func.addTag(.i64_add),4824 .wasm64 => try cg.addTag(.i64_add),
4965 else => unreachable,
4966 }4825 }
4967 try func.addLabel(.local_set, end_ptr.local.value);4826 try cg.addLocal(.local_set, end_ptr.local.value);
49684827
4969 // outer block to jump to when loop is done4828 // outer block to jump to when loop is done
4970 try func.startBlock(.block, wasm.block_empty);4829 try cg.startBlock(.block, .empty);
4971 try func.startBlock(.loop, wasm.block_empty);4830 try cg.startBlock(.loop, .empty);
49724831
4973 // check for condition for loop end4832 // check for condition for loop end
4974 try func.emitWValue(new_ptr);4833 try cg.emitWValue(new_ptr);
4975 try func.emitWValue(end_ptr);4834 try cg.emitWValue(end_ptr);
4976 switch (func.arch()) {4835 switch (cg.ptr_size) {
4977 .wasm32 => try func.addTag(.i32_eq),4836 .wasm32 => try cg.addTag(.i32_eq),
4978 .wasm64 => try func.addTag(.i64_eq),4837 .wasm64 => try cg.addTag(.i64_eq),
4979 else => unreachable,
4980 }4838 }
4981 try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished)4839 try cg.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
49824840
4983 // store the value at the current position of the pointer4841 // store the value at the current position of the pointer
4984 try func.store(new_ptr, value, elem_ty, 0);4842 try cg.store(new_ptr, value, elem_ty, 0);
49854843
4986 // move the pointer to the next element4844 // move the pointer to the next element
4987 try func.emitWValue(new_ptr);4845 try cg.emitWValue(new_ptr);
4988 switch (func.arch()) {4846 switch (cg.ptr_size) {
4989 .wasm32 => {4847 .wasm32 => {
4990 try func.emitWValue(.{ .imm32 = abi_size });4848 try cg.emitWValue(.{ .imm32 = abi_size });
4991 try func.addTag(.i32_add);4849 try cg.addTag(.i32_add);
4992 },4850 },
4993 .wasm64 => {4851 .wasm64 => {
4994 try func.emitWValue(.{ .imm64 = abi_size });4852 try cg.emitWValue(.{ .imm64 = abi_size });
4995 try func.addTag(.i64_add);4853 try cg.addTag(.i64_add);
4996 },4854 },
4997 else => unreachable,
4998 }4855 }
4999 try func.addLabel(.local_set, new_ptr.local.value);4856 try cg.addLocal(.local_set, new_ptr.local.value);
50004857
5001 // end of loop4858 // end of loop
5002 try func.addLabel(.br, 0); // jump to start of loop4859 try cg.addLabel(.br, 0); // jump to start of loop
5003 try func.endBlock();4860 try cg.endBlock();
5004 try func.endBlock();4861 try cg.endBlock();
5005}4862}
50064863
5007fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4864fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5008 const pt = func.pt;4865 const zcu = cg.pt.zcu;
5009 const zcu = pt.zcu;4866 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
5010 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
50114867
5012 const array_ty = func.typeOf(bin_op.lhs);4868 const array_ty = cg.typeOf(bin_op.lhs);
5013 const array = try func.resolveInst(bin_op.lhs);4869 const array = try cg.resolveInst(bin_op.lhs);
5014 const index = try func.resolveInst(bin_op.rhs);4870 const index = try cg.resolveInst(bin_op.rhs);
5015 const elem_ty = array_ty.childType(zcu);4871 const elem_ty = array_ty.childType(zcu);
5016 const elem_size = elem_ty.abiSize(zcu);4872 const elem_size = elem_ty.abiSize(zcu);
50174873
5018 if (isByRef(array_ty, pt, func.target.*)) {4874 if (isByRef(array_ty, zcu, cg.target)) {
5019 try func.lowerToStack(array);4875 try cg.lowerToStack(array);
5020 try func.emitWValue(index);4876 try cg.emitWValue(index);
5021 try func.addImm32(@intCast(elem_size));4877 try cg.addImm32(@intCast(elem_size));
5022 try func.addTag(.i32_mul);4878 try cg.addTag(.i32_mul);
5023 try func.addTag(.i32_add);4879 try cg.addTag(.i32_add);
5024 } else {4880 } else {
5025 std.debug.assert(array_ty.zigTypeTag(zcu) == .vector);4881 assert(array_ty.zigTypeTag(zcu) == .vector);
50264882
5027 switch (index) {4883 switch (index) {
5028 inline .imm32, .imm64 => |lane| {4884 inline .imm32, .imm64 => |lane| {
5029 const opcode: wasm.SimdOpcode = switch (elem_ty.bitSize(zcu)) {4885 const opcode: std.wasm.SimdOpcode = switch (elem_ty.bitSize(zcu)) {
5030 8 => if (elem_ty.isSignedInt(zcu)) .i8x16_extract_lane_s else .i8x16_extract_lane_u,4886 8 => if (elem_ty.isSignedInt(zcu)) .i8x16_extract_lane_s else .i8x16_extract_lane_u,
5031 16 => if (elem_ty.isSignedInt(zcu)) .i16x8_extract_lane_s else .i16x8_extract_lane_u,4887 16 => if (elem_ty.isSignedInt(zcu)) .i16x8_extract_lane_s else .i16x8_extract_lane_u,
5032 32 => if (elem_ty.isInt(zcu)) .i32x4_extract_lane else .f32x4_extract_lane,4888 32 => if (elem_ty.isInt(zcu)) .i32x4_extract_lane else .f32x4_extract_lane,
...@@ -5034,174 +4890,185 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5034,174 +4890,185 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5034 else => unreachable,4890 else => unreachable,
5035 };4891 };
50364892
5037 var operands = [_]u32{ std.wasm.simdOpcode(opcode), @as(u8, @intCast(lane)) };4893 var operands = [_]u32{ @intFromEnum(opcode), @as(u8, @intCast(lane)) };
50384894
5039 try func.emitWValue(array);4895 try cg.emitWValue(array);
50404896
5041 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));4897 const extra_index = cg.extraLen();
5042 try func.mir_extra.appendSlice(func.gpa, &operands);4898 try cg.mir_extra.appendSlice(cg.gpa, &operands);
5043 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });4899 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
50444900
5045 return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });4901 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
5046 },4902 },
5047 else => {4903 else => {
5048 const stack_vec = try func.allocStack(array_ty);4904 const stack_vec = try cg.allocStack(array_ty);
5049 try func.store(stack_vec, array, array_ty, 0);4905 try cg.store(stack_vec, array, array_ty, 0);
50504906
5051 // Is a non-unrolled vector (v128)4907 // Is a non-unrolled vector (v128)
5052 try func.lowerToStack(stack_vec);4908 try cg.lowerToStack(stack_vec);
5053 try func.emitWValue(index);4909 try cg.emitWValue(index);
5054 try func.addImm32(@intCast(elem_size));4910 try cg.addImm32(@intCast(elem_size));
5055 try func.addTag(.i32_mul);4911 try cg.addTag(.i32_mul);
5056 try func.addTag(.i32_add);4912 try cg.addTag(.i32_add);
5057 },4913 },
5058 }4914 }
5059 }4915 }
50604916
5061 const elem_result = if (isByRef(elem_ty, pt, func.target.*))4917 const elem_result = if (isByRef(elem_ty, zcu, cg.target))
5062 .stack4918 .stack
5063 else4919 else
5064 try func.load(.stack, elem_ty, 0);4920 try cg.load(.stack, elem_ty, 0);
50654921
5066 return func.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });4922 return cg.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
5067}4923}
50684924
5069fn airIntFromFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4925fn airIntFromFloat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5070 const pt = func.pt;4926 const zcu = cg.pt.zcu;
5071 const zcu = pt.zcu;4927 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5072 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
50734928
5074 const operand = try func.resolveInst(ty_op.operand);4929 const operand = try cg.resolveInst(ty_op.operand);
5075 const op_ty = func.typeOf(ty_op.operand);4930 const op_ty = cg.typeOf(ty_op.operand);
5076 const op_bits = op_ty.floatBits(func.target.*);4931 const op_bits = op_ty.floatBits(cg.target.*);
50774932
5078 const dest_ty = func.typeOfIndex(inst);4933 const dest_ty = cg.typeOfIndex(inst);
5079 const dest_info = dest_ty.intInfo(zcu);4934 const dest_info = dest_ty.intInfo(zcu);
50804935
5081 if (dest_info.bits > 128) {4936 if (dest_info.bits > 128) {
5082 return func.fail("TODO: intFromFloat for integers/floats with bitsize {}", .{dest_info.bits});4937 return cg.fail("TODO: intFromFloat for integers/floats with bitsize {}", .{dest_info.bits});
5083 }4938 }
50844939
5085 if ((op_bits != 32 and op_bits != 64) or dest_info.bits > 64) {4940 if ((op_bits != 32 and op_bits != 64) or dest_info.bits > 64) {
5086 const dest_bitsize = if (dest_info.bits <= 16) 16 else std.math.ceilPowerOfTwoAssert(u16, dest_info.bits);4941 const dest_bitsize = if (dest_info.bits <= 32) 32 else std.math.ceilPowerOfTwoAssert(u16, dest_info.bits);
50874942
5088 var fn_name_buf: [16]u8 = undefined;4943 const intrinsic = switch (dest_info.signedness) {
5089 const fn_name = std.fmt.bufPrint(&fn_name_buf, "__fix{s}{s}f{s}i", .{4944 inline .signed, .unsigned => |ct_s| switch (op_bits) {
5090 switch (dest_info.signedness) {4945 inline 16, 32, 64, 80, 128 => |ct_op_bits| switch (dest_bitsize) {
5091 .signed => "",4946 inline 32, 64, 128 => |ct_dest_bits| @field(
5092 .unsigned => "uns",4947 Mir.Intrinsic,
4948 "__fix" ++ switch (ct_s) {
4949 .signed => "",
4950 .unsigned => "uns",
4951 } ++
4952 compilerRtFloatAbbrev(ct_op_bits) ++ "f" ++
4953 compilerRtIntAbbrev(ct_dest_bits) ++ "i",
4954 ),
4955 else => unreachable,
4956 },
4957 else => unreachable,
5093 },4958 },
5094 target_util.compilerRtFloatAbbrev(op_bits),4959 };
5095 target_util.compilerRtIntAbbrev(dest_bitsize),4960 const result = try cg.callIntrinsic(intrinsic, &.{op_ty.ip_index}, dest_ty, &.{operand});
5096 }) catch unreachable;4961 return cg.finishAir(inst, result, &.{ty_op.operand});
5097
5098 const result = try func.callIntrinsic(fn_name, &.{op_ty.ip_index}, dest_ty, &.{operand});
5099 return func.finishAir(inst, result, &.{ty_op.operand});
5100 }4962 }
51014963
5102 try func.emitWValue(operand);4964 try cg.emitWValue(operand);
5103 const op = buildOpcode(.{4965 const op = buildOpcode(.{
5104 .op = .trunc,4966 .op = .trunc,
5105 .valtype1 = typeToValtype(dest_ty, pt, func.target.*),4967 .valtype1 = typeToValtype(dest_ty, zcu, cg.target),
5106 .valtype2 = typeToValtype(op_ty, pt, func.target.*),4968 .valtype2 = typeToValtype(op_ty, zcu, cg.target),
5107 .signedness = dest_info.signedness,4969 .signedness = dest_info.signedness,
5108 });4970 });
5109 try func.addTag(Mir.Inst.Tag.fromOpcode(op));4971 try cg.addTag(Mir.Inst.Tag.fromOpcode(op));
5110 const result = try func.wrapOperand(.stack, dest_ty);4972 const result = try cg.wrapOperand(.stack, dest_ty);
5111 return func.finishAir(inst, result, &.{ty_op.operand});4973 return cg.finishAir(inst, result, &.{ty_op.operand});
5112}4974}
51134975
5114fn airFloatFromInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4976fn airFloatFromInt(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5115 const pt = func.pt;4977 const zcu = cg.pt.zcu;
5116 const zcu = pt.zcu;4978 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5117 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
51184979
5119 const operand = try func.resolveInst(ty_op.operand);4980 const operand = try cg.resolveInst(ty_op.operand);
5120 const op_ty = func.typeOf(ty_op.operand);4981 const op_ty = cg.typeOf(ty_op.operand);
5121 const op_info = op_ty.intInfo(zcu);4982 const op_info = op_ty.intInfo(zcu);
51224983
5123 const dest_ty = func.typeOfIndex(inst);4984 const dest_ty = cg.typeOfIndex(inst);
5124 const dest_bits = dest_ty.floatBits(func.target.*);4985 const dest_bits = dest_ty.floatBits(cg.target.*);
51254986
5126 if (op_info.bits > 128) {4987 if (op_info.bits > 128) {
5127 return func.fail("TODO: floatFromInt for integers/floats with bitsize {d} bits", .{op_info.bits});4988 return cg.fail("TODO: floatFromInt for integers/floats with bitsize {d} bits", .{op_info.bits});
5128 }4989 }
51294990
5130 if (op_info.bits > 64 or (dest_bits > 64 or dest_bits < 32)) {4991 if (op_info.bits > 64 or (dest_bits > 64 or dest_bits < 32)) {
5131 const op_bitsize = if (op_info.bits <= 16) 16 else std.math.ceilPowerOfTwoAssert(u16, op_info.bits);4992 const op_bitsize = if (op_info.bits <= 32) 32 else std.math.ceilPowerOfTwoAssert(u16, op_info.bits);
51324993
5133 var fn_name_buf: [16]u8 = undefined;4994 const intrinsic = switch (op_info.signedness) {
5134 const fn_name = std.fmt.bufPrint(&fn_name_buf, "__float{s}{s}i{s}f", .{4995 inline .signed, .unsigned => |ct_s| switch (op_bitsize) {
5135 switch (op_info.signedness) {4996 inline 32, 64, 128 => |ct_int_bits| switch (dest_bits) {
5136 .signed => "",4997 inline 16, 32, 64, 80, 128 => |ct_float_bits| @field(
5137 .unsigned => "un",4998 Mir.Intrinsic,
4999 "__float" ++ switch (ct_s) {
5000 .signed => "",
5001 .unsigned => "un",
5002 } ++
5003 compilerRtIntAbbrev(ct_int_bits) ++ "i" ++
5004 compilerRtFloatAbbrev(ct_float_bits) ++ "f",
5005 ),
5006 else => unreachable,
5007 },
5008 else => unreachable,
5138 },5009 },
5139 target_util.compilerRtIntAbbrev(op_bitsize),5010 };
5140 target_util.compilerRtFloatAbbrev(dest_bits),
5141 }) catch unreachable;
51425011
5143 const result = try func.callIntrinsic(fn_name, &.{op_ty.ip_index}, dest_ty, &.{operand});5012 const result = try cg.callIntrinsic(intrinsic, &.{op_ty.ip_index}, dest_ty, &.{operand});
5144 return func.finishAir(inst, result, &.{ty_op.operand});5013 return cg.finishAir(inst, result, &.{ty_op.operand});
5145 }5014 }
51465015
5147 try func.emitWValue(operand);5016 try cg.emitWValue(operand);
5148 const op = buildOpcode(.{5017 const op = buildOpcode(.{
5149 .op = .convert,5018 .op = .convert,
5150 .valtype1 = typeToValtype(dest_ty, pt, func.target.*),5019 .valtype1 = typeToValtype(dest_ty, zcu, cg.target),
5151 .valtype2 = typeToValtype(op_ty, pt, func.target.*),5020 .valtype2 = typeToValtype(op_ty, zcu, cg.target),
5152 .signedness = op_info.signedness,5021 .signedness = op_info.signedness,
5153 });5022 });
5154 try func.addTag(Mir.Inst.Tag.fromOpcode(op));5023 try cg.addTag(Mir.Inst.Tag.fromOpcode(op));
51555024
5156 return func.finishAir(inst, .stack, &.{ty_op.operand});5025 return cg.finishAir(inst, .stack, &.{ty_op.operand});
5157}5026}
51585027
5159fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5028fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5160 const pt = func.pt;5029 const zcu = cg.pt.zcu;
5161 const zcu = pt.zcu;5030 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5162 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5031 const operand = try cg.resolveInst(ty_op.operand);
5163 const operand = try func.resolveInst(ty_op.operand);5032 const ty = cg.typeOfIndex(inst);
5164 const ty = func.typeOfIndex(inst);
5165 const elem_ty = ty.childType(zcu);5033 const elem_ty = ty.childType(zcu);
51665034
5167 if (determineSimdStoreStrategy(ty, zcu, func.target.*) == .direct) blk: {5035 if (determineSimdStoreStrategy(ty, zcu, cg.target) == .direct) blk: {
5168 switch (operand) {5036 switch (operand) {
5169 // when the operand lives in the linear memory section, we can directly5037 // when the operand lives in the linear memory section, we can directly
5170 // load and splat the value at once. Meaning we do not first have to load5038 // load and splat the value at once. Meaning we do not first have to load
5171 // the scalar value onto the stack.5039 // the scalar value onto the stack.
5172 .stack_offset, .memory, .memory_offset => {5040 .stack_offset, .nav_ref, .uav_ref => {
5173 const opcode = switch (elem_ty.bitSize(zcu)) {5041 const opcode = switch (elem_ty.bitSize(zcu)) {
5174 8 => std.wasm.simdOpcode(.v128_load8_splat),5042 8 => @intFromEnum(std.wasm.SimdOpcode.v128_load8_splat),
5175 16 => std.wasm.simdOpcode(.v128_load16_splat),5043 16 => @intFromEnum(std.wasm.SimdOpcode.v128_load16_splat),
5176 32 => std.wasm.simdOpcode(.v128_load32_splat),5044 32 => @intFromEnum(std.wasm.SimdOpcode.v128_load32_splat),
5177 64 => std.wasm.simdOpcode(.v128_load64_splat),5045 64 => @intFromEnum(std.wasm.SimdOpcode.v128_load64_splat),
5178 else => break :blk, // Cannot make use of simd-instructions5046 else => break :blk, // Cannot make use of simd-instructions
5179 };5047 };
5180 try func.emitWValue(operand);5048 try cg.emitWValue(operand);
5181 // TODO: Add helper functions for simd opcodes5049 const extra_index: u32 = cg.extraLen();
5182 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));
5183 // stores as := opcode, offset, alignment (opcode::memarg)5050 // stores as := opcode, offset, alignment (opcode::memarg)
5184 try func.mir_extra.appendSlice(func.gpa, &[_]u32{5051 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
5185 opcode,5052 opcode,
5186 operand.offset(),5053 operand.offset(),
5187 @intCast(elem_ty.abiAlignment(zcu).toByteUnits().?),5054 @intCast(elem_ty.abiAlignment(zcu).toByteUnits().?),
5188 });5055 });
5189 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });5056 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
5190 return func.finishAir(inst, .stack, &.{ty_op.operand});5057 return cg.finishAir(inst, .stack, &.{ty_op.operand});
5191 },5058 },
5192 .local => {5059 .local => {
5193 const opcode = switch (elem_ty.bitSize(zcu)) {5060 const opcode = switch (elem_ty.bitSize(zcu)) {
5194 8 => std.wasm.simdOpcode(.i8x16_splat),5061 8 => @intFromEnum(std.wasm.SimdOpcode.i8x16_splat),
5195 16 => std.wasm.simdOpcode(.i16x8_splat),5062 16 => @intFromEnum(std.wasm.SimdOpcode.i16x8_splat),
5196 32 => if (elem_ty.isInt(zcu)) std.wasm.simdOpcode(.i32x4_splat) else std.wasm.simdOpcode(.f32x4_splat),5063 32 => if (elem_ty.isInt(zcu)) @intFromEnum(std.wasm.SimdOpcode.i32x4_splat) else @intFromEnum(std.wasm.SimdOpcode.f32x4_splat),
5197 64 => if (elem_ty.isInt(zcu)) std.wasm.simdOpcode(.i64x2_splat) else std.wasm.simdOpcode(.f64x2_splat),5064 64 => if (elem_ty.isInt(zcu)) @intFromEnum(std.wasm.SimdOpcode.i64x2_splat) else @intFromEnum(std.wasm.SimdOpcode.f64x2_splat),
5198 else => break :blk, // Cannot make use of simd-instructions5065 else => break :blk, // Cannot make use of simd-instructions
5199 };5066 };
5200 try func.emitWValue(operand);5067 try cg.emitWValue(operand);
5201 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));5068 const extra_index = cg.extraLen();
5202 try func.mir_extra.append(func.gpa, opcode);5069 try cg.mir_extra.append(cg.gpa, opcode);
5203 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });5070 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
5204 return func.finishAir(inst, .stack, &.{ty_op.operand});5071 return cg.finishAir(inst, .stack, &.{ty_op.operand});
5205 },5072 },
5206 else => unreachable,5073 else => unreachable,
5207 }5074 }
...@@ -5209,38 +5076,38 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5209,38 +5076,38 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5209 const elem_size = elem_ty.bitSize(zcu);5076 const elem_size = elem_ty.bitSize(zcu);
5210 const vector_len = @as(usize, @intCast(ty.vectorLen(zcu)));5077 const vector_len = @as(usize, @intCast(ty.vectorLen(zcu)));
5211 if ((!std.math.isPowerOfTwo(elem_size) or elem_size % 8 != 0) and vector_len > 1) {5078 if ((!std.math.isPowerOfTwo(elem_size) or elem_size % 8 != 0) and vector_len > 1) {
5212 return func.fail("TODO: WebAssembly `@splat` for arbitrary element bitsize {d}", .{elem_size});5079 return cg.fail("TODO: WebAssembly `@splat` for arbitrary element bitsize {d}", .{elem_size});
5213 }5080 }
52145081
5215 const result = try func.allocStack(ty);5082 const result = try cg.allocStack(ty);
5216 const elem_byte_size = @as(u32, @intCast(elem_ty.abiSize(zcu)));5083 const elem_byte_size = @as(u32, @intCast(elem_ty.abiSize(zcu)));
5217 var index: usize = 0;5084 var index: usize = 0;
5218 var offset: u32 = 0;5085 var offset: u32 = 0;
5219 while (index < vector_len) : (index += 1) {5086 while (index < vector_len) : (index += 1) {
5220 try func.store(result, operand, elem_ty, offset);5087 try cg.store(result, operand, elem_ty, offset);
5221 offset += elem_byte_size;5088 offset += elem_byte_size;
5222 }5089 }
52235090
5224 return func.finishAir(inst, result, &.{ty_op.operand});5091 return cg.finishAir(inst, result, &.{ty_op.operand});
5225}5092}
52265093
5227fn airSelect(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5094fn airSelect(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5228 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;5095 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
5229 const operand = try func.resolveInst(pl_op.operand);5096 const operand = try cg.resolveInst(pl_op.operand);
52305097
5231 _ = operand;5098 _ = operand;
5232 return func.fail("TODO: Implement wasm airSelect", .{});5099 return cg.fail("TODO: Implement wasm airSelect", .{});
5233}5100}
52345101
5235fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5102fn airShuffle(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5236 const pt = func.pt;5103 const pt = cg.pt;
5237 const zcu = pt.zcu;5104 const zcu = pt.zcu;
5238 const inst_ty = func.typeOfIndex(inst);5105 const inst_ty = cg.typeOfIndex(inst);
5239 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5106 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5240 const extra = func.air.extraData(Air.Shuffle, ty_pl.payload).data;5107 const extra = cg.air.extraData(Air.Shuffle, ty_pl.payload).data;
52415108
5242 const a = try func.resolveInst(extra.a);5109 const a = try cg.resolveInst(extra.a);
5243 const b = try func.resolveInst(extra.b);5110 const b = try cg.resolveInst(extra.b);
5244 const mask = Value.fromInterned(extra.mask);5111 const mask = Value.fromInterned(extra.mask);
5245 const mask_len = extra.mask_len;5112 const mask_len = extra.mask_len;
52465113
...@@ -5248,26 +5115,26 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5248,26 +5115,26 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5248 const elem_size = child_ty.abiSize(zcu);5115 const elem_size = child_ty.abiSize(zcu);
52495116
5250 // TODO: One of them could be by ref; handle in loop5117 // TODO: One of them could be by ref; handle in loop
5251 if (isByRef(func.typeOf(extra.a), pt, func.target.*) or isByRef(inst_ty, pt, func.target.*)) {5118 if (isByRef(cg.typeOf(extra.a), zcu, cg.target) or isByRef(inst_ty, zcu, cg.target)) {
5252 const result = try func.allocStack(inst_ty);5119 const result = try cg.allocStack(inst_ty);
52535120
5254 for (0..mask_len) |index| {5121 for (0..mask_len) |index| {
5255 const value = (try mask.elemValue(pt, index)).toSignedInt(zcu);5122 const value = (try mask.elemValue(pt, index)).toSignedInt(zcu);
52565123
5257 try func.emitWValue(result);5124 try cg.emitWValue(result);
52585125
5259 const loaded = if (value >= 0)5126 const loaded = if (value >= 0)
5260 try func.load(a, child_ty, @as(u32, @intCast(@as(i64, @intCast(elem_size)) * value)))5127 try cg.load(a, child_ty, @as(u32, @intCast(@as(i64, @intCast(elem_size)) * value)))
5261 else5128 else
5262 try func.load(b, child_ty, @as(u32, @intCast(@as(i64, @intCast(elem_size)) * ~value)));5129 try cg.load(b, child_ty, @as(u32, @intCast(@as(i64, @intCast(elem_size)) * ~value)));
52635130
5264 try func.store(.stack, loaded, child_ty, result.stack_offset.value + @as(u32, @intCast(elem_size)) * @as(u32, @intCast(index)));5131 try cg.store(.stack, loaded, child_ty, result.stack_offset.value + @as(u32, @intCast(elem_size)) * @as(u32, @intCast(index)));
5265 }5132 }
52665133
5267 return func.finishAir(inst, result, &.{ extra.a, extra.b });5134 return cg.finishAir(inst, result, &.{ extra.a, extra.b });
5268 } else {5135 } else {
5269 var operands = [_]u32{5136 var operands = [_]u32{
5270 std.wasm.simdOpcode(.i8x16_shuffle),5137 @intFromEnum(std.wasm.SimdOpcode.i8x16_shuffle),
5271 } ++ [1]u32{undefined} ** 4;5138 } ++ [1]u32{undefined} ** 4;
52725139
5273 var lanes = mem.asBytes(operands[1..]);5140 var lanes = mem.asBytes(operands[1..]);
...@@ -5283,91 +5150,91 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5283,91 +5150,91 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5283 }5150 }
5284 }5151 }
52855152
5286 try func.emitWValue(a);5153 try cg.emitWValue(a);
5287 try func.emitWValue(b);5154 try cg.emitWValue(b);
52885155
5289 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));5156 const extra_index = cg.extraLen();
5290 try func.mir_extra.appendSlice(func.gpa, &operands);5157 try cg.mir_extra.appendSlice(cg.gpa, &operands);
5291 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });5158 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
52925159
5293 return func.finishAir(inst, .stack, &.{ extra.a, extra.b });5160 return cg.finishAir(inst, .stack, &.{ extra.a, extra.b });
5294 }5161 }
5295}5162}
52965163
5297fn airReduce(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5164fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5298 const reduce = func.air.instructions.items(.data)[@intFromEnum(inst)].reduce;5165 const reduce = cg.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
5299 const operand = try func.resolveInst(reduce.operand);5166 const operand = try cg.resolveInst(reduce.operand);
53005167
5301 _ = operand;5168 _ = operand;
5302 return func.fail("TODO: Implement wasm airReduce", .{});5169 return cg.fail("TODO: Implement wasm airReduce", .{});
5303}5170}
53045171
5305fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5172fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5306 const pt = func.pt;5173 const pt = cg.pt;
5307 const zcu = pt.zcu;5174 const zcu = pt.zcu;
5308 const ip = &zcu.intern_pool;5175 const ip = &zcu.intern_pool;
5309 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5176 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5310 const result_ty = func.typeOfIndex(inst);5177 const result_ty = cg.typeOfIndex(inst);
5311 const len = @as(usize, @intCast(result_ty.arrayLen(zcu)));5178 const len = @as(usize, @intCast(result_ty.arrayLen(zcu)));
5312 const elements = @as([]const Air.Inst.Ref, @ptrCast(func.air.extra[ty_pl.payload..][0..len]));5179 const elements = @as([]const Air.Inst.Ref, @ptrCast(cg.air.extra[ty_pl.payload..][0..len]));
53135180
5314 const result: WValue = result_value: {5181 const result: WValue = result_value: {
5315 switch (result_ty.zigTypeTag(zcu)) {5182 switch (result_ty.zigTypeTag(zcu)) {
5316 .array => {5183 .array => {
5317 const result = try func.allocStack(result_ty);5184 const result = try cg.allocStack(result_ty);
5318 const elem_ty = result_ty.childType(zcu);5185 const elem_ty = result_ty.childType(zcu);
5319 const elem_size = @as(u32, @intCast(elem_ty.abiSize(zcu)));5186 const elem_size = @as(u32, @intCast(elem_ty.abiSize(zcu)));
5320 const sentinel = if (result_ty.sentinel(zcu)) |sent| blk: {5187 const sentinel = if (result_ty.sentinel(zcu)) |sent| blk: {
5321 break :blk try func.lowerConstant(sent, elem_ty);5188 break :blk try cg.lowerConstant(sent, elem_ty);
5322 } else null;5189 } else null;
53235190
5324 // When the element type is by reference, we must copy the entire5191 // When the element type is by reference, we must copy the entire
5325 // value. It is therefore safer to move the offset pointer and store5192 // value. It is therefore safer to move the offset pointer and store
5326 // each value individually, instead of using store offsets.5193 // each value individually, instead of using store offsets.
5327 if (isByRef(elem_ty, pt, func.target.*)) {5194 if (isByRef(elem_ty, zcu, cg.target)) {
5328 // copy stack pointer into a temporary local, which is5195 // copy stack pointer into a temporary local, which is
5329 // moved for each element to store each value in the right position.5196 // moved for each element to store each value in the right position.
5330 const offset = try func.buildPointerOffset(result, 0, .new);5197 const offset = try cg.buildPointerOffset(result, 0, .new);
5331 for (elements, 0..) |elem, elem_index| {5198 for (elements, 0..) |elem, elem_index| {
5332 const elem_val = try func.resolveInst(elem);5199 const elem_val = try cg.resolveInst(elem);
5333 try func.store(offset, elem_val, elem_ty, 0);5200 try cg.store(offset, elem_val, elem_ty, 0);
53345201
5335 if (elem_index < elements.len - 1 and sentinel == null) {5202 if (elem_index < elements.len - 1 and sentinel == null) {
5336 _ = try func.buildPointerOffset(offset, elem_size, .modify);5203 _ = try cg.buildPointerOffset(offset, elem_size, .modify);
5337 }5204 }
5338 }5205 }
5339 if (sentinel) |sent| {5206 if (sentinel) |sent| {
5340 try func.store(offset, sent, elem_ty, 0);5207 try cg.store(offset, sent, elem_ty, 0);
5341 }5208 }
5342 } else {5209 } else {
5343 var offset: u32 = 0;5210 var offset: u32 = 0;
5344 for (elements) |elem| {5211 for (elements) |elem| {
5345 const elem_val = try func.resolveInst(elem);5212 const elem_val = try cg.resolveInst(elem);
5346 try func.store(result, elem_val, elem_ty, offset);5213 try cg.store(result, elem_val, elem_ty, offset);
5347 offset += elem_size;5214 offset += elem_size;
5348 }5215 }
5349 if (sentinel) |sent| {5216 if (sentinel) |sent| {
5350 try func.store(result, sent, elem_ty, offset);5217 try cg.store(result, sent, elem_ty, offset);
5351 }5218 }
5352 }5219 }
5353 break :result_value result;5220 break :result_value result;
5354 },5221 },
5355 .@"struct" => switch (result_ty.containerLayout(zcu)) {5222 .@"struct" => switch (result_ty.containerLayout(zcu)) {
5356 .@"packed" => {5223 .@"packed" => {
5357 if (isByRef(result_ty, pt, func.target.*)) {5224 if (isByRef(result_ty, zcu, cg.target)) {
5358 return func.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{});5225 return cg.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{});
5359 }5226 }
5360 const packed_struct = zcu.typeToPackedStruct(result_ty).?;5227 const packed_struct = zcu.typeToPackedStruct(result_ty).?;
5361 const field_types = packed_struct.field_types;5228 const field_types = packed_struct.field_types;
5362 const backing_type = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip));5229 const backing_type = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip));
53635230
5364 // ensure the result is zero'd5231 // ensure the result is zero'd
5365 const result = try func.allocLocal(backing_type);5232 const result = try cg.allocLocal(backing_type);
5366 if (backing_type.bitSize(zcu) <= 32)5233 if (backing_type.bitSize(zcu) <= 32)
5367 try func.addImm32(0)5234 try cg.addImm32(0)
5368 else5235 else
5369 try func.addImm64(0);5236 try cg.addImm64(0);
5370 try func.addLabel(.local_set, result.local.value);5237 try cg.addLocal(.local_set, result.local.value);
53715238
5372 var current_bit: u16 = 0;5239 var current_bit: u16 = 0;
5373 for (elements, 0..) |elem, elem_index| {5240 for (elements, 0..) |elem, elem_index| {
...@@ -5379,46 +5246,46 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5379,46 +5246,46 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5379 else5246 else
5380 .{ .imm64 = current_bit };5247 .{ .imm64 = current_bit };
53815248
5382 const value = try func.resolveInst(elem);5249 const value = try cg.resolveInst(elem);
5383 const value_bit_size: u16 = @intCast(field_ty.bitSize(zcu));5250 const value_bit_size: u16 = @intCast(field_ty.bitSize(zcu));
5384 const int_ty = try pt.intType(.unsigned, value_bit_size);5251 const int_ty = try pt.intType(.unsigned, value_bit_size);
53855252
5386 // load our current result on stack so we can perform all transformations5253 // load our current result on stack so we can perform all transformations
5387 // using only stack values. Saving the cost of loads and stores.5254 // using only stack values. Saving the cost of loads and stores.
5388 try func.emitWValue(result);5255 try cg.emitWValue(result);
5389 const bitcasted = try func.bitcast(int_ty, field_ty, value);5256 const bitcasted = try cg.bitcast(int_ty, field_ty, value);
5390 const extended_val = try func.intcast(bitcasted, int_ty, backing_type);5257 const extended_val = try cg.intcast(bitcasted, int_ty, backing_type);
5391 // no need to shift any values when the current offset is 05258 // no need to shift any values when the current offset is 0
5392 const shifted = if (current_bit != 0) shifted: {5259 const shifted = if (current_bit != 0) shifted: {
5393 break :shifted try func.binOp(extended_val, shift_val, backing_type, .shl);5260 break :shifted try cg.binOp(extended_val, shift_val, backing_type, .shl);
5394 } else extended_val;5261 } else extended_val;
5395 // we ignore the result as we keep it on the stack to assign it directly to `result`5262 // we ignore the result as we keep it on the stack to assign it directly to `result`
5396 _ = try func.binOp(.stack, shifted, backing_type, .@"or");5263 _ = try cg.binOp(.stack, shifted, backing_type, .@"or");
5397 try func.addLabel(.local_set, result.local.value);5264 try cg.addLocal(.local_set, result.local.value);
5398 current_bit += value_bit_size;5265 current_bit += value_bit_size;
5399 }5266 }
5400 break :result_value result;5267 break :result_value result;
5401 },5268 },
5402 else => {5269 else => {
5403 const result = try func.allocStack(result_ty);5270 const result = try cg.allocStack(result_ty);
5404 const offset = try func.buildPointerOffset(result, 0, .new); // pointer to offset5271 const offset = try cg.buildPointerOffset(result, 0, .new); // pointer to offset
5405 var prev_field_offset: u64 = 0;5272 var prev_field_offset: u64 = 0;
5406 for (elements, 0..) |elem, elem_index| {5273 for (elements, 0..) |elem, elem_index| {
5407 if (try result_ty.structFieldValueComptime(pt, elem_index) != null) continue;5274 if (try result_ty.structFieldValueComptime(pt, elem_index) != null) continue;
54085275
5409 const elem_ty = result_ty.fieldType(elem_index, zcu);5276 const elem_ty = result_ty.fieldType(elem_index, zcu);
5410 const field_offset = result_ty.structFieldOffset(elem_index, zcu);5277 const field_offset = result_ty.structFieldOffset(elem_index, zcu);
5411 _ = try func.buildPointerOffset(offset, @intCast(field_offset - prev_field_offset), .modify);5278 _ = try cg.buildPointerOffset(offset, @intCast(field_offset - prev_field_offset), .modify);
5412 prev_field_offset = field_offset;5279 prev_field_offset = field_offset;
54135280
5414 const value = try func.resolveInst(elem);5281 const value = try cg.resolveInst(elem);
5415 try func.store(offset, value, elem_ty, 0);5282 try cg.store(offset, value, elem_ty, 0);
5416 }5283 }
54175284
5418 break :result_value result;5285 break :result_value result;
5419 },5286 },
5420 },5287 },
5421 .vector => return func.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),5288 .vector => return cg.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
5422 else => unreachable,5289 else => unreachable,
5423 }5290 }
5424 };5291 };
...@@ -5426,22 +5293,22 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5426,22 +5293,22 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5426 if (elements.len <= Liveness.bpi - 1) {5293 if (elements.len <= Liveness.bpi - 1) {
5427 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);5294 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
5428 @memcpy(buf[0..elements.len], elements);5295 @memcpy(buf[0..elements.len], elements);
5429 return func.finishAir(inst, result, &buf);5296 return cg.finishAir(inst, result, &buf);
5430 }5297 }
5431 var bt = try func.iterateBigTomb(inst, elements.len);5298 var bt = try cg.iterateBigTomb(inst, elements.len);
5432 for (elements) |arg| bt.feed(arg);5299 for (elements) |arg| bt.feed(arg);
5433 return bt.finishAir(result);5300 return bt.finishAir(result);
5434}5301}
54355302
5436fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5303fn airUnionInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5437 const pt = func.pt;5304 const pt = cg.pt;
5438 const zcu = pt.zcu;5305 const zcu = pt.zcu;
5439 const ip = &zcu.intern_pool;5306 const ip = &zcu.intern_pool;
5440 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5307 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5441 const extra = func.air.extraData(Air.UnionInit, ty_pl.payload).data;5308 const extra = cg.air.extraData(Air.UnionInit, ty_pl.payload).data;
54425309
5443 const result = result: {5310 const result = result: {
5444 const union_ty = func.typeOfIndex(inst);5311 const union_ty = cg.typeOfIndex(inst);
5445 const layout = union_ty.unionGetLayout(zcu);5312 const layout = union_ty.unionGetLayout(zcu);
5446 const union_obj = zcu.typeToUnion(union_ty).?;5313 const union_obj = zcu.typeToUnion(union_ty).?;
5447 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]);5314 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]);
...@@ -5451,34 +5318,34 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5451,34 +5318,34 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5451 const tag_ty = union_ty.unionTagTypeHypothetical(zcu);5318 const tag_ty = union_ty.unionTagTypeHypothetical(zcu);
5452 const enum_field_index = tag_ty.enumFieldIndex(field_name, zcu).?;5319 const enum_field_index = tag_ty.enumFieldIndex(field_name, zcu).?;
5453 const tag_val = try pt.enumValueFieldIndex(tag_ty, enum_field_index);5320 const tag_val = try pt.enumValueFieldIndex(tag_ty, enum_field_index);
5454 break :blk try func.lowerConstant(tag_val, tag_ty);5321 break :blk try cg.lowerConstant(tag_val, tag_ty);
5455 };5322 };
5456 if (layout.payload_size == 0) {5323 if (layout.payload_size == 0) {
5457 if (layout.tag_size == 0) {5324 if (layout.tag_size == 0) {
5458 break :result .none;5325 break :result .none;
5459 }5326 }
5460 assert(!isByRef(union_ty, pt, func.target.*));5327 assert(!isByRef(union_ty, zcu, cg.target));
5461 break :result tag_int;5328 break :result tag_int;
5462 }5329 }
54635330
5464 if (isByRef(union_ty, pt, func.target.*)) {5331 if (isByRef(union_ty, zcu, cg.target)) {
5465 const result_ptr = try func.allocStack(union_ty);5332 const result_ptr = try cg.allocStack(union_ty);
5466 const payload = try func.resolveInst(extra.init);5333 const payload = try cg.resolveInst(extra.init);
5467 if (layout.tag_align.compare(.gte, layout.payload_align)) {5334 if (layout.tag_align.compare(.gte, layout.payload_align)) {
5468 if (isByRef(field_ty, pt, func.target.*)) {5335 if (isByRef(field_ty, zcu, cg.target)) {
5469 const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new);5336 const payload_ptr = try cg.buildPointerOffset(result_ptr, layout.tag_size, .new);
5470 try func.store(payload_ptr, payload, field_ty, 0);5337 try cg.store(payload_ptr, payload, field_ty, 0);
5471 } else {5338 } else {
5472 try func.store(result_ptr, payload, field_ty, @intCast(layout.tag_size));5339 try cg.store(result_ptr, payload, field_ty, @intCast(layout.tag_size));
5473 }5340 }
54745341
5475 if (layout.tag_size > 0) {5342 if (layout.tag_size > 0) {
5476 try func.store(result_ptr, tag_int, Type.fromInterned(union_obj.enum_tag_ty), 0);5343 try cg.store(result_ptr, tag_int, Type.fromInterned(union_obj.enum_tag_ty), 0);
5477 }5344 }
5478 } else {5345 } else {
5479 try func.store(result_ptr, payload, field_ty, 0);5346 try cg.store(result_ptr, payload, field_ty, 0);
5480 if (layout.tag_size > 0) {5347 if (layout.tag_size > 0) {
5481 try func.store(5348 try cg.store(
5482 result_ptr,5349 result_ptr,
5483 tag_int,5350 tag_int,
5484 Type.fromInterned(union_obj.enum_tag_ty),5351 Type.fromInterned(union_obj.enum_tag_ty),
...@@ -5488,138 +5355,136 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5488,138 +5355,136 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5488 }5355 }
5489 break :result result_ptr;5356 break :result result_ptr;
5490 } else {5357 } else {
5491 const operand = try func.resolveInst(extra.init);5358 const operand = try cg.resolveInst(extra.init);
5492 const union_int_type = try pt.intType(.unsigned, @as(u16, @intCast(union_ty.bitSize(zcu))));5359 const union_int_type = try pt.intType(.unsigned, @as(u16, @intCast(union_ty.bitSize(zcu))));
5493 if (field_ty.zigTypeTag(zcu) == .float) {5360 if (field_ty.zigTypeTag(zcu) == .float) {
5494 const int_type = try pt.intType(.unsigned, @intCast(field_ty.bitSize(zcu)));5361 const int_type = try pt.intType(.unsigned, @intCast(field_ty.bitSize(zcu)));
5495 const bitcasted = try func.bitcast(field_ty, int_type, operand);5362 const bitcasted = try cg.bitcast(field_ty, int_type, operand);
5496 break :result try func.trunc(bitcasted, int_type, union_int_type);5363 break :result try cg.trunc(bitcasted, int_type, union_int_type);
5497 } else if (field_ty.isPtrAtRuntime(zcu)) {5364 } else if (field_ty.isPtrAtRuntime(zcu)) {
5498 const int_type = try pt.intType(.unsigned, @intCast(field_ty.bitSize(zcu)));5365 const int_type = try pt.intType(.unsigned, @intCast(field_ty.bitSize(zcu)));
5499 break :result try func.intcast(operand, int_type, union_int_type);5366 break :result try cg.intcast(operand, int_type, union_int_type);
5500 }5367 }
5501 break :result try func.intcast(operand, field_ty, union_int_type);5368 break :result try cg.intcast(operand, field_ty, union_int_type);
5502 }5369 }
5503 };5370 };
55045371
5505 return func.finishAir(inst, result, &.{extra.init});5372 return cg.finishAir(inst, result, &.{extra.init});
5506}5373}
55075374
5508fn airPrefetch(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5375fn airPrefetch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5509 const prefetch = func.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;5376 const prefetch = cg.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
5510 return func.finishAir(inst, .none, &.{prefetch.ptr});5377 return cg.finishAir(inst, .none, &.{prefetch.ptr});
5511}5378}
55125379
5513fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5380fn airWasmMemorySize(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5514 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;5381 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
55155382
5516 try func.addLabel(.memory_size, pl_op.payload);5383 try cg.addLabel(.memory_size, pl_op.payload);
5517 return func.finishAir(inst, .stack, &.{pl_op.operand});5384 return cg.finishAir(inst, .stack, &.{pl_op.operand});
5518}5385}
55195386
5520fn airWasmMemoryGrow(func: *CodeGen, inst: Air.Inst.Index) !void {5387fn airWasmMemoryGrow(cg: *CodeGen, inst: Air.Inst.Index) !void {
5521 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;5388 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
55225389
5523 const operand = try func.resolveInst(pl_op.operand);5390 const operand = try cg.resolveInst(pl_op.operand);
5524 try func.emitWValue(operand);5391 try cg.emitWValue(operand);
5525 try func.addLabel(.memory_grow, pl_op.payload);5392 try cg.addLabel(.memory_grow, pl_op.payload);
5526 return func.finishAir(inst, .stack, &.{pl_op.operand});5393 return cg.finishAir(inst, .stack, &.{pl_op.operand});
5527}5394}
55285395
5529fn cmpOptionals(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {5396fn cmpOptionals(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
5530 const pt = func.pt;5397 const zcu = cg.pt.zcu;
5531 const zcu = pt.zcu;
5532 assert(operand_ty.hasRuntimeBitsIgnoreComptime(zcu));5398 assert(operand_ty.hasRuntimeBitsIgnoreComptime(zcu));
5533 assert(op == .eq or op == .neq);5399 assert(op == .eq or op == .neq);
5534 const payload_ty = operand_ty.optionalChild(zcu);5400 const payload_ty = operand_ty.optionalChild(zcu);
55355401
5536 // We store the final result in here that will be validated5402 // We store the final result in here that will be validated
5537 // if the optional is truly equal.5403 // if the optional is truly equal.
5538 var result = try func.ensureAllocLocal(Type.i32);5404 var result = try cg.ensureAllocLocal(Type.i32);
5539 defer result.free(func);5405 defer result.free(cg);
55405406
5541 try func.startBlock(.block, wasm.block_empty);5407 try cg.startBlock(.block, .empty);
5542 _ = try func.isNull(lhs, operand_ty, .i32_eq);5408 _ = try cg.isNull(lhs, operand_ty, .i32_eq);
5543 _ = try func.isNull(rhs, operand_ty, .i32_eq);5409 _ = try cg.isNull(rhs, operand_ty, .i32_eq);
5544 try func.addTag(.i32_ne); // inverse so we can exit early5410 try cg.addTag(.i32_ne); // inverse so we can exit early
5545 try func.addLabel(.br_if, 0);5411 try cg.addLabel(.br_if, 0);
55465412
5547 _ = try func.load(lhs, payload_ty, 0);5413 _ = try cg.load(lhs, payload_ty, 0);
5548 _ = try func.load(rhs, payload_ty, 0);5414 _ = try cg.load(rhs, payload_ty, 0);
5549 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, pt, func.target.*) });5415 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, zcu, cg.target) });
5550 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));5416 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
5551 try func.addLabel(.br_if, 0);5417 try cg.addLabel(.br_if, 0);
55525418
5553 try func.addImm32(1);5419 try cg.addImm32(1);
5554 try func.addLabel(.local_set, result.local.value);5420 try cg.addLocal(.local_set, result.local.value);
5555 try func.endBlock();5421 try cg.endBlock();
55565422
5557 try func.emitWValue(result);5423 try cg.emitWValue(result);
5558 try func.addImm32(0);5424 try cg.addImm32(0);
5559 try func.addTag(if (op == .eq) .i32_ne else .i32_eq);5425 try cg.addTag(if (op == .eq) .i32_ne else .i32_eq);
5560 return .stack;5426 return .stack;
5561}5427}
55625428
5563/// Compares big integers by checking both its high bits and low bits.5429/// Compares big integers by checking both its high bits and low bits.
5564/// NOTE: Leaves the result of the comparison on top of the stack.5430/// NOTE: Leaves the result of the comparison on top of the stack.
5565/// TODO: Lower this to compiler_rt call when bitsize > 1285431/// TODO: Lower this to compiler_rt call when bitsize > 128
5566fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {5432fn cmpBigInt(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
5567 const pt = func.pt;5433 const zcu = cg.pt.zcu;
5568 const zcu = pt.zcu;
5569 assert(operand_ty.abiSize(zcu) >= 16);5434 assert(operand_ty.abiSize(zcu) >= 16);
5570 assert(!(lhs != .stack and rhs == .stack));5435 assert(!(lhs != .stack and rhs == .stack));
5571 if (operand_ty.bitSize(zcu) > 128) {5436 if (operand_ty.bitSize(zcu) > 128) {
5572 return func.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.bitSize(zcu)});5437 return cg.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.bitSize(zcu)});
5573 }5438 }
55745439
5575 var lhs_msb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);5440 var lhs_msb = try (try cg.load(lhs, Type.u64, 8)).toLocal(cg, Type.u64);
5576 defer lhs_msb.free(func);5441 defer lhs_msb.free(cg);
5577 var rhs_msb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);5442 var rhs_msb = try (try cg.load(rhs, Type.u64, 8)).toLocal(cg, Type.u64);
5578 defer rhs_msb.free(func);5443 defer rhs_msb.free(cg);
55795444
5580 switch (op) {5445 switch (op) {
5581 .eq, .neq => {5446 .eq, .neq => {
5582 const xor_high = try func.binOp(lhs_msb, rhs_msb, Type.u64, .xor);5447 const xor_high = try cg.binOp(lhs_msb, rhs_msb, Type.u64, .xor);
5583 const lhs_lsb = try func.load(lhs, Type.u64, 0);5448 const lhs_lsb = try cg.load(lhs, Type.u64, 0);
5584 const rhs_lsb = try func.load(rhs, Type.u64, 0);5449 const rhs_lsb = try cg.load(rhs, Type.u64, 0);
5585 const xor_low = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, .xor);5450 const xor_low = try cg.binOp(lhs_lsb, rhs_lsb, Type.u64, .xor);
5586 const or_result = try func.binOp(xor_high, xor_low, Type.u64, .@"or");5451 const or_result = try cg.binOp(xor_high, xor_low, Type.u64, .@"or");
55875452
5588 switch (op) {5453 switch (op) {
5589 .eq => return func.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq),5454 .eq => return cg.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq),
5590 .neq => return func.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq),5455 .neq => return cg.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq),
5591 else => unreachable,5456 else => unreachable,
5592 }5457 }
5593 },5458 },
5594 else => {5459 else => {
5595 const ty = if (operand_ty.isSignedInt(zcu)) Type.i64 else Type.u64;5460 const ty = if (operand_ty.isSignedInt(zcu)) Type.i64 else Type.u64;
5596 // leave those value on top of the stack for '.select'5461 // leave those value on top of the stack for '.select'
5597 const lhs_lsb = try func.load(lhs, Type.u64, 0);5462 const lhs_lsb = try cg.load(lhs, Type.u64, 0);
5598 const rhs_lsb = try func.load(rhs, Type.u64, 0);5463 const rhs_lsb = try cg.load(rhs, Type.u64, 0);
5599 _ = try func.cmp(lhs_lsb, rhs_lsb, Type.u64, op);5464 _ = try cg.cmp(lhs_lsb, rhs_lsb, Type.u64, op);
5600 _ = try func.cmp(lhs_msb, rhs_msb, ty, op);5465 _ = try cg.cmp(lhs_msb, rhs_msb, ty, op);
5601 _ = try func.cmp(lhs_msb, rhs_msb, ty, .eq);5466 _ = try cg.cmp(lhs_msb, rhs_msb, ty, .eq);
5602 try func.addTag(.select);5467 try cg.addTag(.select);
5603 },5468 },
5604 }5469 }
56055470
5606 return .stack;5471 return .stack;
5607}5472}
56085473
5609fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5474fn airSetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5610 const pt = func.pt;5475 const pt = cg.pt;
5611 const zcu = pt.zcu;5476 const zcu = pt.zcu;
5612 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;5477 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
5613 const un_ty = func.typeOf(bin_op.lhs).childType(zcu);5478 const un_ty = cg.typeOf(bin_op.lhs).childType(zcu);
5614 const tag_ty = func.typeOf(bin_op.rhs);5479 const tag_ty = cg.typeOf(bin_op.rhs);
5615 const layout = un_ty.unionGetLayout(zcu);5480 const layout = un_ty.unionGetLayout(zcu);
5616 if (layout.tag_size == 0) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });5481 if (layout.tag_size == 0) return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
56175482
5618 const union_ptr = try func.resolveInst(bin_op.lhs);5483 const union_ptr = try cg.resolveInst(bin_op.lhs);
5619 const new_tag = try func.resolveInst(bin_op.rhs);5484 const new_tag = try cg.resolveInst(bin_op.rhs);
5620 if (layout.payload_size == 0) {5485 if (layout.payload_size == 0) {
5621 try func.store(union_ptr, new_tag, tag_ty, 0);5486 try cg.store(union_ptr, new_tag, tag_ty, 0);
5622 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });5487 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
5623 }5488 }
56245489
5625 // when the tag alignment is smaller than the payload, the field will be stored5490 // when the tag alignment is smaller than the payload, the field will be stored
...@@ -5627,124 +5492,147 @@ fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5627,124 +5492,147 @@ fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5627 const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align)) blk: {5492 const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align)) blk: {
5628 break :blk @intCast(layout.payload_size);5493 break :blk @intCast(layout.payload_size);
5629 } else 0;5494 } else 0;
5630 try func.store(union_ptr, new_tag, tag_ty, offset);5495 try cg.store(union_ptr, new_tag, tag_ty, offset);
5631 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });5496 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
5632}5497}
56335498
5634fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5499fn airGetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5635 const zcu = func.pt.zcu;5500 const zcu = cg.pt.zcu;
5636 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5501 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
56375502
5638 const un_ty = func.typeOf(ty_op.operand);5503 const un_ty = cg.typeOf(ty_op.operand);
5639 const tag_ty = func.typeOfIndex(inst);5504 const tag_ty = cg.typeOfIndex(inst);
5640 const layout = un_ty.unionGetLayout(zcu);5505 const layout = un_ty.unionGetLayout(zcu);
5641 if (layout.tag_size == 0) return func.finishAir(inst, .none, &.{ty_op.operand});5506 if (layout.tag_size == 0) return cg.finishAir(inst, .none, &.{ty_op.operand});
56425507
5643 const operand = try func.resolveInst(ty_op.operand);5508 const operand = try cg.resolveInst(ty_op.operand);
5644 // when the tag alignment is smaller than the payload, the field will be stored5509 // when the tag alignment is smaller than the payload, the field will be stored
5645 // after the payload.5510 // after the payload.
5646 const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align))5511 const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align))
5647 @intCast(layout.payload_size)5512 @intCast(layout.payload_size)
5648 else5513 else
5649 0;5514 0;
5650 const result = try func.load(operand, tag_ty, offset);5515 const result = try cg.load(operand, tag_ty, offset);
5651 return func.finishAir(inst, result, &.{ty_op.operand});5516 return cg.finishAir(inst, result, &.{ty_op.operand});
5652}5517}
56535518
5654fn airFpext(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5519fn airFpext(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5655 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5520 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
56565521
5657 const dest_ty = func.typeOfIndex(inst);5522 const dest_ty = cg.typeOfIndex(inst);
5658 const operand = try func.resolveInst(ty_op.operand);5523 const operand = try cg.resolveInst(ty_op.operand);
5659 const result = try func.fpext(operand, func.typeOf(ty_op.operand), dest_ty);5524 const result = try cg.fpext(operand, cg.typeOf(ty_op.operand), dest_ty);
5660 return func.finishAir(inst, result, &.{ty_op.operand});5525 return cg.finishAir(inst, result, &.{ty_op.operand});
5661}5526}
56625527
5663/// Extends a float from a given `Type` to a larger wanted `Type`5528/// Extends a float from a given `Type` to a larger wanted `Type`, leaving the
5664/// NOTE: Leaves the result on the stack5529/// result on the stack.
5665fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {5530fn fpext(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
5666 const given_bits = given.floatBits(func.target.*);5531 const given_bits = given.floatBits(cg.target.*);
5667 const wanted_bits = wanted.floatBits(func.target.*);5532 const wanted_bits = wanted.floatBits(cg.target.*);
5668
5669 if (wanted_bits == 64 and given_bits == 32) {
5670 try func.emitWValue(operand);
5671 try func.addTag(.f64_promote_f32);
5672 return .stack;
5673 } else if (given_bits == 16 and wanted_bits <= 64) {
5674 // call __extendhfsf2(f16) f32
5675 const f32_result = try func.callIntrinsic(
5676 "__extendhfsf2",
5677 &.{.f16_type},
5678 Type.f32,
5679 &.{operand},
5680 );
5681 std.debug.assert(f32_result == .stack);
5682
5683 if (wanted_bits == 64) {
5684 try func.addTag(.f64_promote_f32);
5685 }
5686 return .stack;
5687 }
5688
5689 var fn_name_buf: [13]u8 = undefined;
5690 const fn_name = std.fmt.bufPrint(&fn_name_buf, "__extend{s}f{s}f2", .{
5691 target_util.compilerRtFloatAbbrev(given_bits),
5692 target_util.compilerRtFloatAbbrev(wanted_bits),
5693 }) catch unreachable;
56945533
5695 return func.callIntrinsic(fn_name, &.{given.ip_index}, wanted, &.{operand});5534 const intrinsic: Mir.Intrinsic = switch (given_bits) {
5535 16 => switch (wanted_bits) {
5536 32 => {
5537 assert(.stack == try cg.callIntrinsic(.__extendhfsf2, &.{.f16_type}, Type.f32, &.{operand}));
5538 return .stack;
5539 },
5540 64 => {
5541 assert(.stack == try cg.callIntrinsic(.__extendhfsf2, &.{.f16_type}, Type.f32, &.{operand}));
5542 try cg.addTag(.f64_promote_f32);
5543 return .stack;
5544 },
5545 80 => .__extendhfxf2,
5546 128 => .__extendhftf2,
5547 else => unreachable,
5548 },
5549 32 => switch (wanted_bits) {
5550 64 => {
5551 try cg.emitWValue(operand);
5552 try cg.addTag(.f64_promote_f32);
5553 return .stack;
5554 },
5555 80 => .__extendsfxf2,
5556 128 => .__extendsftf2,
5557 else => unreachable,
5558 },
5559 64 => switch (wanted_bits) {
5560 80 => .__extenddfxf2,
5561 128 => .__extenddftf2,
5562 else => unreachable,
5563 },
5564 80 => switch (wanted_bits) {
5565 128 => .__extendxftf2,
5566 else => unreachable,
5567 },
5568 else => unreachable,
5569 };
5570 return cg.callIntrinsic(intrinsic, &.{given.ip_index}, wanted, &.{operand});
5696}5571}
56975572
5698fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5573fn airFptrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5699 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5574 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
57005575
5701 const dest_ty = func.typeOfIndex(inst);5576 const dest_ty = cg.typeOfIndex(inst);
5702 const operand = try func.resolveInst(ty_op.operand);5577 const operand = try cg.resolveInst(ty_op.operand);
5703 const result = try func.fptrunc(operand, func.typeOf(ty_op.operand), dest_ty);5578 const result = try cg.fptrunc(operand, cg.typeOf(ty_op.operand), dest_ty);
5704 return func.finishAir(inst, result, &.{ty_op.operand});5579 return cg.finishAir(inst, result, &.{ty_op.operand});
5705}5580}
57065581
5707/// Truncates a float from a given `Type` to its wanted `Type`5582/// Truncates a float from a given `Type` to its wanted `Type`, leaving the
5708/// NOTE: The result value remains on the stack5583/// result on the stack.
5709fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {5584fn fptrunc(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
5710 const given_bits = given.floatBits(func.target.*);5585 const given_bits = given.floatBits(cg.target.*);
5711 const wanted_bits = wanted.floatBits(func.target.*);5586 const wanted_bits = wanted.floatBits(cg.target.*);
5712
5713 if (wanted_bits == 32 and given_bits == 64) {
5714 try func.emitWValue(operand);
5715 try func.addTag(.f32_demote_f64);
5716 return .stack;
5717 } else if (wanted_bits == 16 and given_bits <= 64) {
5718 const op: WValue = if (given_bits == 64) blk: {
5719 try func.emitWValue(operand);
5720 try func.addTag(.f32_demote_f64);
5721 break :blk .stack;
5722 } else operand;
5723
5724 // call __truncsfhf2(f32) f16
5725 return func.callIntrinsic("__truncsfhf2", &.{.f32_type}, Type.f16, &.{op});
5726 }
5727
5728 var fn_name_buf: [12]u8 = undefined;
5729 const fn_name = std.fmt.bufPrint(&fn_name_buf, "__trunc{s}f{s}f2", .{
5730 target_util.compilerRtFloatAbbrev(given_bits),
5731 target_util.compilerRtFloatAbbrev(wanted_bits),
5732 }) catch unreachable;
57335587
5734 return func.callIntrinsic(fn_name, &.{given.ip_index}, wanted, &.{operand});5588 const intrinsic: Mir.Intrinsic = switch (given_bits) {
5589 32 => switch (wanted_bits) {
5590 16 => {
5591 return cg.callIntrinsic(.__truncsfhf2, &.{.f32_type}, Type.f16, &.{operand});
5592 },
5593 else => unreachable,
5594 },
5595 64 => switch (wanted_bits) {
5596 16 => {
5597 try cg.emitWValue(operand);
5598 try cg.addTag(.f32_demote_f64);
5599 return cg.callIntrinsic(.__truncsfhf2, &.{.f32_type}, Type.f16, &.{.stack});
5600 },
5601 32 => {
5602 try cg.emitWValue(operand);
5603 try cg.addTag(.f32_demote_f64);
5604 return .stack;
5605 },
5606 else => unreachable,
5607 },
5608 80 => switch (wanted_bits) {
5609 16 => .__truncxfhf2,
5610 32 => .__truncxfsf2,
5611 64 => .__truncxfdf2,
5612 else => unreachable,
5613 },
5614 128 => switch (wanted_bits) {
5615 16 => .__trunctfhf2,
5616 32 => .__trunctfsf2,
5617 64 => .__trunctfdf2,
5618 80 => .__trunctfxf2,
5619 else => unreachable,
5620 },
5621 else => unreachable,
5622 };
5623 return cg.callIntrinsic(intrinsic, &.{given.ip_index}, wanted, &.{operand});
5735}5624}
57365625
5737fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5626fn airErrUnionPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5738 const pt = func.pt;5627 const zcu = cg.pt.zcu;
5739 const zcu = pt.zcu;5628 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5740 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
57415629
5742 const err_set_ty = func.typeOf(ty_op.operand).childType(zcu);5630 const err_set_ty = cg.typeOf(ty_op.operand).childType(zcu);
5743 const payload_ty = err_set_ty.errorUnionPayload(zcu);5631 const payload_ty = err_set_ty.errorUnionPayload(zcu);
5744 const operand = try func.resolveInst(ty_op.operand);5632 const operand = try cg.resolveInst(ty_op.operand);
57455633
5746 // set error-tag to '0' to annotate error union is non-error5634 // set error-tag to '0' to annotate error union is non-error
5747 try func.store(5635 try cg.store(
5748 operand,5636 operand,
5749 .{ .imm32 = 0 },5637 .{ .imm32 = 0 },
5750 Type.anyerror,5638 Type.anyerror,
...@@ -5753,63 +5641,60 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi...@@ -5753,63 +5641,60 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi
57535641
5754 const result = result: {5642 const result = result: {
5755 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {5643 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
5756 break :result func.reuseOperand(ty_op.operand, operand);5644 break :result cg.reuseOperand(ty_op.operand, operand);
5757 }5645 }
57585646
5759 break :result try func.buildPointerOffset(operand, @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu))), .new);5647 break :result try cg.buildPointerOffset(operand, @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu))), .new);
5760 };5648 };
5761 return func.finishAir(inst, result, &.{ty_op.operand});5649 return cg.finishAir(inst, result, &.{ty_op.operand});
5762}5650}
57635651
5764fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5652fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5765 const pt = func.pt;5653 const zcu = cg.pt.zcu;
5766 const zcu = pt.zcu;5654 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5767 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5655 const extra = cg.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
5768 const extra = func.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
57695656
5770 const field_ptr = try func.resolveInst(extra.field_ptr);5657 const field_ptr = try cg.resolveInst(extra.field_ptr);
5771 const parent_ty = ty_pl.ty.toType().childType(zcu);5658 const parent_ty = ty_pl.ty.toType().childType(zcu);
5772 const field_offset = parent_ty.structFieldOffset(extra.field_index, zcu);5659 const field_offset = parent_ty.structFieldOffset(extra.field_index, zcu);
57735660
5774 const result = if (field_offset != 0) result: {5661 const result = if (field_offset != 0) result: {
5775 const base = try func.buildPointerOffset(field_ptr, 0, .new);5662 const base = try cg.buildPointerOffset(field_ptr, 0, .new);
5776 try func.addLabel(.local_get, base.local.value);5663 try cg.addLocal(.local_get, base.local.value);
5777 try func.addImm32(@intCast(field_offset));5664 try cg.addImm32(@intCast(field_offset));
5778 try func.addTag(.i32_sub);5665 try cg.addTag(.i32_sub);
5779 try func.addLabel(.local_set, base.local.value);5666 try cg.addLocal(.local_set, base.local.value);
5780 break :result base;5667 break :result base;
5781 } else func.reuseOperand(extra.field_ptr, field_ptr);5668 } else cg.reuseOperand(extra.field_ptr, field_ptr);
57825669
5783 return func.finishAir(inst, result, &.{extra.field_ptr});5670 return cg.finishAir(inst, result, &.{extra.field_ptr});
5784}5671}
57855672
5786fn sliceOrArrayPtr(func: *CodeGen, ptr: WValue, ptr_ty: Type) InnerError!WValue {5673fn sliceOrArrayPtr(cg: *CodeGen, ptr: WValue, ptr_ty: Type) InnerError!WValue {
5787 const pt = func.pt;5674 const zcu = cg.pt.zcu;
5788 const zcu = pt.zcu;
5789 if (ptr_ty.isSlice(zcu)) {5675 if (ptr_ty.isSlice(zcu)) {
5790 return func.slicePtr(ptr);5676 return cg.slicePtr(ptr);
5791 } else {5677 } else {
5792 return ptr;5678 return ptr;
5793 }5679 }
5794}5680}
57955681
5796fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5682fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5797 const pt = func.pt;5683 const zcu = cg.pt.zcu;
5798 const zcu = pt.zcu;5684 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
5799 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;5685 const dst = try cg.resolveInst(bin_op.lhs);
5800 const dst = try func.resolveInst(bin_op.lhs);5686 const dst_ty = cg.typeOf(bin_op.lhs);
5801 const dst_ty = func.typeOf(bin_op.lhs);
5802 const ptr_elem_ty = dst_ty.childType(zcu);5687 const ptr_elem_ty = dst_ty.childType(zcu);
5803 const src = try func.resolveInst(bin_op.rhs);5688 const src = try cg.resolveInst(bin_op.rhs);
5804 const src_ty = func.typeOf(bin_op.rhs);5689 const src_ty = cg.typeOf(bin_op.rhs);
5805 const len = switch (dst_ty.ptrSize(zcu)) {5690 const len = switch (dst_ty.ptrSize(zcu)) {
5806 .Slice => blk: {5691 .Slice => blk: {
5807 const slice_len = try func.sliceLen(dst);5692 const slice_len = try cg.sliceLen(dst);
5808 if (ptr_elem_ty.abiSize(zcu) != 1) {5693 if (ptr_elem_ty.abiSize(zcu) != 1) {
5809 try func.emitWValue(slice_len);5694 try cg.emitWValue(slice_len);
5810 try func.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });5695 try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });
5811 try func.addTag(.i32_mul);5696 try cg.addTag(.i32_mul);
5812 try func.addLabel(.local_set, slice_len.local.value);5697 try cg.addLocal(.local_set, slice_len.local.value);
5813 }5698 }
5814 break :blk slice_len;5699 break :blk slice_len;
5815 },5700 },
...@@ -5818,96 +5703,94 @@ fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5818,96 +5703,94 @@ fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5818 }),5703 }),
5819 .C, .Many => unreachable,5704 .C, .Many => unreachable,
5820 };5705 };
5821 const dst_ptr = try func.sliceOrArrayPtr(dst, dst_ty);5706 const dst_ptr = try cg.sliceOrArrayPtr(dst, dst_ty);
5822 const src_ptr = try func.sliceOrArrayPtr(src, src_ty);5707 const src_ptr = try cg.sliceOrArrayPtr(src, src_ty);
5823 try func.memcpy(dst_ptr, src_ptr, len);5708 try cg.memcpy(dst_ptr, src_ptr, len);
58245709
5825 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });5710 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
5826}5711}
58275712
5828fn airRetAddr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5713fn airRetAddr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5829 // TODO: Implement this properly once stack serialization is solved5714 // TODO: Implement this properly once stack serialization is solved
5830 return func.finishAir(inst, switch (func.arch()) {5715 return cg.finishAir(inst, switch (cg.ptr_size) {
5831 .wasm32 => .{ .imm32 = 0 },5716 .wasm32 => .{ .imm32 = 0 },
5832 .wasm64 => .{ .imm64 = 0 },5717 .wasm64 => .{ .imm64 = 0 },
5833 else => unreachable,
5834 }, &.{});5718 }, &.{});
5835}5719}
58365720
5837fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5721fn airPopcount(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5838 const pt = func.pt;5722 const pt = cg.pt;
5839 const zcu = pt.zcu;5723 const zcu = pt.zcu;
5840 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5724 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
58415725
5842 const operand = try func.resolveInst(ty_op.operand);5726 const operand = try cg.resolveInst(ty_op.operand);
5843 const op_ty = func.typeOf(ty_op.operand);5727 const op_ty = cg.typeOf(ty_op.operand);
58445728
5845 if (op_ty.zigTypeTag(zcu) == .vector) {5729 if (op_ty.zigTypeTag(zcu) == .vector) {
5846 return func.fail("TODO: Implement @popCount for vectors", .{});5730 return cg.fail("TODO: Implement @popCount for vectors", .{});
5847 }5731 }
58485732
5849 const int_info = op_ty.intInfo(zcu);5733 const int_info = op_ty.intInfo(zcu);
5850 const bits = int_info.bits;5734 const bits = int_info.bits;
5851 const wasm_bits = toWasmBits(bits) orelse {5735 const wasm_bits = toWasmBits(bits) orelse {
5852 return func.fail("TODO: Implement @popCount for integers with bitsize '{d}'", .{bits});5736 return cg.fail("TODO: Implement @popCount for integers with bitsize '{d}'", .{bits});
5853 };5737 };
58545738
5855 switch (wasm_bits) {5739 switch (wasm_bits) {
5856 32 => {5740 32 => {
5857 try func.emitWValue(operand);5741 try cg.emitWValue(operand);
5858 if (op_ty.isSignedInt(zcu) and bits != wasm_bits) {5742 if (op_ty.isSignedInt(zcu) and bits != wasm_bits) {
5859 _ = try func.wrapOperand(.stack, try pt.intType(.unsigned, bits));5743 _ = try cg.wrapOperand(.stack, try pt.intType(.unsigned, bits));
5860 }5744 }
5861 try func.addTag(.i32_popcnt);5745 try cg.addTag(.i32_popcnt);
5862 },5746 },
5863 64 => {5747 64 => {
5864 try func.emitWValue(operand);5748 try cg.emitWValue(operand);
5865 if (op_ty.isSignedInt(zcu) and bits != wasm_bits) {5749 if (op_ty.isSignedInt(zcu) and bits != wasm_bits) {
5866 _ = try func.wrapOperand(.stack, try pt.intType(.unsigned, bits));5750 _ = try cg.wrapOperand(.stack, try pt.intType(.unsigned, bits));
5867 }5751 }
5868 try func.addTag(.i64_popcnt);5752 try cg.addTag(.i64_popcnt);
5869 try func.addTag(.i32_wrap_i64);5753 try cg.addTag(.i32_wrap_i64);
5870 try func.emitWValue(operand);5754 try cg.emitWValue(operand);
5871 },5755 },
5872 128 => {5756 128 => {
5873 _ = try func.load(operand, Type.u64, 0);5757 _ = try cg.load(operand, Type.u64, 0);
5874 try func.addTag(.i64_popcnt);5758 try cg.addTag(.i64_popcnt);
5875 _ = try func.load(operand, Type.u64, 8);5759 _ = try cg.load(operand, Type.u64, 8);
5876 if (op_ty.isSignedInt(zcu) and bits != wasm_bits) {5760 if (op_ty.isSignedInt(zcu) and bits != wasm_bits) {
5877 _ = try func.wrapOperand(.stack, try pt.intType(.unsigned, bits - 64));5761 _ = try cg.wrapOperand(.stack, try pt.intType(.unsigned, bits - 64));
5878 }5762 }
5879 try func.addTag(.i64_popcnt);5763 try cg.addTag(.i64_popcnt);
5880 try func.addTag(.i64_add);5764 try cg.addTag(.i64_add);
5881 try func.addTag(.i32_wrap_i64);5765 try cg.addTag(.i32_wrap_i64);
5882 },5766 },
5883 else => unreachable,5767 else => unreachable,
5884 }5768 }
58855769
5886 return func.finishAir(inst, .stack, &.{ty_op.operand});5770 return cg.finishAir(inst, .stack, &.{ty_op.operand});
5887}5771}
58885772
5889fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5773fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5890 const pt = func.pt;5774 const zcu = cg.pt.zcu;
5891 const zcu = pt.zcu;5775 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5892 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
58935776
5894 const operand = try func.resolveInst(ty_op.operand);5777 const operand = try cg.resolveInst(ty_op.operand);
5895 const ty = func.typeOf(ty_op.operand);5778 const ty = cg.typeOf(ty_op.operand);
58965779
5897 if (ty.zigTypeTag(zcu) == .vector) {5780 if (ty.zigTypeTag(zcu) == .vector) {
5898 return func.fail("TODO: Implement @bitReverse for vectors", .{});5781 return cg.fail("TODO: Implement @bitReverse for vectors", .{});
5899 }5782 }
59005783
5901 const int_info = ty.intInfo(zcu);5784 const int_info = ty.intInfo(zcu);
5902 const bits = int_info.bits;5785 const bits = int_info.bits;
5903 const wasm_bits = toWasmBits(bits) orelse {5786 const wasm_bits = toWasmBits(bits) orelse {
5904 return func.fail("TODO: Implement @bitReverse for integers with bitsize '{d}'", .{bits});5787 return cg.fail("TODO: Implement @bitReverse for integers with bitsize '{d}'", .{bits});
5905 };5788 };
59065789
5907 switch (wasm_bits) {5790 switch (wasm_bits) {
5908 32 => {5791 32 => {
5909 const intrin_ret = try func.callIntrinsic(5792 const intrin_ret = try cg.callIntrinsic(
5910 "__bitreversesi2",5793 .__bitreversesi2,
5911 &.{.u32_type},5794 &.{.u32_type},
5912 Type.u32,5795 Type.u32,
5913 &.{operand},5796 &.{operand},
...@@ -5915,12 +5798,12 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5915,12 +5798,12 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5915 const result = if (bits == 32)5798 const result = if (bits == 32)
5916 intrin_ret5799 intrin_ret
5917 else5800 else
5918 try func.binOp(intrin_ret, .{ .imm32 = 32 - bits }, ty, .shr);5801 try cg.binOp(intrin_ret, .{ .imm32 = 32 - bits }, ty, .shr);
5919 return func.finishAir(inst, result, &.{ty_op.operand});5802 return cg.finishAir(inst, result, &.{ty_op.operand});
5920 },5803 },
5921 64 => {5804 64 => {
5922 const intrin_ret = try func.callIntrinsic(5805 const intrin_ret = try cg.callIntrinsic(
5923 "__bitreversedi2",5806 .__bitreversedi2,
5924 &.{.u64_type},5807 &.{.u64_type},
5925 Type.u64,5808 Type.u64,
5926 &.{operand},5809 &.{operand},
...@@ -5928,68 +5811,63 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5928,68 +5811,63 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5928 const result = if (bits == 64)5811 const result = if (bits == 64)
5929 intrin_ret5812 intrin_ret
5930 else5813 else
5931 try func.binOp(intrin_ret, .{ .imm64 = 64 - bits }, ty, .shr);5814 try cg.binOp(intrin_ret, .{ .imm64 = 64 - bits }, ty, .shr);
5932 return func.finishAir(inst, result, &.{ty_op.operand});5815 return cg.finishAir(inst, result, &.{ty_op.operand});
5933 },5816 },
5934 128 => {5817 128 => {
5935 const result = try func.allocStack(ty);5818 const result = try cg.allocStack(ty);
59365819
5937 try func.emitWValue(result);5820 try cg.emitWValue(result);
5938 const first_half = try func.load(operand, Type.u64, 8);5821 const first_half = try cg.load(operand, Type.u64, 8);
5939 const intrin_ret_first = try func.callIntrinsic(5822 const intrin_ret_first = try cg.callIntrinsic(
5940 "__bitreversedi2",5823 .__bitreversedi2,
5941 &.{.u64_type},5824 &.{.u64_type},
5942 Type.u64,5825 Type.u64,
5943 &.{first_half},5826 &.{first_half},
5944 );5827 );
5945 try func.emitWValue(intrin_ret_first);5828 try cg.emitWValue(intrin_ret_first);
5946 if (bits < 128) {5829 if (bits < 128) {
5947 try func.emitWValue(.{ .imm64 = 128 - bits });5830 try cg.emitWValue(.{ .imm64 = 128 - bits });
5948 try func.addTag(.i64_shr_u);5831 try cg.addTag(.i64_shr_u);
5949 }5832 }
5950 try func.emitWValue(result);5833 try cg.emitWValue(result);
5951 const second_half = try func.load(operand, Type.u64, 0);5834 const second_half = try cg.load(operand, Type.u64, 0);
5952 const intrin_ret_second = try func.callIntrinsic(5835 const intrin_ret_second = try cg.callIntrinsic(
5953 "__bitreversedi2",5836 .__bitreversedi2,
5954 &.{.u64_type},5837 &.{.u64_type},
5955 Type.u64,5838 Type.u64,
5956 &.{second_half},5839 &.{second_half},
5957 );5840 );
5958 try func.emitWValue(intrin_ret_second);5841 try cg.emitWValue(intrin_ret_second);
5959 if (bits == 128) {5842 if (bits == 128) {
5960 try func.store(.stack, .stack, Type.u64, result.offset() + 8);5843 try cg.store(.stack, .stack, Type.u64, result.offset() + 8);
5961 try func.store(.stack, .stack, Type.u64, result.offset());5844 try cg.store(.stack, .stack, Type.u64, result.offset());
5962 } else {5845 } else {
5963 var tmp = try func.allocLocal(Type.u64);5846 var tmp = try cg.allocLocal(Type.u64);
5964 defer tmp.free(func);5847 defer tmp.free(cg);
5965 try func.addLabel(.local_tee, tmp.local.value);5848 try cg.addLocal(.local_tee, tmp.local.value);
5966 try func.emitWValue(.{ .imm64 = 128 - bits });5849 try cg.emitWValue(.{ .imm64 = 128 - bits });
5967 if (ty.isSignedInt(zcu)) {5850 if (ty.isSignedInt(zcu)) {
5968 try func.addTag(.i64_shr_s);5851 try cg.addTag(.i64_shr_s);
5969 } else {5852 } else {
5970 try func.addTag(.i64_shr_u);5853 try cg.addTag(.i64_shr_u);
5971 }5854 }
5972 try func.store(.stack, .stack, Type.u64, result.offset() + 8);5855 try cg.store(.stack, .stack, Type.u64, result.offset() + 8);
5973 try func.addLabel(.local_get, tmp.local.value);5856 try cg.addLocal(.local_get, tmp.local.value);
5974 try func.emitWValue(.{ .imm64 = bits - 64 });5857 try cg.emitWValue(.{ .imm64 = bits - 64 });
5975 try func.addTag(.i64_shl);5858 try cg.addTag(.i64_shl);
5976 try func.addTag(.i64_or);5859 try cg.addTag(.i64_or);
5977 try func.store(.stack, .stack, Type.u64, result.offset());5860 try cg.store(.stack, .stack, Type.u64, result.offset());
5978 }5861 }
5979 return func.finishAir(inst, result, &.{ty_op.operand});5862 return cg.finishAir(inst, result, &.{ty_op.operand});
5980 },5863 },
5981 else => unreachable,5864 else => unreachable,
5982 }5865 }
5983}5866}
59845867
5985fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5868fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5986 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;5869 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
59875870 const operand = try cg.resolveInst(un_op);
5988 const operand = try func.resolveInst(un_op);
5989 // First retrieve the symbol index to the error name table
5990 // that will be used to emit a relocation for the pointer
5991 // to the error name table.
5992 //
5993 // Each entry to this table is a slice (ptr+len).5871 // Each entry to this table is a slice (ptr+len).
5994 // The operand in this instruction represents the index within this table.5872 // The operand in this instruction represents the index within this table.
5995 // This means to get the final name, we emit the base pointer and then perform5873 // This means to get the final name, we emit the base pointer and then perform
...@@ -5997,82 +5875,82 @@ fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5997,82 +5875,82 @@ fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5997 //5875 //
5998 // As the names are global and the slice elements are constant, we do not have5876 // As the names are global and the slice elements are constant, we do not have
5999 // to make a copy of the ptr+value but can point towards them directly.5877 // to make a copy of the ptr+value but can point towards them directly.
6000 const pt = func.pt;5878 const pt = cg.pt;
6001 const error_table_symbol = try func.bin_file.getErrorTableSymbol(pt);
6002 const name_ty = Type.slice_const_u8_sentinel_0;5879 const name_ty = Type.slice_const_u8_sentinel_0;
6003 const abi_size = name_ty.abiSize(pt.zcu);5880 const abi_size = name_ty.abiSize(pt.zcu);
60045881
6005 const error_name_value: WValue = .{ .memory = error_table_symbol }; // emitting this will create a relocation5882 cg.wasm.error_name_table_ref_count += 1;
6006 try func.emitWValue(error_name_value);5883
6007 try func.emitWValue(operand);5884 // Lowers to a i32.const or i64.const with the error table memory address.
6008 switch (func.arch()) {5885 try cg.addTag(.error_name_table_ref);
5886 try cg.emitWValue(operand);
5887 switch (cg.ptr_size) {
6009 .wasm32 => {5888 .wasm32 => {
6010 try func.addImm32(@intCast(abi_size));5889 try cg.addImm32(@intCast(abi_size));
6011 try func.addTag(.i32_mul);5890 try cg.addTag(.i32_mul);
6012 try func.addTag(.i32_add);5891 try cg.addTag(.i32_add);
6013 },5892 },
6014 .wasm64 => {5893 .wasm64 => {
6015 try func.addImm64(abi_size);5894 try cg.addImm64(abi_size);
6016 try func.addTag(.i64_mul);5895 try cg.addTag(.i64_mul);
6017 try func.addTag(.i64_add);5896 try cg.addTag(.i64_add);
6018 },5897 },
6019 else => unreachable,
6020 }5898 }
60215899
6022 return func.finishAir(inst, .stack, &.{un_op});5900 return cg.finishAir(inst, .stack, &.{un_op});
6023}5901}
60245902
6025fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void {5903fn airPtrSliceFieldPtr(cg: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void {
6026 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5904 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6027 const slice_ptr = try func.resolveInst(ty_op.operand);5905 const slice_ptr = try cg.resolveInst(ty_op.operand);
6028 const result = try func.buildPointerOffset(slice_ptr, offset, .new);5906 const result = try cg.buildPointerOffset(slice_ptr, offset, .new);
6029 return func.finishAir(inst, result, &.{ty_op.operand});5907 return cg.finishAir(inst, result, &.{ty_op.operand});
6030}5908}
60315909
6032/// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits5910/// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits
6033fn intZeroValue(func: *CodeGen, ty: Type) InnerError!WValue {5911fn intZeroValue(cg: *CodeGen, ty: Type) InnerError!WValue {
6034 const zcu = func.bin_file.base.comp.zcu.?;5912 const zcu = cg.wasm.base.comp.zcu.?;
6035 const int_info = ty.intInfo(zcu);5913 const int_info = ty.intInfo(zcu);
6036 const wasm_bits = toWasmBits(int_info.bits) orelse {5914 const wasm_bits = toWasmBits(int_info.bits) orelse {
6037 return func.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits});5915 return cg.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits});
6038 };5916 };
6039 switch (wasm_bits) {5917 switch (wasm_bits) {
6040 32 => return .{ .imm32 = 0 },5918 32 => return .{ .imm32 = 0 },
6041 64 => return .{ .imm64 = 0 },5919 64 => return .{ .imm64 = 0 },
6042 128 => {5920 128 => {
6043 const result = try func.allocStack(ty);5921 const result = try cg.allocStack(ty);
6044 try func.store(result, .{ .imm64 = 0 }, Type.u64, 0);5922 try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0);
6045 try func.store(result, .{ .imm64 = 0 }, Type.u64, 8);5923 try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8);
6046 return result;5924 return result;
6047 },5925 },
6048 else => unreachable,5926 else => unreachable,
6049 }5927 }
6050}5928}
60515929
6052fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {5930fn airAddSubWithOverflow(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
6053 assert(op == .add or op == .sub);5931 assert(op == .add or op == .sub);
6054 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5932 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6055 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;5933 const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;
60565934
6057 const lhs = try func.resolveInst(extra.lhs);5935 const lhs = try cg.resolveInst(extra.lhs);
6058 const rhs = try func.resolveInst(extra.rhs);5936 const rhs = try cg.resolveInst(extra.rhs);
6059 const ty = func.typeOf(extra.lhs);5937 const ty = cg.typeOf(extra.lhs);
6060 const pt = func.pt;5938 const pt = cg.pt;
6061 const zcu = pt.zcu;5939 const zcu = pt.zcu;
60625940
6063 if (ty.zigTypeTag(zcu) == .vector) {5941 if (ty.zigTypeTag(zcu) == .vector) {
6064 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});5942 return cg.fail("TODO: Implement overflow arithmetic for vectors", .{});
6065 }5943 }
60665944
6067 const int_info = ty.intInfo(zcu);5945 const int_info = ty.intInfo(zcu);
6068 const is_signed = int_info.signedness == .signed;5946 const is_signed = int_info.signedness == .signed;
6069 if (int_info.bits > 128) {5947 if (int_info.bits > 128) {
6070 return func.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits});5948 return cg.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits});
6071 }5949 }
60725950
6073 const op_result = try func.wrapBinOp(lhs, rhs, ty, op);5951 const op_result = try cg.wrapBinOp(lhs, rhs, ty, op);
6074 var op_tmp = try op_result.toLocal(func, ty);5952 var op_tmp = try op_result.toLocal(cg, ty);
6075 defer op_tmp.free(func);5953 defer op_tmp.free(cg);
60765954
6077 const cmp_op: std.math.CompareOperator = switch (op) {5955 const cmp_op: std.math.CompareOperator = switch (op) {
6078 .add => .lt,5956 .add => .lt,
...@@ -6080,40 +5958,40 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro...@@ -6080,40 +5958,40 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
6080 else => unreachable,5958 else => unreachable,
6081 };5959 };
6082 const overflow_bit = if (is_signed) blk: {5960 const overflow_bit = if (is_signed) blk: {
6083 const zero = try intZeroValue(func, ty);5961 const zero = try intZeroValue(cg, ty);
6084 const rhs_is_neg = try func.cmp(rhs, zero, ty, .lt);5962 const rhs_is_neg = try cg.cmp(rhs, zero, ty, .lt);
6085 const overflow_cmp = try func.cmp(op_tmp, lhs, ty, cmp_op);5963 const overflow_cmp = try cg.cmp(op_tmp, lhs, ty, cmp_op);
6086 break :blk try func.cmp(rhs_is_neg, overflow_cmp, Type.u1, .neq);5964 break :blk try cg.cmp(rhs_is_neg, overflow_cmp, Type.u1, .neq);
6087 } else try func.cmp(op_tmp, lhs, ty, cmp_op);5965 } else try cg.cmp(op_tmp, lhs, ty, cmp_op);
6088 var bit_tmp = try overflow_bit.toLocal(func, Type.u1);5966 var bit_tmp = try overflow_bit.toLocal(cg, Type.u1);
6089 defer bit_tmp.free(func);5967 defer bit_tmp.free(cg);
60905968
6091 const result = try func.allocStack(func.typeOfIndex(inst));5969 const result = try cg.allocStack(cg.typeOfIndex(inst));
6092 const offset: u32 = @intCast(ty.abiSize(zcu));5970 const offset: u32 = @intCast(ty.abiSize(zcu));
6093 try func.store(result, op_tmp, ty, 0);5971 try cg.store(result, op_tmp, ty, 0);
6094 try func.store(result, bit_tmp, Type.u1, offset);5972 try cg.store(result, bit_tmp, Type.u1, offset);
60955973
6096 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });5974 return cg.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
6097}5975}
60985976
6099fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5977fn airShlWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6100 const pt = func.pt;5978 const pt = cg.pt;
6101 const zcu = pt.zcu;5979 const zcu = pt.zcu;
6102 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5980 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6103 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;5981 const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;
61045982
6105 const lhs = try func.resolveInst(extra.lhs);5983 const lhs = try cg.resolveInst(extra.lhs);
6106 const rhs = try func.resolveInst(extra.rhs);5984 const rhs = try cg.resolveInst(extra.rhs);
6107 const ty = func.typeOf(extra.lhs);5985 const ty = cg.typeOf(extra.lhs);
6108 const rhs_ty = func.typeOf(extra.rhs);5986 const rhs_ty = cg.typeOf(extra.rhs);
61095987
6110 if (ty.zigTypeTag(zcu) == .vector) {5988 if (ty.zigTypeTag(zcu) == .vector) {
6111 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});5989 return cg.fail("TODO: Implement overflow arithmetic for vectors", .{});
6112 }5990 }
61135991
6114 const int_info = ty.intInfo(zcu);5992 const int_info = ty.intInfo(zcu);
6115 const wasm_bits = toWasmBits(int_info.bits) orelse {5993 const wasm_bits = toWasmBits(int_info.bits) orelse {
6116 return func.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});5994 return cg.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});
6117 };5995 };
61185996
6119 // Ensure rhs is coerced to lhs as they must have the same WebAssembly types5997 // Ensure rhs is coerced to lhs as they must have the same WebAssembly types
...@@ -6121,50 +5999,50 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6121,50 +5999,50 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6121 const rhs_wasm_bits = toWasmBits(rhs_ty.intInfo(zcu).bits).?;5999 const rhs_wasm_bits = toWasmBits(rhs_ty.intInfo(zcu).bits).?;
6122 // If wasm_bits == 128, compiler-rt expects i32 for shift6000 // If wasm_bits == 128, compiler-rt expects i32 for shift
6123 const rhs_final = if (wasm_bits != rhs_wasm_bits and wasm_bits == 64) blk: {6001 const rhs_final = if (wasm_bits != rhs_wasm_bits and wasm_bits == 64) blk: {
6124 const rhs_casted = try func.intcast(rhs, rhs_ty, ty);6002 const rhs_casted = try cg.intcast(rhs, rhs_ty, ty);
6125 break :blk try rhs_casted.toLocal(func, ty);6003 break :blk try rhs_casted.toLocal(cg, ty);
6126 } else rhs;6004 } else rhs;
61276005
6128 var shl = try (try func.wrapBinOp(lhs, rhs_final, ty, .shl)).toLocal(func, ty);6006 var shl = try (try cg.wrapBinOp(lhs, rhs_final, ty, .shl)).toLocal(cg, ty);
6129 defer shl.free(func);6007 defer shl.free(cg);
61306008
6131 const overflow_bit = blk: {6009 const overflow_bit = blk: {
6132 const shr = try func.binOp(shl, rhs_final, ty, .shr);6010 const shr = try cg.binOp(shl, rhs_final, ty, .shr);
6133 break :blk try func.cmp(shr, lhs, ty, .neq);6011 break :blk try cg.cmp(shr, lhs, ty, .neq);
6134 };6012 };
6135 var overflow_local = try overflow_bit.toLocal(func, Type.u1);6013 var overflow_local = try overflow_bit.toLocal(cg, Type.u1);
6136 defer overflow_local.free(func);6014 defer overflow_local.free(cg);
61376015
6138 const result = try func.allocStack(func.typeOfIndex(inst));6016 const result = try cg.allocStack(cg.typeOfIndex(inst));
6139 const offset: u32 = @intCast(ty.abiSize(zcu));6017 const offset: u32 = @intCast(ty.abiSize(zcu));
6140 try func.store(result, shl, ty, 0);6018 try cg.store(result, shl, ty, 0);
6141 try func.store(result, overflow_local, Type.u1, offset);6019 try cg.store(result, overflow_local, Type.u1, offset);
61426020
6143 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });6021 return cg.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
6144}6022}
61456023
6146fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6024fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6147 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6025 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6148 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;6026 const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;
61496027
6150 const lhs = try func.resolveInst(extra.lhs);6028 const lhs = try cg.resolveInst(extra.lhs);
6151 const rhs = try func.resolveInst(extra.rhs);6029 const rhs = try cg.resolveInst(extra.rhs);
6152 const ty = func.typeOf(extra.lhs);6030 const ty = cg.typeOf(extra.lhs);
6153 const pt = func.pt;6031 const pt = cg.pt;
6154 const zcu = pt.zcu;6032 const zcu = pt.zcu;
61556033
6156 if (ty.zigTypeTag(zcu) == .vector) {6034 if (ty.zigTypeTag(zcu) == .vector) {
6157 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});6035 return cg.fail("TODO: Implement overflow arithmetic for vectors", .{});
6158 }6036 }
61596037
6160 // We store the bit if it's overflowed or not in this. As it's zero-initialized6038 // We store the bit if it's overflowed or not in this. As it's zero-initialized
6161 // we only need to update it if an overflow (or underflow) occurred.6039 // we only need to update it if an overflow (or underflow) occurred.
6162 var overflow_bit = try func.ensureAllocLocal(Type.u1);6040 var overflow_bit = try cg.ensureAllocLocal(Type.u1);
6163 defer overflow_bit.free(func);6041 defer overflow_bit.free(cg);
61646042
6165 const int_info = ty.intInfo(zcu);6043 const int_info = ty.intInfo(zcu);
6166 const wasm_bits = toWasmBits(int_info.bits) orelse {6044 const wasm_bits = toWasmBits(int_info.bits) orelse {
6167 return func.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits});6045 return cg.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits});
6168 };6046 };
61696047
6170 const zero: WValue = switch (wasm_bits) {6048 const zero: WValue = switch (wasm_bits) {
...@@ -6176,248 +6054,250 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6176,248 +6054,250 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6176 // for 32 bit integers we upcast it to a 64bit integer6054 // for 32 bit integers we upcast it to a 64bit integer
6177 const mul = if (wasm_bits == 32) blk: {6055 const mul = if (wasm_bits == 32) blk: {
6178 const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64;6056 const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64;
6179 const lhs_upcast = try func.intcast(lhs, ty, new_ty);6057 const lhs_upcast = try cg.intcast(lhs, ty, new_ty);
6180 const rhs_upcast = try func.intcast(rhs, ty, new_ty);6058 const rhs_upcast = try cg.intcast(rhs, ty, new_ty);
6181 const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty);6059 const bin_op = try (try cg.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(cg, new_ty);
6182 const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty);6060 const res = try (try cg.trunc(bin_op, ty, new_ty)).toLocal(cg, ty);
6183 const res_upcast = try func.intcast(res, ty, new_ty);6061 const res_upcast = try cg.intcast(res, ty, new_ty);
6184 _ = try func.cmp(res_upcast, bin_op, new_ty, .neq);6062 _ = try cg.cmp(res_upcast, bin_op, new_ty, .neq);
6185 try func.addLabel(.local_set, overflow_bit.local.value);6063 try cg.addLocal(.local_set, overflow_bit.local.value);
6186 break :blk res;6064 break :blk res;
6187 } else if (wasm_bits == 64) blk: {6065 } else if (wasm_bits == 64) blk: {
6188 const new_ty = if (int_info.signedness == .signed) Type.i128 else Type.u128;6066 const new_ty = if (int_info.signedness == .signed) Type.i128 else Type.u128;
6189 const lhs_upcast = try func.intcast(lhs, ty, new_ty);6067 const lhs_upcast = try cg.intcast(lhs, ty, new_ty);
6190 const rhs_upcast = try func.intcast(rhs, ty, new_ty);6068 const rhs_upcast = try cg.intcast(rhs, ty, new_ty);
6191 const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty);6069 const bin_op = try (try cg.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(cg, new_ty);
6192 const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty);6070 const res = try (try cg.trunc(bin_op, ty, new_ty)).toLocal(cg, ty);
6193 const res_upcast = try func.intcast(res, ty, new_ty);6071 const res_upcast = try cg.intcast(res, ty, new_ty);
6194 _ = try func.cmp(res_upcast, bin_op, new_ty, .neq);6072 _ = try cg.cmp(res_upcast, bin_op, new_ty, .neq);
6195 try func.addLabel(.local_set, overflow_bit.local.value);6073 try cg.addLocal(.local_set, overflow_bit.local.value);
6196 break :blk res;6074 break :blk res;
6197 } else if (int_info.bits == 128 and int_info.signedness == .unsigned) blk: {6075 } else if (int_info.bits == 128 and int_info.signedness == .unsigned) blk: {
6198 var lhs_lsb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);6076 var lhs_lsb = try (try cg.load(lhs, Type.u64, 0)).toLocal(cg, Type.u64);
6199 defer lhs_lsb.free(func);6077 defer lhs_lsb.free(cg);
6200 var lhs_msb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);6078 var lhs_msb = try (try cg.load(lhs, Type.u64, 8)).toLocal(cg, Type.u64);
6201 defer lhs_msb.free(func);6079 defer lhs_msb.free(cg);
6202 var rhs_lsb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);6080 var rhs_lsb = try (try cg.load(rhs, Type.u64, 0)).toLocal(cg, Type.u64);
6203 defer rhs_lsb.free(func);6081 defer rhs_lsb.free(cg);
6204 var rhs_msb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);6082 var rhs_msb = try (try cg.load(rhs, Type.u64, 8)).toLocal(cg, Type.u64);
6205 defer rhs_msb.free(func);6083 defer rhs_msb.free(cg);
62066084
6207 const cross_1 = try func.callIntrinsic(6085 const cross_1 = try cg.callIntrinsic(
6208 "__multi3",6086 .__multi3,
6209 &[_]InternPool.Index{.i64_type} ** 4,6087 &[_]InternPool.Index{.i64_type} ** 4,
6210 Type.i128,6088 Type.i128,
6211 &.{ lhs_msb, zero, rhs_lsb, zero },6089 &.{ lhs_msb, zero, rhs_lsb, zero },
6212 );6090 );
6213 const cross_2 = try func.callIntrinsic(6091 const cross_2 = try cg.callIntrinsic(
6214 "__multi3",6092 .__multi3,
6215 &[_]InternPool.Index{.i64_type} ** 4,6093 &[_]InternPool.Index{.i64_type} ** 4,
6216 Type.i128,6094 Type.i128,
6217 &.{ rhs_msb, zero, lhs_lsb, zero },6095 &.{ rhs_msb, zero, lhs_lsb, zero },
6218 );6096 );
6219 const mul_lsb = try func.callIntrinsic(6097 const mul_lsb = try cg.callIntrinsic(
6220 "__multi3",6098 .__multi3,
6221 &[_]InternPool.Index{.i64_type} ** 4,6099 &[_]InternPool.Index{.i64_type} ** 4,
6222 Type.i128,6100 Type.i128,
6223 &.{ rhs_lsb, zero, lhs_lsb, zero },6101 &.{ rhs_lsb, zero, lhs_lsb, zero },
6224 );6102 );
62256103
6226 const rhs_msb_not_zero = try func.cmp(rhs_msb, zero, Type.u64, .neq);6104 const rhs_msb_not_zero = try cg.cmp(rhs_msb, zero, Type.u64, .neq);
6227 const lhs_msb_not_zero = try func.cmp(lhs_msb, zero, Type.u64, .neq);6105 const lhs_msb_not_zero = try cg.cmp(lhs_msb, zero, Type.u64, .neq);
6228 const both_msb_not_zero = try func.binOp(rhs_msb_not_zero, lhs_msb_not_zero, Type.bool, .@"and");6106 const both_msb_not_zero = try cg.binOp(rhs_msb_not_zero, lhs_msb_not_zero, Type.bool, .@"and");
6229 const cross_1_msb = try func.load(cross_1, Type.u64, 8);6107 const cross_1_msb = try cg.load(cross_1, Type.u64, 8);
6230 const cross_1_msb_not_zero = try func.cmp(cross_1_msb, zero, Type.u64, .neq);6108 const cross_1_msb_not_zero = try cg.cmp(cross_1_msb, zero, Type.u64, .neq);
6231 const cond_1 = try func.binOp(both_msb_not_zero, cross_1_msb_not_zero, Type.bool, .@"or");6109 const cond_1 = try cg.binOp(both_msb_not_zero, cross_1_msb_not_zero, Type.bool, .@"or");
6232 const cross_2_msb = try func.load(cross_2, Type.u64, 8);6110 const cross_2_msb = try cg.load(cross_2, Type.u64, 8);
6233 const cross_2_msb_not_zero = try func.cmp(cross_2_msb, zero, Type.u64, .neq);6111 const cross_2_msb_not_zero = try cg.cmp(cross_2_msb, zero, Type.u64, .neq);
6234 const cond_2 = try func.binOp(cond_1, cross_2_msb_not_zero, Type.bool, .@"or");6112 const cond_2 = try cg.binOp(cond_1, cross_2_msb_not_zero, Type.bool, .@"or");
62356113
6236 const cross_1_lsb = try func.load(cross_1, Type.u64, 0);6114 const cross_1_lsb = try cg.load(cross_1, Type.u64, 0);
6237 const cross_2_lsb = try func.load(cross_2, Type.u64, 0);6115 const cross_2_lsb = try cg.load(cross_2, Type.u64, 0);
6238 const cross_add = try func.binOp(cross_1_lsb, cross_2_lsb, Type.u64, .add);6116 const cross_add = try cg.binOp(cross_1_lsb, cross_2_lsb, Type.u64, .add);
62396117
6240 var mul_lsb_msb = try (try func.load(mul_lsb, Type.u64, 8)).toLocal(func, Type.u64);6118 var mul_lsb_msb = try (try cg.load(mul_lsb, Type.u64, 8)).toLocal(cg, Type.u64);
6241 defer mul_lsb_msb.free(func);6119 defer mul_lsb_msb.free(cg);
6242 var all_add = try (try func.binOp(cross_add, mul_lsb_msb, Type.u64, .add)).toLocal(func, Type.u64);6120 var all_add = try (try cg.binOp(cross_add, mul_lsb_msb, Type.u64, .add)).toLocal(cg, Type.u64);
6243 defer all_add.free(func);6121 defer all_add.free(cg);
6244 const add_overflow = try func.cmp(all_add, mul_lsb_msb, Type.u64, .lt);6122 const add_overflow = try cg.cmp(all_add, mul_lsb_msb, Type.u64, .lt);
62456123
6246 // result for overflow bit6124 // result for overflow bit
6247 _ = try func.binOp(cond_2, add_overflow, Type.bool, .@"or");6125 _ = try cg.binOp(cond_2, add_overflow, Type.bool, .@"or");
6248 try func.addLabel(.local_set, overflow_bit.local.value);6126 try cg.addLocal(.local_set, overflow_bit.local.value);
62496127
6250 const tmp_result = try func.allocStack(Type.u128);6128 const tmp_result = try cg.allocStack(Type.u128);
6251 try func.emitWValue(tmp_result);6129 try cg.emitWValue(tmp_result);
6252 const mul_lsb_lsb = try func.load(mul_lsb, Type.u64, 0);6130 const mul_lsb_lsb = try cg.load(mul_lsb, Type.u64, 0);
6253 try func.store(.stack, mul_lsb_lsb, Type.u64, tmp_result.offset());6131 try cg.store(.stack, mul_lsb_lsb, Type.u64, tmp_result.offset());
6254 try func.store(tmp_result, all_add, Type.u64, 8);6132 try cg.store(tmp_result, all_add, Type.u64, 8);
6255 break :blk tmp_result;6133 break :blk tmp_result;
6256 } else if (int_info.bits == 128 and int_info.signedness == .signed) blk: {6134 } else if (int_info.bits == 128 and int_info.signedness == .signed) blk: {
6257 const overflow_ret = try func.allocStack(Type.i32);6135 const overflow_ret = try cg.allocStack(Type.i32);
6258 const res = try func.callIntrinsic(6136 const res = try cg.callIntrinsic(
6259 "__muloti4",6137 .__muloti4,
6260 &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type },6138 &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type },
6261 Type.i128,6139 Type.i128,
6262 &.{ lhs, rhs, overflow_ret },6140 &.{ lhs, rhs, overflow_ret },
6263 );6141 );
6264 _ = try func.load(overflow_ret, Type.i32, 0);6142 _ = try cg.load(overflow_ret, Type.i32, 0);
6265 try func.addLabel(.local_set, overflow_bit.local.value);6143 try cg.addLocal(.local_set, overflow_bit.local.value);
6266 break :blk res;6144 break :blk res;
6267 } else return func.fail("TODO: @mulWithOverflow for {}", .{ty.fmt(pt)});6145 } else return cg.fail("TODO: @mulWithOverflow for {}", .{ty.fmt(pt)});
6268 var bin_op_local = try mul.toLocal(func, ty);6146 var bin_op_local = try mul.toLocal(cg, ty);
6269 defer bin_op_local.free(func);6147 defer bin_op_local.free(cg);
62706148
6271 const result = try func.allocStack(func.typeOfIndex(inst));6149 const result = try cg.allocStack(cg.typeOfIndex(inst));
6272 const offset: u32 = @intCast(ty.abiSize(zcu));6150 const offset: u32 = @intCast(ty.abiSize(zcu));
6273 try func.store(result, bin_op_local, ty, 0);6151 try cg.store(result, bin_op_local, ty, 0);
6274 try func.store(result, overflow_bit, Type.u1, offset);6152 try cg.store(result, overflow_bit, Type.u1, offset);
62756153
6276 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });6154 return cg.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
6277}6155}
62786156
6279fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {6157fn airMaxMin(
6280 assert(op == .max or op == .min);6158 cg: *CodeGen,
6281 const pt = func.pt;6159 inst: Air.Inst.Index,
6282 const zcu = pt.zcu;6160 op: enum { fmax, fmin },
6283 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6161 cmp_op: std.math.CompareOperator,
6162) InnerError!void {
6163 const zcu = cg.pt.zcu;
6164 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
62846165
6285 const ty = func.typeOfIndex(inst);6166 const ty = cg.typeOfIndex(inst);
6286 if (ty.zigTypeTag(zcu) == .vector) {6167 if (ty.zigTypeTag(zcu) == .vector) {
6287 return func.fail("TODO: `@maximum` and `@minimum` for vectors", .{});6168 return cg.fail("TODO: `@maximum` and `@minimum` for vectors", .{});
6288 }6169 }
62896170
6290 if (ty.abiSize(zcu) > 16) {6171 if (ty.abiSize(zcu) > 16) {
6291 return func.fail("TODO: `@maximum` and `@minimum` for types larger than 16 bytes", .{});6172 return cg.fail("TODO: `@maximum` and `@minimum` for types larger than 16 bytes", .{});
6292 }6173 }
62936174
6294 const lhs = try func.resolveInst(bin_op.lhs);6175 const lhs = try cg.resolveInst(bin_op.lhs);
6295 const rhs = try func.resolveInst(bin_op.rhs);6176 const rhs = try cg.resolveInst(bin_op.rhs);
62966177
6297 if (ty.zigTypeTag(zcu) == .float) {6178 if (ty.zigTypeTag(zcu) == .float) {
6298 var fn_name_buf: [64]u8 = undefined;6179 const intrinsic = switch (op) {
6299 const float_bits = ty.floatBits(func.target.*);6180 inline .fmin, .fmax => |ct_op| switch (ty.floatBits(cg.target.*)) {
6300 const fn_name = std.fmt.bufPrint(&fn_name_buf, "{s}f{s}{s}", .{6181 inline 16, 32, 64, 80, 128 => |bits| @field(
6301 target_util.libcFloatPrefix(float_bits),6182 Mir.Intrinsic,
6302 @tagName(op),6183 libcFloatPrefix(bits) ++ @tagName(ct_op) ++ libcFloatSuffix(bits),
6303 target_util.libcFloatSuffix(float_bits),6184 ),
6304 }) catch unreachable;6185 else => unreachable,
6305 const result = try func.callIntrinsic(fn_name, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs });6186 },
6306 try func.lowerToStack(result);6187 };
6188 const result = try cg.callIntrinsic(intrinsic, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs });
6189 try cg.lowerToStack(result);
6307 } else {6190 } else {
6308 // operands to select from6191 // operands to select from
6309 try func.lowerToStack(lhs);6192 try cg.lowerToStack(lhs);
6310 try func.lowerToStack(rhs);6193 try cg.lowerToStack(rhs);
6311 _ = try func.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt);6194 _ = try cg.cmp(lhs, rhs, ty, cmp_op);
63126195
6313 // based on the result from comparison, return operand 0 or 1.6196 // based on the result from comparison, return operand 0 or 1.
6314 try func.addTag(.select);6197 try cg.addTag(.select);
6315 }6198 }
63166199
6317 return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });6200 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
6318}6201}
63196202
6320fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6203fn airMulAdd(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6321 const pt = func.pt;6204 const zcu = cg.pt.zcu;
6322 const zcu = pt.zcu;6205 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6323 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;6206 const bin_op = cg.air.extraData(Air.Bin, pl_op.payload).data;
6324 const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data;
63256207
6326 const ty = func.typeOfIndex(inst);6208 const ty = cg.typeOfIndex(inst);
6327 if (ty.zigTypeTag(zcu) == .vector) {6209 if (ty.zigTypeTag(zcu) == .vector) {
6328 return func.fail("TODO: `@mulAdd` for vectors", .{});6210 return cg.fail("TODO: `@mulAdd` for vectors", .{});
6329 }6211 }
63306212
6331 const addend = try func.resolveInst(pl_op.operand);6213 const addend = try cg.resolveInst(pl_op.operand);
6332 const lhs = try func.resolveInst(bin_op.lhs);6214 const lhs = try cg.resolveInst(bin_op.lhs);
6333 const rhs = try func.resolveInst(bin_op.rhs);6215 const rhs = try cg.resolveInst(bin_op.rhs);
63346216
6335 const result = if (ty.floatBits(func.target.*) == 16) fl_result: {6217 const result = if (ty.floatBits(cg.target.*) == 16) fl_result: {
6336 const rhs_ext = try func.fpext(rhs, ty, Type.f32);6218 const rhs_ext = try cg.fpext(rhs, ty, Type.f32);
6337 const lhs_ext = try func.fpext(lhs, ty, Type.f32);6219 const lhs_ext = try cg.fpext(lhs, ty, Type.f32);
6338 const addend_ext = try func.fpext(addend, ty, Type.f32);6220 const addend_ext = try cg.fpext(addend, ty, Type.f32);
6339 // call to compiler-rt `fn fmaf(f32, f32, f32) f32`6221 // call to compiler-rt `fn fmaf(f32, f32, f32) f32`
6340 const result = try func.callIntrinsic(6222 const result = try cg.callIntrinsic(
6341 "fmaf",6223 .fmaf,
6342 &.{ .f32_type, .f32_type, .f32_type },6224 &.{ .f32_type, .f32_type, .f32_type },
6343 Type.f32,6225 Type.f32,
6344 &.{ rhs_ext, lhs_ext, addend_ext },6226 &.{ rhs_ext, lhs_ext, addend_ext },
6345 );6227 );
6346 break :fl_result try func.fptrunc(result, Type.f32, ty);6228 break :fl_result try cg.fptrunc(result, Type.f32, ty);
6347 } else result: {6229 } else result: {
6348 const mul_result = try func.binOp(lhs, rhs, ty, .mul);6230 const mul_result = try cg.binOp(lhs, rhs, ty, .mul);
6349 break :result try func.binOp(mul_result, addend, ty, .add);6231 break :result try cg.binOp(mul_result, addend, ty, .add);
6350 };6232 };
63516233
6352 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });6234 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });
6353}6235}
63546236
6355fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6237fn airClz(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6356 const pt = func.pt;6238 const zcu = cg.pt.zcu;
6357 const zcu = pt.zcu;6239 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6358 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
63596240
6360 const ty = func.typeOf(ty_op.operand);6241 const ty = cg.typeOf(ty_op.operand);
6361 if (ty.zigTypeTag(zcu) == .vector) {6242 if (ty.zigTypeTag(zcu) == .vector) {
6362 return func.fail("TODO: `@clz` for vectors", .{});6243 return cg.fail("TODO: `@clz` for vectors", .{});
6363 }6244 }
63646245
6365 const operand = try func.resolveInst(ty_op.operand);6246 const operand = try cg.resolveInst(ty_op.operand);
6366 const int_info = ty.intInfo(zcu);6247 const int_info = ty.intInfo(zcu);
6367 const wasm_bits = toWasmBits(int_info.bits) orelse {6248 const wasm_bits = toWasmBits(int_info.bits) orelse {
6368 return func.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits});6249 return cg.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits});
6369 };6250 };
63706251
6371 switch (wasm_bits) {6252 switch (wasm_bits) {
6372 32 => {6253 32 => {
6373 try func.emitWValue(operand);6254 try cg.emitWValue(operand);
6374 try func.addTag(.i32_clz);6255 try cg.addTag(.i32_clz);
6375 },6256 },
6376 64 => {6257 64 => {
6377 try func.emitWValue(operand);6258 try cg.emitWValue(operand);
6378 try func.addTag(.i64_clz);6259 try cg.addTag(.i64_clz);
6379 try func.addTag(.i32_wrap_i64);6260 try cg.addTag(.i32_wrap_i64);
6380 },6261 },
6381 128 => {6262 128 => {
6382 var msb = try (try func.load(operand, Type.u64, 8)).toLocal(func, Type.u64);6263 var msb = try (try cg.load(operand, Type.u64, 8)).toLocal(cg, Type.u64);
6383 defer msb.free(func);6264 defer msb.free(cg);
63846265
6385 try func.emitWValue(msb);6266 try cg.emitWValue(msb);
6386 try func.addTag(.i64_clz);6267 try cg.addTag(.i64_clz);
6387 _ = try func.load(operand, Type.u64, 0);6268 _ = try cg.load(operand, Type.u64, 0);
6388 try func.addTag(.i64_clz);6269 try cg.addTag(.i64_clz);
6389 try func.emitWValue(.{ .imm64 = 64 });6270 try cg.emitWValue(.{ .imm64 = 64 });
6390 try func.addTag(.i64_add);6271 try cg.addTag(.i64_add);
6391 _ = try func.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq);6272 _ = try cg.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq);
6392 try func.addTag(.select);6273 try cg.addTag(.select);
6393 try func.addTag(.i32_wrap_i64);6274 try cg.addTag(.i32_wrap_i64);
6394 },6275 },
6395 else => unreachable,6276 else => unreachable,
6396 }6277 }
63976278
6398 if (wasm_bits != int_info.bits) {6279 if (wasm_bits != int_info.bits) {
6399 try func.emitWValue(.{ .imm32 = wasm_bits - int_info.bits });6280 try cg.emitWValue(.{ .imm32 = wasm_bits - int_info.bits });
6400 try func.addTag(.i32_sub);6281 try cg.addTag(.i32_sub);
6401 }6282 }
64026283
6403 return func.finishAir(inst, .stack, &.{ty_op.operand});6284 return cg.finishAir(inst, .stack, &.{ty_op.operand});
6404}6285}
64056286
6406fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6287fn airCtz(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6407 const pt = func.pt;6288 const zcu = cg.pt.zcu;
6408 const zcu = pt.zcu;6289 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6409 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
64106290
6411 const ty = func.typeOf(ty_op.operand);6291 const ty = cg.typeOf(ty_op.operand);
64126292
6413 if (ty.zigTypeTag(zcu) == .vector) {6293 if (ty.zigTypeTag(zcu) == .vector) {
6414 return func.fail("TODO: `@ctz` for vectors", .{});6294 return cg.fail("TODO: `@ctz` for vectors", .{});
6415 }6295 }
64166296
6417 const operand = try func.resolveInst(ty_op.operand);6297 const operand = try cg.resolveInst(ty_op.operand);
6418 const int_info = ty.intInfo(zcu);6298 const int_info = ty.intInfo(zcu);
6419 const wasm_bits = toWasmBits(int_info.bits) orelse {6299 const wasm_bits = toWasmBits(int_info.bits) orelse {
6420 return func.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits});6300 return cg.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits});
6421 };6301 };
64226302
6423 switch (wasm_bits) {6303 switch (wasm_bits) {
...@@ -6425,131 +6305,108 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6425,131 +6305,108 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6425 if (wasm_bits != int_info.bits) {6305 if (wasm_bits != int_info.bits) {
6426 const val: u32 = @as(u32, 1) << @as(u5, @intCast(int_info.bits));6306 const val: u32 = @as(u32, 1) << @as(u5, @intCast(int_info.bits));
6427 // leave value on the stack6307 // leave value on the stack
6428 _ = try func.binOp(operand, .{ .imm32 = val }, ty, .@"or");6308 _ = try cg.binOp(operand, .{ .imm32 = val }, ty, .@"or");
6429 } else try func.emitWValue(operand);6309 } else try cg.emitWValue(operand);
6430 try func.addTag(.i32_ctz);6310 try cg.addTag(.i32_ctz);
6431 },6311 },
6432 64 => {6312 64 => {
6433 if (wasm_bits != int_info.bits) {6313 if (wasm_bits != int_info.bits) {
6434 const val: u64 = @as(u64, 1) << @as(u6, @intCast(int_info.bits));6314 const val: u64 = @as(u64, 1) << @as(u6, @intCast(int_info.bits));
6435 // leave value on the stack6315 // leave value on the stack
6436 _ = try func.binOp(operand, .{ .imm64 = val }, ty, .@"or");6316 _ = try cg.binOp(operand, .{ .imm64 = val }, ty, .@"or");
6437 } else try func.emitWValue(operand);6317 } else try cg.emitWValue(operand);
6438 try func.addTag(.i64_ctz);6318 try cg.addTag(.i64_ctz);
6439 try func.addTag(.i32_wrap_i64);6319 try cg.addTag(.i32_wrap_i64);
6440 },6320 },
6441 128 => {6321 128 => {
6442 var lsb = try (try func.load(operand, Type.u64, 0)).toLocal(func, Type.u64);6322 var lsb = try (try cg.load(operand, Type.u64, 0)).toLocal(cg, Type.u64);
6443 defer lsb.free(func);6323 defer lsb.free(cg);
64446324
6445 try func.emitWValue(lsb);6325 try cg.emitWValue(lsb);
6446 try func.addTag(.i64_ctz);6326 try cg.addTag(.i64_ctz);
6447 _ = try func.load(operand, Type.u64, 8);6327 _ = try cg.load(operand, Type.u64, 8);
6448 if (wasm_bits != int_info.bits) {6328 if (wasm_bits != int_info.bits) {
6449 try func.addImm64(@as(u64, 1) << @as(u6, @intCast(int_info.bits - 64)));6329 try cg.addImm64(@as(u64, 1) << @as(u6, @intCast(int_info.bits - 64)));
6450 try func.addTag(.i64_or);6330 try cg.addTag(.i64_or);
6451 }6331 }
6452 try func.addTag(.i64_ctz);6332 try cg.addTag(.i64_ctz);
6453 try func.addImm64(64);6333 try cg.addImm64(64);
6454 if (wasm_bits != int_info.bits) {6334 if (wasm_bits != int_info.bits) {
6455 try func.addTag(.i64_or);6335 try cg.addTag(.i64_or);
6456 } else {6336 } else {
6457 try func.addTag(.i64_add);6337 try cg.addTag(.i64_add);
6458 }6338 }
6459 _ = try func.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq);6339 _ = try cg.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq);
6460 try func.addTag(.select);6340 try cg.addTag(.select);
6461 try func.addTag(.i32_wrap_i64);6341 try cg.addTag(.i32_wrap_i64);
6462 },6342 },
6463 else => unreachable,6343 else => unreachable,
6464 }6344 }
64656345
6466 return func.finishAir(inst, .stack, &.{ty_op.operand});6346 return cg.finishAir(inst, .stack, &.{ty_op.operand});
6467}6347}
64686348
6469fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6349fn airDbgStmt(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6470 if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});6350 const dbg_stmt = cg.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
64716351 try cg.addInst(.{ .tag = .dbg_line, .data = .{
6472 const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;6352 .payload = try cg.addExtra(Mir.DbgLineColumn{
6473 try func.addInst(.{ .tag = .dbg_line, .data = .{
6474 .payload = try func.addExtra(Mir.DbgLineColumn{
6475 .line = dbg_stmt.line,6353 .line = dbg_stmt.line,
6476 .column = dbg_stmt.column,6354 .column = dbg_stmt.column,
6477 }),6355 }),
6478 } });6356 } });
6479 return func.finishAir(inst, .none, &.{});6357 return cg.finishAir(inst, .none, &.{});
6480}6358}
64816359
6482fn airDbgInlineBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6360fn airDbgInlineBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6483 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6361 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6484 const extra = func.air.extraData(Air.DbgInlineBlock, ty_pl.payload);6362 const extra = cg.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
6485 // TODO6363 // TODO
6486 try func.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]));6364 try cg.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len]));
6487}6365}
64886366
6489fn airDbgVar(6367fn airDbgVar(
6490 func: *CodeGen,6368 cg: *CodeGen,
6491 inst: Air.Inst.Index,6369 inst: Air.Inst.Index,
6492 local_tag: link.File.Dwarf.WipNav.LocalTag,6370 local_tag: link.File.Dwarf.WipNav.LocalTag,
6493 is_ptr: bool,6371 is_ptr: bool,
6494) InnerError!void {6372) InnerError!void {
6495 _ = is_ptr;6373 _ = is_ptr;
6496 if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});6374 _ = local_tag;
64976375 return cg.finishAir(inst, .none, &.{});
6498 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6499 const ty = func.typeOf(pl_op.operand);
6500 const operand = try func.resolveInst(pl_op.operand);
6501
6502 log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), operand });
6503
6504 const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
6505 log.debug(" var name = ({s})", .{name.toSlice(func.air)});
6506
6507 const loc: link.File.Dwarf.Loc = switch (operand) {
6508 .local => |local| .{ .wasm_ext = .{ .local = local.value } },
6509 else => blk: {
6510 log.debug("TODO generate debug info for {}", .{operand});
6511 break :blk .empty;
6512 },
6513 };
6514 try func.debug_output.dwarf.genLocalDebugInfo(local_tag, name.toSlice(func.air), ty, loc);
6515
6516 return func.finishAir(inst, .none, &.{});
6517}6376}
65186377
6519fn airTry(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6378fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6520 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;6379 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6521 const err_union = try func.resolveInst(pl_op.operand);6380 const err_union = try cg.resolveInst(pl_op.operand);
6522 const extra = func.air.extraData(Air.Try, pl_op.payload);6381 const extra = cg.air.extraData(Air.Try, pl_op.payload);
6523 const body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]);6382 const body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len]);
6524 const err_union_ty = func.typeOf(pl_op.operand);6383 const err_union_ty = cg.typeOf(pl_op.operand);
6525 const result = try lowerTry(func, inst, err_union, body, err_union_ty, false);6384 const result = try lowerTry(cg, inst, err_union, body, err_union_ty, false);
6526 return func.finishAir(inst, result, &.{pl_op.operand});6385 return cg.finishAir(inst, result, &.{pl_op.operand});
6527}6386}
65286387
6529fn airTryPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6388fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6530 const pt = func.pt;6389 const zcu = cg.pt.zcu;
6531 const zcu = pt.zcu;6390 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6532 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6391 const extra = cg.air.extraData(Air.TryPtr, ty_pl.payload);
6533 const extra = func.air.extraData(Air.TryPtr, ty_pl.payload);6392 const err_union_ptr = try cg.resolveInst(extra.data.ptr);
6534 const err_union_ptr = try func.resolveInst(extra.data.ptr);6393 const body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len]);
6535 const body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]);6394 const err_union_ty = cg.typeOf(extra.data.ptr).childType(zcu);
6536 const err_union_ty = func.typeOf(extra.data.ptr).childType(zcu);6395 const result = try lowerTry(cg, inst, err_union_ptr, body, err_union_ty, true);
6537 const result = try lowerTry(func, inst, err_union_ptr, body, err_union_ty, true);6396 return cg.finishAir(inst, result, &.{extra.data.ptr});
6538 return func.finishAir(inst, result, &.{extra.data.ptr});
6539}6397}
65406398
6541fn lowerTry(6399fn lowerTry(
6542 func: *CodeGen,6400 cg: *CodeGen,
6543 inst: Air.Inst.Index,6401 inst: Air.Inst.Index,
6544 err_union: WValue,6402 err_union: WValue,
6545 body: []const Air.Inst.Index,6403 body: []const Air.Inst.Index,
6546 err_union_ty: Type,6404 err_union_ty: Type,
6547 operand_is_ptr: bool,6405 operand_is_ptr: bool,
6548) InnerError!WValue {6406) InnerError!WValue {
6549 const pt = func.pt;6407 const zcu = cg.pt.zcu;
6550 const zcu = pt.zcu;
6551 if (operand_is_ptr) {6408 if (operand_is_ptr) {
6552 return func.fail("TODO: lowerTry for pointers", .{});6409 return cg.fail("TODO: lowerTry for pointers", .{});
6553 }6410 }
65546411
6555 const pl_ty = err_union_ty.errorUnionPayload(zcu);6412 const pl_ty = err_union_ty.errorUnionPayload(zcu);
...@@ -6557,29 +6414,29 @@ fn lowerTry(...@@ -6557,29 +6414,29 @@ fn lowerTry(
65576414
6558 if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {6415 if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
6559 // Block we can jump out of when error is not set6416 // Block we can jump out of when error is not set
6560 try func.startBlock(.block, wasm.block_empty);6417 try cg.startBlock(.block, .empty);
65616418
6562 // check if the error tag is set for the error union.6419 // check if the error tag is set for the error union.
6563 try func.emitWValue(err_union);6420 try cg.emitWValue(err_union);
6564 if (pl_has_bits) {6421 if (pl_has_bits) {
6565 const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));6422 const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
6566 try func.addMemArg(.i32_load16_u, .{6423 try cg.addMemArg(.i32_load16_u, .{
6567 .offset = err_union.offset() + err_offset,6424 .offset = err_union.offset() + err_offset,
6568 .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),6425 .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),
6569 });6426 });
6570 }6427 }
6571 try func.addTag(.i32_eqz);6428 try cg.addTag(.i32_eqz);
6572 try func.addLabel(.br_if, 0); // jump out of block when error is '0'6429 try cg.addLabel(.br_if, 0); // jump out of block when error is '0'
65736430
6574 const liveness = func.liveness.getCondBr(inst);6431 const liveness = cg.liveness.getCondBr(inst);
6575 try func.branches.append(func.gpa, .{});6432 try cg.branches.append(cg.gpa, .{});
6576 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.else_deaths.len + liveness.then_deaths.len);6433 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, liveness.else_deaths.len + liveness.then_deaths.len);
6577 defer {6434 defer {
6578 var branch = func.branches.pop();6435 var branch = cg.branches.pop();
6579 branch.deinit(func.gpa);6436 branch.deinit(cg.gpa);
6580 }6437 }
6581 try func.genBody(body);6438 try cg.genBody(body);
6582 try func.endBlock();6439 try cg.endBlock();
6583 }6440 }
65846441
6585 // if we reach here it means error was not set, and we want the payload6442 // if we reach here it means error was not set, and we want the payload
...@@ -6588,39 +6445,38 @@ fn lowerTry(...@@ -6588,39 +6445,38 @@ fn lowerTry(
6588 }6445 }
65896446
6590 const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));6447 const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));
6591 if (isByRef(pl_ty, pt, func.target.*)) {6448 if (isByRef(pl_ty, zcu, cg.target)) {
6592 return buildPointerOffset(func, err_union, pl_offset, .new);6449 return buildPointerOffset(cg, err_union, pl_offset, .new);
6593 }6450 }
6594 const payload = try func.load(err_union, pl_ty, pl_offset);6451 const payload = try cg.load(err_union, pl_ty, pl_offset);
6595 return payload.toLocal(func, pl_ty);6452 return payload.toLocal(cg, pl_ty);
6596}6453}
65976454
6598fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6455fn airByteSwap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6599 const pt = func.pt;6456 const zcu = cg.pt.zcu;
6600 const zcu = pt.zcu;6457 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6601 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
66026458
6603 const ty = func.typeOfIndex(inst);6459 const ty = cg.typeOfIndex(inst);
6604 const operand = try func.resolveInst(ty_op.operand);6460 const operand = try cg.resolveInst(ty_op.operand);
66056461
6606 if (ty.zigTypeTag(zcu) == .vector) {6462 if (ty.zigTypeTag(zcu) == .vector) {
6607 return func.fail("TODO: @byteSwap for vectors", .{});6463 return cg.fail("TODO: @byteSwap for vectors", .{});
6608 }6464 }
6609 const int_info = ty.intInfo(zcu);6465 const int_info = ty.intInfo(zcu);
6610 const wasm_bits = toWasmBits(int_info.bits) orelse {6466 const wasm_bits = toWasmBits(int_info.bits) orelse {
6611 return func.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits});6467 return cg.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits});
6612 };6468 };
66136469
6614 // bytes are no-op6470 // bytes are no-op
6615 if (int_info.bits == 8) {6471 if (int_info.bits == 8) {
6616 return func.finishAir(inst, func.reuseOperand(ty_op.operand, operand), &.{ty_op.operand});6472 return cg.finishAir(inst, cg.reuseOperand(ty_op.operand, operand), &.{ty_op.operand});
6617 }6473 }
66186474
6619 const result = result: {6475 const result = result: {
6620 switch (wasm_bits) {6476 switch (wasm_bits) {
6621 32 => {6477 32 => {
6622 const intrin_ret = try func.callIntrinsic(6478 const intrin_ret = try cg.callIntrinsic(
6623 "__bswapsi2",6479 .__bswapsi2,
6624 &.{.u32_type},6480 &.{.u32_type},
6625 Type.u32,6481 Type.u32,
6626 &.{operand},6482 &.{operand},
...@@ -6628,11 +6484,11 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6628,11 +6484,11 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6628 break :result if (int_info.bits == 32)6484 break :result if (int_info.bits == 32)
6629 intrin_ret6485 intrin_ret
6630 else6486 else
6631 try func.binOp(intrin_ret, .{ .imm32 = 32 - int_info.bits }, ty, .shr);6487 try cg.binOp(intrin_ret, .{ .imm32 = 32 - int_info.bits }, ty, .shr);
6632 },6488 },
6633 64 => {6489 64 => {
6634 const intrin_ret = try func.callIntrinsic(6490 const intrin_ret = try cg.callIntrinsic(
6635 "__bswapdi2",6491 .__bswapdi2,
6636 &.{.u64_type},6492 &.{.u64_type},
6637 Type.u64,6493 Type.u64,
6638 &.{operand},6494 &.{operand},
...@@ -6640,61 +6496,60 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6640,61 +6496,60 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6640 break :result if (int_info.bits == 64)6496 break :result if (int_info.bits == 64)
6641 intrin_ret6497 intrin_ret
6642 else6498 else
6643 try func.binOp(intrin_ret, .{ .imm64 = 64 - int_info.bits }, ty, .shr);6499 try cg.binOp(intrin_ret, .{ .imm64 = 64 - int_info.bits }, ty, .shr);
6644 },6500 },
6645 else => return func.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),6501 else => return cg.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),
6646 }6502 }
6647 };6503 };
6648 return func.finishAir(inst, result, &.{ty_op.operand});6504 return cg.finishAir(inst, result, &.{ty_op.operand});
6649}6505}
66506506
6651fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6507fn airDiv(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6652 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6508 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
66536509
6654 const ty = func.typeOfIndex(inst);6510 const ty = cg.typeOfIndex(inst);
6655 const lhs = try func.resolveInst(bin_op.lhs);6511 const lhs = try cg.resolveInst(bin_op.lhs);
6656 const rhs = try func.resolveInst(bin_op.rhs);6512 const rhs = try cg.resolveInst(bin_op.rhs);
66576513
6658 const result = try func.binOp(lhs, rhs, ty, .div);6514 const result = try cg.binOp(lhs, rhs, ty, .div);
6659 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });6515 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
6660}6516}
66616517
6662fn airDivTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6518fn airDivTrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6663 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6519 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
66646520
6665 const ty = func.typeOfIndex(inst);6521 const ty = cg.typeOfIndex(inst);
6666 const lhs = try func.resolveInst(bin_op.lhs);6522 const lhs = try cg.resolveInst(bin_op.lhs);
6667 const rhs = try func.resolveInst(bin_op.rhs);6523 const rhs = try cg.resolveInst(bin_op.rhs);
66686524
6669 const div_result = try func.binOp(lhs, rhs, ty, .div);6525 const div_result = try cg.binOp(lhs, rhs, ty, .div);
66706526
6671 if (ty.isAnyFloat()) {6527 if (ty.isAnyFloat()) {
6672 const trunc_result = try func.floatOp(.trunc, ty, &.{div_result});6528 const trunc_result = try cg.floatOp(.trunc, ty, &.{div_result});
6673 return func.finishAir(inst, trunc_result, &.{ bin_op.lhs, bin_op.rhs });6529 return cg.finishAir(inst, trunc_result, &.{ bin_op.lhs, bin_op.rhs });
6674 }6530 }
66756531
6676 return func.finishAir(inst, div_result, &.{ bin_op.lhs, bin_op.rhs });6532 return cg.finishAir(inst, div_result, &.{ bin_op.lhs, bin_op.rhs });
6677}6533}
66786534
6679fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6535fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6680 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6536 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
66816537
6682 const pt = func.pt;6538 const zcu = cg.pt.zcu;
6683 const zcu = pt.zcu;6539 const ty = cg.typeOfIndex(inst);
6684 const ty = func.typeOfIndex(inst);6540 const lhs = try cg.resolveInst(bin_op.lhs);
6685 const lhs = try func.resolveInst(bin_op.lhs);6541 const rhs = try cg.resolveInst(bin_op.rhs);
6686 const rhs = try func.resolveInst(bin_op.rhs);
66876542
6688 if (ty.isUnsignedInt(zcu)) {6543 if (ty.isUnsignedInt(zcu)) {
6689 _ = try func.binOp(lhs, rhs, ty, .div);6544 _ = try cg.binOp(lhs, rhs, ty, .div);
6690 } else if (ty.isSignedInt(zcu)) {6545 } else if (ty.isSignedInt(zcu)) {
6691 const int_bits = ty.intInfo(zcu).bits;6546 const int_bits = ty.intInfo(zcu).bits;
6692 const wasm_bits = toWasmBits(int_bits) orelse {6547 const wasm_bits = toWasmBits(int_bits) orelse {
6693 return func.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});6548 return cg.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});
6694 };6549 };
66956550
6696 if (wasm_bits > 64) {6551 if (wasm_bits > 64) {
6697 return func.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});6552 return cg.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});
6698 }6553 }
66996554
6700 const zero: WValue = switch (wasm_bits) {6555 const zero: WValue = switch (wasm_bits) {
...@@ -6704,108 +6559,108 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6704,108 +6559,108 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6704 };6559 };
67056560
6706 // tee leaves the value on the stack and stores it in a local.6561 // tee leaves the value on the stack and stores it in a local.
6707 const quotient = try func.allocLocal(ty);6562 const quotient = try cg.allocLocal(ty);
6708 _ = try func.binOp(lhs, rhs, ty, .div);6563 _ = try cg.binOp(lhs, rhs, ty, .div);
6709 try func.addLabel(.local_tee, quotient.local.value);6564 try cg.addLocal(.local_tee, quotient.local.value);
67106565
6711 // select takes a 32 bit value as the condition, so in the 64 bit case we use eqz to narrow6566 // select takes a 32 bit value as the condition, so in the 64 bit case we use eqz to narrow
6712 // the 64 bit value we want to use as the condition to 32 bits.6567 // the 64 bit value we want to use as the condition to 32 bits.
6713 // This also inverts the condition (non 0 => 0, 0 => 1), so we put the adjusted and6568 // This also inverts the condition (non 0 => 0, 0 => 1), so we put the adjusted and
6714 // non-adjusted quotients on the stack in the opposite order for 32 vs 64 bits.6569 // non-adjusted quotients on the stack in the opposite order for 32 vs 64 bits.
6715 if (wasm_bits == 64) {6570 if (wasm_bits == 64) {
6716 try func.emitWValue(quotient);6571 try cg.emitWValue(quotient);
6717 }6572 }
67186573
6719 // 0 if the signs of rhs_wasm and lhs_wasm are the same, 1 otherwise.6574 // 0 if the signs of rhs_wasm and lhs_wasm are the same, 1 otherwise.
6720 _ = try func.binOp(lhs, rhs, ty, .xor);6575 _ = try cg.binOp(lhs, rhs, ty, .xor);
6721 _ = try func.cmp(.stack, zero, ty, .lt);6576 _ = try cg.cmp(.stack, zero, ty, .lt);
67226577
6723 switch (wasm_bits) {6578 switch (wasm_bits) {
6724 32 => {6579 32 => {
6725 try func.addTag(.i32_sub);6580 try cg.addTag(.i32_sub);
6726 try func.emitWValue(quotient);6581 try cg.emitWValue(quotient);
6727 },6582 },
6728 64 => {6583 64 => {
6729 try func.addTag(.i64_extend_i32_u);6584 try cg.addTag(.i64_extend_i32_u);
6730 try func.addTag(.i64_sub);6585 try cg.addTag(.i64_sub);
6731 },6586 },
6732 else => unreachable,6587 else => unreachable,
6733 }6588 }
67346589
6735 _ = try func.binOp(lhs, rhs, ty, .rem);6590 _ = try cg.binOp(lhs, rhs, ty, .rem);
67366591
6737 if (wasm_bits == 64) {6592 if (wasm_bits == 64) {
6738 try func.addTag(.i64_eqz);6593 try cg.addTag(.i64_eqz);
6739 }6594 }
67406595
6741 try func.addTag(.select);6596 try cg.addTag(.select);
67426597
6743 // We need to zero the high bits because N bit comparisons consider all 32 or 64 bits, and6598 // We need to zero the high bits because N bit comparisons consider all 32 or 64 bits, and
6744 // expect all but the lowest N bits to be 0.6599 // expect all but the lowest N bits to be 0.
6745 // TODO: Should we be zeroing the high bits here or should we be ignoring the high bits6600 // TODO: Should we be zeroing the high bits here or should we be ignoring the high bits
6746 // when performing comparisons?6601 // when performing comparisons?
6747 if (int_bits != wasm_bits) {6602 if (int_bits != wasm_bits) {
6748 _ = try func.wrapOperand(.stack, ty);6603 _ = try cg.wrapOperand(.stack, ty);
6749 }6604 }
6750 } else {6605 } else {
6751 const float_bits = ty.floatBits(func.target.*);6606 const float_bits = ty.floatBits(cg.target.*);
6752 if (float_bits > 64) {6607 if (float_bits > 64) {
6753 return func.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{float_bits});6608 return cg.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{float_bits});
6754 }6609 }
6755 const is_f16 = float_bits == 16;6610 const is_f16 = float_bits == 16;
67566611
6757 const lhs_wasm = if (is_f16) try func.fpext(lhs, Type.f16, Type.f32) else lhs;6612 const lhs_wasm = if (is_f16) try cg.fpext(lhs, Type.f16, Type.f32) else lhs;
6758 const rhs_wasm = if (is_f16) try func.fpext(rhs, Type.f16, Type.f32) else rhs;6613 const rhs_wasm = if (is_f16) try cg.fpext(rhs, Type.f16, Type.f32) else rhs;
67596614
6760 try func.emitWValue(lhs_wasm);6615 try cg.emitWValue(lhs_wasm);
6761 try func.emitWValue(rhs_wasm);6616 try cg.emitWValue(rhs_wasm);
67626617
6763 switch (float_bits) {6618 switch (float_bits) {
6764 16, 32 => {6619 16, 32 => {
6765 try func.addTag(.f32_div);6620 try cg.addTag(.f32_div);
6766 try func.addTag(.f32_floor);6621 try cg.addTag(.f32_floor);
6767 },6622 },
6768 64 => {6623 64 => {
6769 try func.addTag(.f64_div);6624 try cg.addTag(.f64_div);
6770 try func.addTag(.f64_floor);6625 try cg.addTag(.f64_floor);
6771 },6626 },
6772 else => unreachable,6627 else => unreachable,
6773 }6628 }
67746629
6775 if (is_f16) {6630 if (is_f16) {
6776 _ = try func.fptrunc(.stack, Type.f32, Type.f16);6631 _ = try cg.fptrunc(.stack, Type.f32, Type.f16);
6777 }6632 }
6778 }6633 }
67796634
6780 return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });6635 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
6781}6636}
67826637
6783fn airRem(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6638fn airRem(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6784 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6639 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
67856640
6786 const ty = func.typeOfIndex(inst);6641 const ty = cg.typeOfIndex(inst);
6787 const lhs = try func.resolveInst(bin_op.lhs);6642 const lhs = try cg.resolveInst(bin_op.lhs);
6788 const rhs = try func.resolveInst(bin_op.rhs);6643 const rhs = try cg.resolveInst(bin_op.rhs);
67896644
6790 const result = try func.binOp(lhs, rhs, ty, .rem);6645 const result = try cg.binOp(lhs, rhs, ty, .rem);
67916646
6792 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });6647 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
6793}6648}
67946649
6795/// Remainder after floor division, defined by:6650/// Remainder after floor division, defined by:
6796/// @divFloor(a, b) * b + @mod(a, b) = a6651/// @divFloor(a, b) * b + @mod(a, b) = a
6797fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6652fn airMod(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6798 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6653 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
67996654
6800 const pt = func.pt;6655 const pt = cg.pt;
6801 const zcu = pt.zcu;6656 const zcu = pt.zcu;
6802 const ty = func.typeOfIndex(inst);6657 const ty = cg.typeOfIndex(inst);
6803 const lhs = try func.resolveInst(bin_op.lhs);6658 const lhs = try cg.resolveInst(bin_op.lhs);
6804 const rhs = try func.resolveInst(bin_op.rhs);6659 const rhs = try cg.resolveInst(bin_op.rhs);
68056660
6806 const result = result: {6661 const result = result: {
6807 if (ty.isUnsignedInt(zcu)) {6662 if (ty.isUnsignedInt(zcu)) {
6808 break :result try func.binOp(lhs, rhs, ty, .rem);6663 break :result try cg.binOp(lhs, rhs, ty, .rem);
6809 }6664 }
6810 if (ty.isSignedInt(zcu)) {6665 if (ty.isSignedInt(zcu)) {
6811 // The wasm rem instruction gives the remainder after truncating division (rounding towards6666 // The wasm rem instruction gives the remainder after truncating division (rounding towards
...@@ -6814,153 +6669,152 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6814,153 +6669,152 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6814 // @mod(a, b) = @rem(@rem(a, b) + b, b)6669 // @mod(a, b) = @rem(@rem(a, b) + b, b)
6815 const int_bits = ty.intInfo(zcu).bits;6670 const int_bits = ty.intInfo(zcu).bits;
6816 const wasm_bits = toWasmBits(int_bits) orelse {6671 const wasm_bits = toWasmBits(int_bits) orelse {
6817 return func.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});6672 return cg.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});
6818 };6673 };
68196674
6820 if (wasm_bits > 64) {6675 if (wasm_bits > 64) {
6821 return func.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});6676 return cg.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});
6822 }6677 }
68236678
6824 _ = try func.binOp(lhs, rhs, ty, .rem);6679 _ = try cg.binOp(lhs, rhs, ty, .rem);
6825 _ = try func.binOp(.stack, rhs, ty, .add);6680 _ = try cg.binOp(.stack, rhs, ty, .add);
6826 break :result try func.binOp(.stack, rhs, ty, .rem);6681 break :result try cg.binOp(.stack, rhs, ty, .rem);
6827 }6682 }
6828 if (ty.isAnyFloat()) {6683 if (ty.isAnyFloat()) {
6829 const rem = try func.binOp(lhs, rhs, ty, .rem);6684 const rem = try cg.binOp(lhs, rhs, ty, .rem);
6830 const add = try func.binOp(rem, rhs, ty, .add);6685 const add = try cg.binOp(rem, rhs, ty, .add);
6831 break :result try func.binOp(add, rhs, ty, .rem);6686 break :result try cg.binOp(add, rhs, ty, .rem);
6832 }6687 }
6833 return func.fail("TODO: @mod for {}", .{ty.fmt(pt)});6688 return cg.fail("TODO: @mod for {}", .{ty.fmt(pt)});
6834 };6689 };
68356690
6836 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });6691 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
6837}6692}
68386693
6839fn airSatMul(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6694fn airSatMul(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6840 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6695 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
68416696
6842 const pt = func.pt;6697 const pt = cg.pt;
6843 const zcu = pt.zcu;6698 const zcu = pt.zcu;
6844 const ty = func.typeOfIndex(inst);6699 const ty = cg.typeOfIndex(inst);
6845 const int_info = ty.intInfo(zcu);6700 const int_info = ty.intInfo(zcu);
6846 const is_signed = int_info.signedness == .signed;6701 const is_signed = int_info.signedness == .signed;
68476702
6848 const lhs = try func.resolveInst(bin_op.lhs);6703 const lhs = try cg.resolveInst(bin_op.lhs);
6849 const rhs = try func.resolveInst(bin_op.rhs);6704 const rhs = try cg.resolveInst(bin_op.rhs);
6850 const wasm_bits = toWasmBits(int_info.bits) orelse {6705 const wasm_bits = toWasmBits(int_info.bits) orelse {
6851 return func.fail("TODO: mul_sat for {}", .{ty.fmt(pt)});6706 return cg.fail("TODO: mul_sat for {}", .{ty.fmt(pt)});
6852 };6707 };
68536708
6854 switch (wasm_bits) {6709 switch (wasm_bits) {
6855 32 => {6710 32 => {
6856 const upcast_ty: Type = if (is_signed) Type.i64 else Type.u64;6711 const upcast_ty: Type = if (is_signed) Type.i64 else Type.u64;
6857 const lhs_up = try func.intcast(lhs, ty, upcast_ty);6712 const lhs_up = try cg.intcast(lhs, ty, upcast_ty);
6858 const rhs_up = try func.intcast(rhs, ty, upcast_ty);6713 const rhs_up = try cg.intcast(rhs, ty, upcast_ty);
6859 var mul_res = try (try func.binOp(lhs_up, rhs_up, upcast_ty, .mul)).toLocal(func, upcast_ty);6714 var mul_res = try (try cg.binOp(lhs_up, rhs_up, upcast_ty, .mul)).toLocal(cg, upcast_ty);
6860 defer mul_res.free(func);6715 defer mul_res.free(cg);
6861 if (is_signed) {6716 if (is_signed) {
6862 const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - (int_info.bits - 1)) };6717 const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - (int_info.bits - 1)) };
6863 try func.emitWValue(mul_res);6718 try cg.emitWValue(mul_res);
6864 try func.emitWValue(imm_max);6719 try cg.emitWValue(imm_max);
6865 _ = try func.cmp(mul_res, imm_max, upcast_ty, .lt);6720 _ = try cg.cmp(mul_res, imm_max, upcast_ty, .lt);
6866 try func.addTag(.select);6721 try cg.addTag(.select);
68676722
6868 var tmp = try func.allocLocal(upcast_ty);6723 var tmp = try cg.allocLocal(upcast_ty);
6869 defer tmp.free(func);6724 defer tmp.free(cg);
6870 try func.addLabel(.local_set, tmp.local.value);6725 try cg.addLocal(.local_set, tmp.local.value);
68716726
6872 const imm_min: WValue = .{ .imm64 = ~@as(u64, 0) << @intCast(int_info.bits - 1) };6727 const imm_min: WValue = .{ .imm64 = ~@as(u64, 0) << @intCast(int_info.bits - 1) };
6873 try func.emitWValue(tmp);6728 try cg.emitWValue(tmp);
6874 try func.emitWValue(imm_min);6729 try cg.emitWValue(imm_min);
6875 _ = try func.cmp(tmp, imm_min, upcast_ty, .gt);6730 _ = try cg.cmp(tmp, imm_min, upcast_ty, .gt);
6876 try func.addTag(.select);6731 try cg.addTag(.select);
6877 } else {6732 } else {
6878 const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_info.bits) };6733 const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_info.bits) };
6879 try func.emitWValue(mul_res);6734 try cg.emitWValue(mul_res);
6880 try func.emitWValue(imm_max);6735 try cg.emitWValue(imm_max);
6881 _ = try func.cmp(mul_res, imm_max, upcast_ty, .lt);6736 _ = try cg.cmp(mul_res, imm_max, upcast_ty, .lt);
6882 try func.addTag(.select);6737 try cg.addTag(.select);
6883 }6738 }
6884 try func.addTag(.i32_wrap_i64);6739 try cg.addTag(.i32_wrap_i64);
6885 },6740 },
6886 64 => {6741 64 => {
6887 if (!(int_info.bits == 64 and int_info.signedness == .signed)) {6742 if (!(int_info.bits == 64 and int_info.signedness == .signed)) {
6888 return func.fail("TODO: mul_sat for {}", .{ty.fmt(pt)});6743 return cg.fail("TODO: mul_sat for {}", .{ty.fmt(pt)});
6889 }6744 }
6890 const overflow_ret = try func.allocStack(Type.i32);6745 const overflow_ret = try cg.allocStack(Type.i32);
6891 _ = try func.callIntrinsic(6746 _ = try cg.callIntrinsic(
6892 "__mulodi4",6747 .__mulodi4,
6893 &[_]InternPool.Index{ .i64_type, .i64_type, .usize_type },6748 &[_]InternPool.Index{ .i64_type, .i64_type, .usize_type },
6894 Type.i64,6749 Type.i64,
6895 &.{ lhs, rhs, overflow_ret },6750 &.{ lhs, rhs, overflow_ret },
6896 );6751 );
6897 const xor = try func.binOp(lhs, rhs, Type.i64, .xor);6752 const xor = try cg.binOp(lhs, rhs, Type.i64, .xor);
6898 const sign_v = try func.binOp(xor, .{ .imm64 = 63 }, Type.i64, .shr);6753 const sign_v = try cg.binOp(xor, .{ .imm64 = 63 }, Type.i64, .shr);
6899 _ = try func.binOp(sign_v, .{ .imm64 = ~@as(u63, 0) }, Type.i64, .xor);6754 _ = try cg.binOp(sign_v, .{ .imm64 = ~@as(u63, 0) }, Type.i64, .xor);
6900 _ = try func.load(overflow_ret, Type.i32, 0);6755 _ = try cg.load(overflow_ret, Type.i32, 0);
6901 try func.addTag(.i32_eqz);6756 try cg.addTag(.i32_eqz);
6902 try func.addTag(.select);6757 try cg.addTag(.select);
6903 },6758 },
6904 128 => {6759 128 => {
6905 if (!(int_info.bits == 128 and int_info.signedness == .signed)) {6760 if (!(int_info.bits == 128 and int_info.signedness == .signed)) {
6906 return func.fail("TODO: mul_sat for {}", .{ty.fmt(pt)});6761 return cg.fail("TODO: mul_sat for {}", .{ty.fmt(pt)});
6907 }6762 }
6908 const overflow_ret = try func.allocStack(Type.i32);6763 const overflow_ret = try cg.allocStack(Type.i32);
6909 const ret = try func.callIntrinsic(6764 const ret = try cg.callIntrinsic(
6910 "__muloti4",6765 .__muloti4,
6911 &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type },6766 &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type },
6912 Type.i128,6767 Type.i128,
6913 &.{ lhs, rhs, overflow_ret },6768 &.{ lhs, rhs, overflow_ret },
6914 );6769 );
6915 try func.lowerToStack(ret);6770 try cg.lowerToStack(ret);
6916 const xor = try func.binOp(lhs, rhs, Type.i128, .xor);6771 const xor = try cg.binOp(lhs, rhs, Type.i128, .xor);
6917 const sign_v = try func.binOp(xor, .{ .imm32 = 127 }, Type.i128, .shr);6772 const sign_v = try cg.binOp(xor, .{ .imm32 = 127 }, Type.i128, .shr);
69186773
6919 // xor ~@as(u127, 0)6774 // xor ~@as(u127, 0)
6920 try func.emitWValue(sign_v);6775 try cg.emitWValue(sign_v);
6921 const lsb = try func.load(sign_v, Type.u64, 0);6776 const lsb = try cg.load(sign_v, Type.u64, 0);
6922 _ = try func.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);6777 _ = try cg.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
6923 try func.store(.stack, .stack, Type.u64, sign_v.offset());6778 try cg.store(.stack, .stack, Type.u64, sign_v.offset());
6924 try func.emitWValue(sign_v);6779 try cg.emitWValue(sign_v);
6925 const msb = try func.load(sign_v, Type.u64, 8);6780 const msb = try cg.load(sign_v, Type.u64, 8);
6926 _ = try func.binOp(msb, .{ .imm64 = ~@as(u63, 0) }, Type.u64, .xor);6781 _ = try cg.binOp(msb, .{ .imm64 = ~@as(u63, 0) }, Type.u64, .xor);
6927 try func.store(.stack, .stack, Type.u64, sign_v.offset() + 8);6782 try cg.store(.stack, .stack, Type.u64, sign_v.offset() + 8);
69286783
6929 try func.lowerToStack(sign_v);6784 try cg.lowerToStack(sign_v);
6930 _ = try func.load(overflow_ret, Type.i32, 0);6785 _ = try cg.load(overflow_ret, Type.i32, 0);
6931 try func.addTag(.i32_eqz);6786 try cg.addTag(.i32_eqz);
6932 try func.addTag(.select);6787 try cg.addTag(.select);
6933 },6788 },
6934 else => unreachable,6789 else => unreachable,
6935 }6790 }
6936 return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });6791 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
6937}6792}
69386793
6939fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {6794fn airSatBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
6940 assert(op == .add or op == .sub);6795 assert(op == .add or op == .sub);
6941 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6796 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
69426797
6943 const pt = func.pt;6798 const zcu = cg.pt.zcu;
6944 const zcu = pt.zcu;6799 const ty = cg.typeOfIndex(inst);
6945 const ty = func.typeOfIndex(inst);6800 const lhs = try cg.resolveInst(bin_op.lhs);
6946 const lhs = try func.resolveInst(bin_op.lhs);6801 const rhs = try cg.resolveInst(bin_op.rhs);
6947 const rhs = try func.resolveInst(bin_op.rhs);
69486802
6949 const int_info = ty.intInfo(zcu);6803 const int_info = ty.intInfo(zcu);
6950 const is_signed = int_info.signedness == .signed;6804 const is_signed = int_info.signedness == .signed;
69516805
6952 if (int_info.bits > 64) {6806 if (int_info.bits > 64) {
6953 return func.fail("TODO: saturating arithmetic for integers with bitsize '{d}'", .{int_info.bits});6807 return cg.fail("TODO: saturating arithmetic for integers with bitsize '{d}'", .{int_info.bits});
6954 }6808 }
69556809
6956 if (is_signed) {6810 if (is_signed) {
6957 const result = try signedSat(func, lhs, rhs, ty, op);6811 const result = try signedSat(cg, lhs, rhs, ty, op);
6958 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });6812 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
6959 }6813 }
69606814
6961 const wasm_bits = toWasmBits(int_info.bits).?;6815 const wasm_bits = toWasmBits(int_info.bits).?;
6962 var bin_result = try (try func.binOp(lhs, rhs, ty, op)).toLocal(func, ty);6816 var bin_result = try (try cg.binOp(lhs, rhs, ty, op)).toLocal(cg, ty);
6963 defer bin_result.free(func);6817 defer bin_result.free(cg);
6964 if (wasm_bits != int_info.bits and op == .add) {6818 if (wasm_bits != int_info.bits and op == .add) {
6965 const val: u64 = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(int_info.bits))) - 1));6819 const val: u64 = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(int_info.bits))) - 1));
6966 const imm_val: WValue = switch (wasm_bits) {6820 const imm_val: WValue = switch (wasm_bits) {
...@@ -6969,25 +6823,25 @@ fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {...@@ -6969,25 +6823,25 @@ fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
6969 else => unreachable,6823 else => unreachable,
6970 };6824 };
69716825
6972 try func.emitWValue(bin_result);6826 try cg.emitWValue(bin_result);
6973 try func.emitWValue(imm_val);6827 try cg.emitWValue(imm_val);
6974 _ = try func.cmp(bin_result, imm_val, ty, .lt);6828 _ = try cg.cmp(bin_result, imm_val, ty, .lt);
6975 } else {6829 } else {
6976 switch (wasm_bits) {6830 switch (wasm_bits) {
6977 32 => try func.addImm32(if (op == .add) std.math.maxInt(u32) else 0),6831 32 => try cg.addImm32(if (op == .add) std.math.maxInt(u32) else 0),
6978 64 => try func.addImm64(if (op == .add) std.math.maxInt(u64) else 0),6832 64 => try cg.addImm64(if (op == .add) std.math.maxInt(u64) else 0),
6979 else => unreachable,6833 else => unreachable,
6980 }6834 }
6981 try func.emitWValue(bin_result);6835 try cg.emitWValue(bin_result);
6982 _ = try func.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt);6836 _ = try cg.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt);
6983 }6837 }
69846838
6985 try func.addTag(.select);6839 try cg.addTag(.select);
6986 return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });6840 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
6987}6841}
69886842
6989fn signedSat(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {6843fn signedSat(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
6990 const pt = func.pt;6844 const pt = cg.pt;
6991 const zcu = pt.zcu;6845 const zcu = pt.zcu;
6992 const int_info = ty.intInfo(zcu);6846 const int_info = ty.intInfo(zcu);
6993 const wasm_bits = toWasmBits(int_info.bits).?;6847 const wasm_bits = toWasmBits(int_info.bits).?;
...@@ -7007,92 +6861,92 @@ fn signedSat(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr...@@ -7007,92 +6861,92 @@ fn signedSat(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr
7007 else => unreachable,6861 else => unreachable,
7008 };6862 };
70096863
7010 var bin_result = try (try func.binOp(lhs, rhs, ext_ty, op)).toLocal(func, ext_ty);6864 var bin_result = try (try cg.binOp(lhs, rhs, ext_ty, op)).toLocal(cg, ext_ty);
7011 if (!is_wasm_bits) {6865 if (!is_wasm_bits) {
7012 defer bin_result.free(func); // not returned in this branch6866 defer bin_result.free(cg); // not returned in this branch
7013 try func.emitWValue(bin_result);6867 try cg.emitWValue(bin_result);
7014 try func.emitWValue(max_wvalue);6868 try cg.emitWValue(max_wvalue);
7015 _ = try func.cmp(bin_result, max_wvalue, ext_ty, .lt);6869 _ = try cg.cmp(bin_result, max_wvalue, ext_ty, .lt);
7016 try func.addTag(.select);6870 try cg.addTag(.select);
7017 try func.addLabel(.local_set, bin_result.local.value); // re-use local6871 try cg.addLocal(.local_set, bin_result.local.value); // re-use local
70186872
7019 try func.emitWValue(bin_result);6873 try cg.emitWValue(bin_result);
7020 try func.emitWValue(min_wvalue);6874 try cg.emitWValue(min_wvalue);
7021 _ = try func.cmp(bin_result, min_wvalue, ext_ty, .gt);6875 _ = try cg.cmp(bin_result, min_wvalue, ext_ty, .gt);
7022 try func.addTag(.select);6876 try cg.addTag(.select);
7023 try func.addLabel(.local_set, bin_result.local.value); // re-use local6877 try cg.addLocal(.local_set, bin_result.local.value); // re-use local
7024 return (try func.wrapOperand(bin_result, ty)).toLocal(func, ty);6878 return (try cg.wrapOperand(bin_result, ty)).toLocal(cg, ty);
7025 } else {6879 } else {
7026 const zero: WValue = switch (wasm_bits) {6880 const zero: WValue = switch (wasm_bits) {
7027 32 => .{ .imm32 = 0 },6881 32 => .{ .imm32 = 0 },
7028 64 => .{ .imm64 = 0 },6882 64 => .{ .imm64 = 0 },
7029 else => unreachable,6883 else => unreachable,
7030 };6884 };
7031 try func.emitWValue(max_wvalue);6885 try cg.emitWValue(max_wvalue);
7032 try func.emitWValue(min_wvalue);6886 try cg.emitWValue(min_wvalue);
7033 _ = try func.cmp(bin_result, zero, ty, .lt);6887 _ = try cg.cmp(bin_result, zero, ty, .lt);
7034 try func.addTag(.select);6888 try cg.addTag(.select);
7035 try func.emitWValue(bin_result);6889 try cg.emitWValue(bin_result);
7036 // leave on stack6890 // leave on stack
7037 const cmp_zero_result = try func.cmp(rhs, zero, ty, if (op == .add) .lt else .gt);6891 const cmp_zero_result = try cg.cmp(rhs, zero, ty, if (op == .add) .lt else .gt);
7038 const cmp_bin_result = try func.cmp(bin_result, lhs, ty, .lt);6892 const cmp_bin_result = try cg.cmp(bin_result, lhs, ty, .lt);
7039 _ = try func.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor.6893 _ = try cg.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor.
7040 try func.addTag(.select);6894 try cg.addTag(.select);
7041 try func.addLabel(.local_set, bin_result.local.value); // re-use local6895 try cg.addLocal(.local_set, bin_result.local.value); // re-use local
7042 return bin_result;6896 return bin_result;
7043 }6897 }
7044}6898}
70456899
7046fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6900fn airShlSat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7047 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6901 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
70486902
7049 const pt = func.pt;6903 const pt = cg.pt;
7050 const zcu = pt.zcu;6904 const zcu = pt.zcu;
7051 const ty = func.typeOfIndex(inst);6905 const ty = cg.typeOfIndex(inst);
7052 const int_info = ty.intInfo(zcu);6906 const int_info = ty.intInfo(zcu);
7053 const is_signed = int_info.signedness == .signed;6907 const is_signed = int_info.signedness == .signed;
7054 if (int_info.bits > 64) {6908 if (int_info.bits > 64) {
7055 return func.fail("TODO: Saturating shifting left for integers with bitsize '{d}'", .{int_info.bits});6909 return cg.fail("TODO: Saturating shifting left for integers with bitsize '{d}'", .{int_info.bits});
7056 }6910 }
70576911
7058 const lhs = try func.resolveInst(bin_op.lhs);6912 const lhs = try cg.resolveInst(bin_op.lhs);
7059 const rhs = try func.resolveInst(bin_op.rhs);6913 const rhs = try cg.resolveInst(bin_op.rhs);
7060 const wasm_bits = toWasmBits(int_info.bits).?;6914 const wasm_bits = toWasmBits(int_info.bits).?;
7061 const result = try func.allocLocal(ty);6915 const result = try cg.allocLocal(ty);
70626916
7063 if (wasm_bits == int_info.bits) {6917 if (wasm_bits == int_info.bits) {
7064 var shl = try (try func.binOp(lhs, rhs, ty, .shl)).toLocal(func, ty);6918 var shl = try (try cg.binOp(lhs, rhs, ty, .shl)).toLocal(cg, ty);
7065 defer shl.free(func);6919 defer shl.free(cg);
7066 var shr = try (try func.binOp(shl, rhs, ty, .shr)).toLocal(func, ty);6920 var shr = try (try cg.binOp(shl, rhs, ty, .shr)).toLocal(cg, ty);
7067 defer shr.free(func);6921 defer shr.free(cg);
70686922
7069 switch (wasm_bits) {6923 switch (wasm_bits) {
7070 32 => blk: {6924 32 => blk: {
7071 if (!is_signed) {6925 if (!is_signed) {
7072 try func.addImm32(std.math.maxInt(u32));6926 try cg.addImm32(std.math.maxInt(u32));
7073 break :blk;6927 break :blk;
7074 }6928 }
7075 try func.addImm32(@bitCast(@as(i32, std.math.minInt(i32))));6929 try cg.addImm32(@bitCast(@as(i32, std.math.minInt(i32))));
7076 try func.addImm32(@bitCast(@as(i32, std.math.maxInt(i32))));6930 try cg.addImm32(@bitCast(@as(i32, std.math.maxInt(i32))));
7077 _ = try func.cmp(lhs, .{ .imm32 = 0 }, ty, .lt);6931 _ = try cg.cmp(lhs, .{ .imm32 = 0 }, ty, .lt);
7078 try func.addTag(.select);6932 try cg.addTag(.select);
7079 },6933 },
7080 64 => blk: {6934 64 => blk: {
7081 if (!is_signed) {6935 if (!is_signed) {
7082 try func.addImm64(std.math.maxInt(u64));6936 try cg.addImm64(std.math.maxInt(u64));
7083 break :blk;6937 break :blk;
7084 }6938 }
7085 try func.addImm64(@bitCast(@as(i64, std.math.minInt(i64))));6939 try cg.addImm64(@bitCast(@as(i64, std.math.minInt(i64))));
7086 try func.addImm64(@bitCast(@as(i64, std.math.maxInt(i64))));6940 try cg.addImm64(@bitCast(@as(i64, std.math.maxInt(i64))));
7087 _ = try func.cmp(lhs, .{ .imm64 = 0 }, ty, .lt);6941 _ = try cg.cmp(lhs, .{ .imm64 = 0 }, ty, .lt);
7088 try func.addTag(.select);6942 try cg.addTag(.select);
7089 },6943 },
7090 else => unreachable,6944 else => unreachable,
7091 }6945 }
7092 try func.emitWValue(shl);6946 try cg.emitWValue(shl);
7093 _ = try func.cmp(lhs, shr, ty, .neq);6947 _ = try cg.cmp(lhs, shr, ty, .neq);
7094 try func.addTag(.select);6948 try cg.addTag(.select);
7095 try func.addLabel(.local_set, result.local.value);6949 try cg.addLocal(.local_set, result.local.value);
7096 } else {6950 } else {
7097 const shift_size = wasm_bits - int_info.bits;6951 const shift_size = wasm_bits - int_info.bits;
7098 const shift_value: WValue = switch (wasm_bits) {6952 const shift_value: WValue = switch (wasm_bits) {
...@@ -7102,50 +6956,50 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7102,50 +6956,50 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7102 };6956 };
7103 const ext_ty = try pt.intType(int_info.signedness, wasm_bits);6957 const ext_ty = try pt.intType(int_info.signedness, wasm_bits);
71046958
7105 var shl_res = try (try func.binOp(lhs, shift_value, ext_ty, .shl)).toLocal(func, ext_ty);6959 var shl_res = try (try cg.binOp(lhs, shift_value, ext_ty, .shl)).toLocal(cg, ext_ty);
7106 defer shl_res.free(func);6960 defer shl_res.free(cg);
7107 var shl = try (try func.binOp(shl_res, rhs, ext_ty, .shl)).toLocal(func, ext_ty);6961 var shl = try (try cg.binOp(shl_res, rhs, ext_ty, .shl)).toLocal(cg, ext_ty);
7108 defer shl.free(func);6962 defer shl.free(cg);
7109 var shr = try (try func.binOp(shl, rhs, ext_ty, .shr)).toLocal(func, ext_ty);6963 var shr = try (try cg.binOp(shl, rhs, ext_ty, .shr)).toLocal(cg, ext_ty);
7110 defer shr.free(func);6964 defer shr.free(cg);
71116965
7112 switch (wasm_bits) {6966 switch (wasm_bits) {
7113 32 => blk: {6967 32 => blk: {
7114 if (!is_signed) {6968 if (!is_signed) {
7115 try func.addImm32(std.math.maxInt(u32));6969 try cg.addImm32(std.math.maxInt(u32));
7116 break :blk;6970 break :blk;
7117 }6971 }
71186972
7119 try func.addImm32(@bitCast(@as(i32, std.math.minInt(i32))));6973 try cg.addImm32(@bitCast(@as(i32, std.math.minInt(i32))));
7120 try func.addImm32(@bitCast(@as(i32, std.math.maxInt(i32))));6974 try cg.addImm32(@bitCast(@as(i32, std.math.maxInt(i32))));
7121 _ = try func.cmp(shl_res, .{ .imm32 = 0 }, ext_ty, .lt);6975 _ = try cg.cmp(shl_res, .{ .imm32 = 0 }, ext_ty, .lt);
7122 try func.addTag(.select);6976 try cg.addTag(.select);
7123 },6977 },
7124 64 => blk: {6978 64 => blk: {
7125 if (!is_signed) {6979 if (!is_signed) {
7126 try func.addImm64(std.math.maxInt(u64));6980 try cg.addImm64(std.math.maxInt(u64));
7127 break :blk;6981 break :blk;
7128 }6982 }
71296983
7130 try func.addImm64(@bitCast(@as(i64, std.math.minInt(i64))));6984 try cg.addImm64(@bitCast(@as(i64, std.math.minInt(i64))));
7131 try func.addImm64(@bitCast(@as(i64, std.math.maxInt(i64))));6985 try cg.addImm64(@bitCast(@as(i64, std.math.maxInt(i64))));
7132 _ = try func.cmp(shl_res, .{ .imm64 = 0 }, ext_ty, .lt);6986 _ = try cg.cmp(shl_res, .{ .imm64 = 0 }, ext_ty, .lt);
7133 try func.addTag(.select);6987 try cg.addTag(.select);
7134 },6988 },
7135 else => unreachable,6989 else => unreachable,
7136 }6990 }
7137 try func.emitWValue(shl);6991 try cg.emitWValue(shl);
7138 _ = try func.cmp(shl_res, shr, ext_ty, .neq);6992 _ = try cg.cmp(shl_res, shr, ext_ty, .neq);
7139 try func.addTag(.select);6993 try cg.addTag(.select);
7140 try func.addLabel(.local_set, result.local.value);6994 try cg.addLocal(.local_set, result.local.value);
7141 var shift_result = try func.binOp(result, shift_value, ext_ty, .shr);6995 var shift_result = try cg.binOp(result, shift_value, ext_ty, .shr);
7142 if (is_signed) {6996 if (is_signed) {
7143 shift_result = try func.wrapOperand(shift_result, ty);6997 shift_result = try cg.wrapOperand(shift_result, ty);
7144 }6998 }
7145 try func.addLabel(.local_set, result.local.value);6999 try cg.addLocal(.local_set, result.local.value);
7146 }7000 }
71477001
7148 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });7002 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
7149}7003}
71507004
7151/// Calls a compiler-rt intrinsic by creating an undefined symbol,7005/// Calls a compiler-rt intrinsic by creating an undefined symbol,
...@@ -7155,31 +7009,23 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7155,31 +7009,23 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7155/// passed as the first parameter.7009/// passed as the first parameter.
7156/// May leave the return value on the stack.7010/// May leave the return value on the stack.
7157fn callIntrinsic(7011fn callIntrinsic(
7158 func: *CodeGen,7012 cg: *CodeGen,
7159 name: []const u8,7013 intrinsic: Mir.Intrinsic,
7160 param_types: []const InternPool.Index,7014 param_types: []const InternPool.Index,
7161 return_type: Type,7015 return_type: Type,
7162 args: []const WValue,7016 args: []const WValue,
7163) InnerError!WValue {7017) InnerError!WValue {
7164 assert(param_types.len == args.len);7018 assert(param_types.len == args.len);
7165 const symbol_index = func.bin_file.getGlobalSymbol(name, null) catch |err| {7019 const zcu = cg.pt.zcu;
7166 return func.fail("Could not find or create global symbol '{s}'", .{@errorName(err)});
7167 };
71687020
7169 // Always pass over C-ABI7021 // Always pass over C-ABI
7170 const pt = func.pt;
7171 const zcu = pt.zcu;
7172 var func_type = try genFunctype(func.gpa, .{ .wasm_watc = .{} }, param_types, return_type, pt, func.target.*);
7173 defer func_type.deinit(func.gpa);
7174 const func_type_index = try func.bin_file.zig_object.?.putOrGetFuncType(func.gpa, func_type);
7175 try func.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index);
71767022
7177 const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, pt, func.target.*);7023 const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, zcu, cg.target);
7178 // if we want return as first param, we allocate a pointer to stack,7024 // if we want return as first param, we allocate a pointer to stack,
7179 // and emit it as our first argument7025 // and emit it as our first argument
7180 const sret = if (want_sret_param) blk: {7026 const sret = if (want_sret_param) blk: {
7181 const sret_local = try func.allocStack(return_type);7027 const sret_local = try cg.allocStack(return_type);
7182 try func.lowerToStack(sret_local);7028 try cg.lowerToStack(sret_local);
7183 break :blk sret_local;7029 break :blk sret_local;
7184 } else .none;7030 } else .none;
71857031
...@@ -7187,16 +7033,15 @@ fn callIntrinsic(...@@ -7187,16 +7033,15 @@ fn callIntrinsic(
7187 for (args, 0..) |arg, arg_i| {7033 for (args, 0..) |arg, arg_i| {
7188 assert(!(want_sret_param and arg == .stack));7034 assert(!(want_sret_param and arg == .stack));
7189 assert(Type.fromInterned(param_types[arg_i]).hasRuntimeBitsIgnoreComptime(zcu));7035 assert(Type.fromInterned(param_types[arg_i]).hasRuntimeBitsIgnoreComptime(zcu));
7190 try func.lowerArg(.{ .wasm_watc = .{} }, Type.fromInterned(param_types[arg_i]), arg);7036 try cg.lowerArg(.{ .wasm_watc = .{} }, Type.fromInterned(param_types[arg_i]), arg);
7191 }7037 }
71927038
7193 // Actually call our intrinsic7039 try cg.addInst(.{ .tag = .call_intrinsic, .data = .{ .intrinsic = intrinsic } });
7194 try func.addLabel(.call, @intFromEnum(symbol_index));
71957040
7196 if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) {7041 if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) {
7197 return .none;7042 return .none;
7198 } else if (return_type.isNoReturn(zcu)) {7043 } else if (return_type.isNoReturn(zcu)) {
7199 try func.addTag(.@"unreachable");7044 try cg.addTag(.@"unreachable");
7200 return .none;7045 return .none;
7201 } else if (want_sret_param) {7046 } else if (want_sret_param) {
7202 return sret;7047 return sret;
...@@ -7205,194 +7050,30 @@ fn callIntrinsic(...@@ -7205,194 +7050,30 @@ fn callIntrinsic(
7205 }7050 }
7206}7051}
72077052
7208fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {7053fn airTagName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7209 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;7054 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
7210 const operand = try func.resolveInst(un_op);7055 const operand = try cg.resolveInst(un_op);
7211 const enum_ty = func.typeOf(un_op);7056 const enum_ty = cg.typeOf(un_op);
7212
7213 const func_sym_index = try func.getTagNameFunction(enum_ty);
7214
7215 const result_ptr = try func.allocStack(func.typeOfIndex(inst));
7216 try func.lowerToStack(result_ptr);
7217 try func.emitWValue(operand);
7218 try func.addLabel(.call, func_sym_index);
7219
7220 return func.finishAir(inst, result_ptr, &.{un_op});
7221}
7222
7223fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
7224 const pt = func.pt;
7225 const zcu = pt.zcu;
7226 const ip = &zcu.intern_pool;
7227
7228 var arena_allocator = std.heap.ArenaAllocator.init(func.gpa);
7229 defer arena_allocator.deinit();
7230 const arena = arena_allocator.allocator();
7231
7232 const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{}", .{ip.loadEnumType(enum_ty.toIntern()).name.fmt(ip)});
7233
7234 // check if we already generated code for this.
7235 if (func.bin_file.findGlobalSymbol(func_name)) |loc| {
7236 return @intFromEnum(loc.index);
7237 }
7238
7239 const int_tag_ty = enum_ty.intTagType(zcu);
7240
7241 if (int_tag_ty.bitSize(zcu) > 64) {
7242 return func.fail("TODO: Implement @tagName for enums with tag size larger than 64 bits", .{});
7243 }
7244
7245 var relocs = std.ArrayList(link.File.Wasm.Relocation).init(func.gpa);
7246 defer relocs.deinit();
7247
7248 var body_list = std.ArrayList(u8).init(func.gpa);
7249 defer body_list.deinit();
7250 var writer = body_list.writer();
7251
7252 // The locals of the function body (always 0)
7253 try leb.writeUleb128(writer, @as(u32, 0));
7254
7255 // outer block
7256 try writer.writeByte(std.wasm.opcode(.block));
7257 try writer.writeByte(std.wasm.block_empty);
7258
7259 // TODO: Make switch implementation generic so we can use a jump table for this when the tags are not sparse.
7260 // generate an if-else chain for each tag value as well as constant.
7261 const tag_names = enum_ty.enumFields(zcu);
7262 for (0..tag_names.len) |tag_index| {
7263 const tag_name = tag_names.get(ip)[tag_index];
7264 const tag_name_len = tag_name.length(ip);
7265 // for each tag name, create an unnamed const,
7266 // and then get a pointer to its value.
7267 const name_ty = try pt.arrayType(.{
7268 .len = tag_name_len,
7269 .child = .u8_type,
7270 .sentinel = .zero_u8,
7271 });
7272 const name_val = try pt.intern(.{ .aggregate = .{
7273 .ty = name_ty.toIntern(),
7274 .storage = .{ .bytes = tag_name.toString() },
7275 } });
7276 const tag_sym_index = switch (try func.bin_file.lowerUav(pt, name_val, .none, func.src_loc)) {
7277 .mcv => |mcv| mcv.load_symbol,
7278 .fail => |err_msg| {
7279 func.err_msg = err_msg;
7280 return error.CodegenFail;
7281 },
7282 };
7283
7284 // block for this if case
7285 try writer.writeByte(std.wasm.opcode(.block));
7286 try writer.writeByte(std.wasm.block_empty);
7287
7288 // get actual tag value (stored in 2nd parameter);
7289 try writer.writeByte(std.wasm.opcode(.local_get));
7290 try leb.writeUleb128(writer, @as(u32, 1));
7291
7292 const tag_val = try pt.enumValueFieldIndex(enum_ty, @intCast(tag_index));
7293 const tag_value = try func.lowerConstant(tag_val, enum_ty);
7294
7295 switch (tag_value) {
7296 .imm32 => |value| {
7297 try writer.writeByte(std.wasm.opcode(.i32_const));
7298 try leb.writeIleb128(writer, @as(i32, @bitCast(value)));
7299 try writer.writeByte(std.wasm.opcode(.i32_ne));
7300 },
7301 .imm64 => |value| {
7302 try writer.writeByte(std.wasm.opcode(.i64_const));
7303 try leb.writeIleb128(writer, @as(i64, @bitCast(value)));
7304 try writer.writeByte(std.wasm.opcode(.i64_ne));
7305 },
7306 else => unreachable,
7307 }
7308 // if they're not equal, break out of current branch
7309 try writer.writeByte(std.wasm.opcode(.br_if));
7310 try leb.writeUleb128(writer, @as(u32, 0));
7311
7312 // store the address of the tagname in the pointer field of the slice
7313 // get the address twice so we can also store the length.
7314 try writer.writeByte(std.wasm.opcode(.local_get));
7315 try leb.writeUleb128(writer, @as(u32, 0));
7316 try writer.writeByte(std.wasm.opcode(.local_get));
7317 try leb.writeUleb128(writer, @as(u32, 0));
7318
7319 // get address of tagname and emit a relocation to it
7320 if (func.arch() == .wasm32) {
7321 const encoded_alignment = @ctz(@as(u32, 4));
7322 try writer.writeByte(std.wasm.opcode(.i32_const));
7323 try relocs.append(.{
7324 .relocation_type = .R_WASM_MEMORY_ADDR_LEB,
7325 .offset = @as(u32, @intCast(body_list.items.len)),
7326 .index = tag_sym_index,
7327 });
7328 try writer.writeAll(&[_]u8{0} ** 5); // will be relocated
7329
7330 // store pointer
7331 try writer.writeByte(std.wasm.opcode(.i32_store));
7332 try leb.writeUleb128(writer, encoded_alignment);
7333 try leb.writeUleb128(writer, @as(u32, 0));
7334
7335 // store length
7336 try writer.writeByte(std.wasm.opcode(.i32_const));
7337 try leb.writeUleb128(writer, @as(u32, @intCast(tag_name_len)));
7338 try writer.writeByte(std.wasm.opcode(.i32_store));
7339 try leb.writeUleb128(writer, encoded_alignment);
7340 try leb.writeUleb128(writer, @as(u32, 4));
7341 } else {
7342 const encoded_alignment = @ctz(@as(u32, 8));
7343 try writer.writeByte(std.wasm.opcode(.i64_const));
7344 try relocs.append(.{
7345 .relocation_type = .R_WASM_MEMORY_ADDR_LEB64,
7346 .offset = @as(u32, @intCast(body_list.items.len)),
7347 .index = tag_sym_index,
7348 });
7349 try writer.writeAll(&[_]u8{0} ** 10); // will be relocated
7350
7351 // store pointer
7352 try writer.writeByte(std.wasm.opcode(.i64_store));
7353 try leb.writeUleb128(writer, encoded_alignment);
7354 try leb.writeUleb128(writer, @as(u32, 0));
7355
7356 // store length
7357 try writer.writeByte(std.wasm.opcode(.i64_const));
7358 try leb.writeUleb128(writer, @as(u64, @intCast(tag_name_len)));
7359 try writer.writeByte(std.wasm.opcode(.i64_store));
7360 try leb.writeUleb128(writer, encoded_alignment);
7361 try leb.writeUleb128(writer, @as(u32, 8));
7362 }
7363
7364 // break outside blocks
7365 try writer.writeByte(std.wasm.opcode(.br));
7366 try leb.writeUleb128(writer, @as(u32, 1));
7367
7368 // end the block for this case
7369 try writer.writeByte(std.wasm.opcode(.end));
7370 }
73717057
7372 try writer.writeByte(std.wasm.opcode(.@"unreachable")); // tag value does not have a name7058 const result_ptr = try cg.allocStack(cg.typeOfIndex(inst));
7373 // finish outer block7059 try cg.lowerToStack(result_ptr);
7374 try writer.writeByte(std.wasm.opcode(.end));7060 try cg.emitWValue(operand);
7375 // finish function body7061 try cg.addInst(.{ .tag = .call_tag_name, .data = .{ .ip_index = enum_ty.toIntern() } });
7376 try writer.writeByte(std.wasm.opcode(.end));
73777062
7378 const slice_ty = Type.slice_const_u8_sentinel_0;7063 return cg.finishAir(inst, result_ptr, &.{un_op});
7379 const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty.ip_index}, slice_ty, pt, func.target.*);
7380 const sym_index = try func.bin_file.createFunction(func_name, func_type, &body_list, &relocs);
7381 return @intFromEnum(sym_index);
7382}7064}
73837065
7384fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {7066fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7385 const pt = func.pt;7067 const zcu = cg.pt.zcu;
7386 const zcu = pt.zcu;
7387 const ip = &zcu.intern_pool;7068 const ip = &zcu.intern_pool;
7388 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;7069 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
73897070
7390 const operand = try func.resolveInst(ty_op.operand);7071 const operand = try cg.resolveInst(ty_op.operand);
7391 const error_set_ty = ty_op.ty.toType();7072 const error_set_ty = ty_op.ty.toType();
7392 const result = try func.allocLocal(Type.bool);7073 const result = try cg.allocLocal(Type.bool);
73937074
7394 const names = error_set_ty.errorSetNames(zcu);7075 const names = error_set_ty.errorSetNames(zcu);
7395 var values = try std.ArrayList(u32).initCapacity(func.gpa, names.len);7076 var values = try std.ArrayList(u32).initCapacity(cg.gpa, names.len);
7396 defer values.deinit();7077 defer values.deinit();
73977078
7398 var lowest: ?u32 = null;7079 var lowest: ?u32 = null;
...@@ -7418,23 +7099,23 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7418,23 +7099,23 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7418 }7099 }
74197100
7420 // start block for 'true' branch7101 // start block for 'true' branch
7421 try func.startBlock(.block, wasm.block_empty);7102 try cg.startBlock(.block, .empty);
7422 // start block for 'false' branch7103 // start block for 'false' branch
7423 try func.startBlock(.block, wasm.block_empty);7104 try cg.startBlock(.block, .empty);
7424 // block for the jump table itself7105 // block for the jump table itself
7425 try func.startBlock(.block, wasm.block_empty);7106 try cg.startBlock(.block, .empty);
74267107
7427 // lower operand to determine jump table target7108 // lower operand to determine jump table target
7428 try func.emitWValue(operand);7109 try cg.emitWValue(operand);
7429 try func.addImm32(lowest.?);7110 try cg.addImm32(lowest.?);
7430 try func.addTag(.i32_sub);7111 try cg.addTag(.i32_sub);
74317112
7432 // Account for default branch so always add '1'7113 // Account for default branch so always add '1'
7433 const depth = @as(u32, @intCast(highest.? - lowest.? + 1));7114 const depth = @as(u32, @intCast(highest.? - lowest.? + 1));
7434 const jump_table: Mir.JumpTable = .{ .length = depth };7115 const jump_table: Mir.JumpTable = .{ .length = depth };
7435 const table_extra_index = try func.addExtra(jump_table);7116 const table_extra_index = try cg.addExtra(jump_table);
7436 try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });7117 try cg.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
7437 try func.mir_extra.ensureUnusedCapacity(func.gpa, depth);7118 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, depth);
74387119
7439 var value: u32 = lowest.?;7120 var value: u32 = lowest.?;
7440 while (value <= highest.?) : (value += 1) {7121 while (value <= highest.?) : (value += 1) {
...@@ -7444,202 +7125,200 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7444,202 +7125,200 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7444 }7125 }
7445 break :blk 0;7126 break :blk 0;
7446 };7127 };
7447 func.mir_extra.appendAssumeCapacity(idx);7128 cg.mir_extra.appendAssumeCapacity(idx);
7448 }7129 }
7449 try func.endBlock();7130 try cg.endBlock();
74507131
7451 // 'false' branch (i.e. error set does not have value7132 // 'false' branch (i.e. error set does not have value
7452 // ensure we set local to 0 in case the local was re-used.7133 // ensure we set local to 0 in case the local was re-used.
7453 try func.addImm32(0);7134 try cg.addImm32(0);
7454 try func.addLabel(.local_set, result.local.value);7135 try cg.addLocal(.local_set, result.local.value);
7455 try func.addLabel(.br, 1);7136 try cg.addLabel(.br, 1);
7456 try func.endBlock();7137 try cg.endBlock();
74577138
7458 // 'true' branch7139 // 'true' branch
7459 try func.addImm32(1);7140 try cg.addImm32(1);
7460 try func.addLabel(.local_set, result.local.value);7141 try cg.addLocal(.local_set, result.local.value);
7461 try func.addLabel(.br, 0);7142 try cg.addLabel(.br, 0);
7462 try func.endBlock();7143 try cg.endBlock();
74637144
7464 return func.finishAir(inst, result, &.{ty_op.operand});7145 return cg.finishAir(inst, result, &.{ty_op.operand});
7465}7146}
74667147
7467inline fn useAtomicFeature(func: *const CodeGen) bool {7148inline fn useAtomicFeature(cg: *const CodeGen) bool {
7468 return std.Target.wasm.featureSetHas(func.target.cpu.features, .atomics);7149 return std.Target.wasm.featureSetHas(cg.target.cpu.features, .atomics);
7469}7150}
74707151
7471fn airCmpxchg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {7152fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7472 const pt = func.pt;7153 const zcu = cg.pt.zcu;
7473 const zcu = pt.zcu;7154 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
7474 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;7155 const extra = cg.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
7475 const extra = func.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
74767156
7477 const ptr_ty = func.typeOf(extra.ptr);7157 const ptr_ty = cg.typeOf(extra.ptr);
7478 const ty = ptr_ty.childType(zcu);7158 const ty = ptr_ty.childType(zcu);
7479 const result_ty = func.typeOfIndex(inst);7159 const result_ty = cg.typeOfIndex(inst);
74807160
7481 const ptr_operand = try func.resolveInst(extra.ptr);7161 const ptr_operand = try cg.resolveInst(extra.ptr);
7482 const expected_val = try func.resolveInst(extra.expected_value);7162 const expected_val = try cg.resolveInst(extra.expected_value);
7483 const new_val = try func.resolveInst(extra.new_value);7163 const new_val = try cg.resolveInst(extra.new_value);
74847164
7485 const cmp_result = try func.allocLocal(Type.bool);7165 const cmp_result = try cg.allocLocal(Type.bool);
74867166
7487 const ptr_val = if (func.useAtomicFeature()) val: {7167 const ptr_val = if (cg.useAtomicFeature()) val: {
7488 const val_local = try func.allocLocal(ty);7168 const val_local = try cg.allocLocal(ty);
7489 try func.emitWValue(ptr_operand);7169 try cg.emitWValue(ptr_operand);
7490 try func.lowerToStack(expected_val);7170 try cg.lowerToStack(expected_val);
7491 try func.lowerToStack(new_val);7171 try cg.lowerToStack(new_val);
7492 try func.addAtomicMemArg(switch (ty.abiSize(zcu)) {7172 try cg.addAtomicMemArg(switch (ty.abiSize(zcu)) {
7493 1 => .i32_atomic_rmw8_cmpxchg_u,7173 1 => .i32_atomic_rmw8_cmpxchg_u,
7494 2 => .i32_atomic_rmw16_cmpxchg_u,7174 2 => .i32_atomic_rmw16_cmpxchg_u,
7495 4 => .i32_atomic_rmw_cmpxchg,7175 4 => .i32_atomic_rmw_cmpxchg,
7496 8 => .i32_atomic_rmw_cmpxchg,7176 8 => .i32_atomic_rmw_cmpxchg,
7497 else => |size| return func.fail("TODO: implement `@cmpxchg` for types with abi size '{d}'", .{size}),7177 else => |size| return cg.fail("TODO: implement `@cmpxchg` for types with abi size '{d}'", .{size}),
7498 }, .{7178 }, .{
7499 .offset = ptr_operand.offset(),7179 .offset = ptr_operand.offset(),
7500 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),7180 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
7501 });7181 });
7502 try func.addLabel(.local_tee, val_local.local.value);7182 try cg.addLocal(.local_tee, val_local.local.value);
7503 _ = try func.cmp(.stack, expected_val, ty, .eq);7183 _ = try cg.cmp(.stack, expected_val, ty, .eq);
7504 try func.addLabel(.local_set, cmp_result.local.value);7184 try cg.addLocal(.local_set, cmp_result.local.value);
7505 break :val val_local;7185 break :val val_local;
7506 } else val: {7186 } else val: {
7507 if (ty.abiSize(zcu) > 8) {7187 if (ty.abiSize(zcu) > 8) {
7508 return func.fail("TODO: Implement `@cmpxchg` for types larger than abi size of 8 bytes", .{});7188 return cg.fail("TODO: Implement `@cmpxchg` for types larger than abi size of 8 bytes", .{});
7509 }7189 }
7510 const ptr_val = try WValue.toLocal(try func.load(ptr_operand, ty, 0), func, ty);7190 const ptr_val = try WValue.toLocal(try cg.load(ptr_operand, ty, 0), cg, ty);
75117191
7512 try func.lowerToStack(ptr_operand);7192 try cg.lowerToStack(ptr_operand);
7513 try func.lowerToStack(new_val);7193 try cg.lowerToStack(new_val);
7514 try func.emitWValue(ptr_val);7194 try cg.emitWValue(ptr_val);
7515 _ = try func.cmp(ptr_val, expected_val, ty, .eq);7195 _ = try cg.cmp(ptr_val, expected_val, ty, .eq);
7516 try func.addLabel(.local_tee, cmp_result.local.value);7196 try cg.addLocal(.local_tee, cmp_result.local.value);
7517 try func.addTag(.select);7197 try cg.addTag(.select);
7518 try func.store(.stack, .stack, ty, 0);7198 try cg.store(.stack, .stack, ty, 0);
75197199
7520 break :val ptr_val;7200 break :val ptr_val;
7521 };7201 };
75227202
7523 const result = if (isByRef(result_ty, pt, func.target.*)) val: {7203 const result = if (isByRef(result_ty, zcu, cg.target)) val: {
7524 try func.emitWValue(cmp_result);7204 try cg.emitWValue(cmp_result);
7525 try func.addImm32(~@as(u32, 0));7205 try cg.addImm32(~@as(u32, 0));
7526 try func.addTag(.i32_xor);7206 try cg.addTag(.i32_xor);
7527 try func.addImm32(1);7207 try cg.addImm32(1);
7528 try func.addTag(.i32_and);7208 try cg.addTag(.i32_and);
7529 const and_result = try WValue.toLocal(.stack, func, Type.bool);7209 const and_result = try WValue.toLocal(.stack, cg, Type.bool);
7530 const result_ptr = try func.allocStack(result_ty);7210 const result_ptr = try cg.allocStack(result_ty);
7531 try func.store(result_ptr, and_result, Type.bool, @as(u32, @intCast(ty.abiSize(zcu))));7211 try cg.store(result_ptr, and_result, Type.bool, @as(u32, @intCast(ty.abiSize(zcu))));
7532 try func.store(result_ptr, ptr_val, ty, 0);7212 try cg.store(result_ptr, ptr_val, ty, 0);
7533 break :val result_ptr;7213 break :val result_ptr;
7534 } else val: {7214 } else val: {
7535 try func.addImm32(0);7215 try cg.addImm32(0);
7536 try func.emitWValue(ptr_val);7216 try cg.emitWValue(ptr_val);
7537 try func.emitWValue(cmp_result);7217 try cg.emitWValue(cmp_result);
7538 try func.addTag(.select);7218 try cg.addTag(.select);
7539 break :val .stack;7219 break :val .stack;
7540 };7220 };
75417221
7542 return func.finishAir(inst, result, &.{ extra.ptr, extra.expected_value, extra.new_value });7222 return cg.finishAir(inst, result, &.{ extra.ptr, extra.expected_value, extra.new_value });
7543}7223}
75447224
7545fn airAtomicLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {7225fn airAtomicLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7546 const pt = func.pt;7226 const zcu = cg.pt.zcu;
7547 const atomic_load = func.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;7227 const atomic_load = cg.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;
7548 const ptr = try func.resolveInst(atomic_load.ptr);7228 const ptr = try cg.resolveInst(atomic_load.ptr);
7549 const ty = func.typeOfIndex(inst);7229 const ty = cg.typeOfIndex(inst);
75507230
7551 if (func.useAtomicFeature()) {7231 if (cg.useAtomicFeature()) {
7552 const tag: wasm.AtomicsOpcode = switch (ty.abiSize(pt.zcu)) {7232 const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {
7553 1 => .i32_atomic_load8_u,7233 1 => .i32_atomic_load8_u,
7554 2 => .i32_atomic_load16_u,7234 2 => .i32_atomic_load16_u,
7555 4 => .i32_atomic_load,7235 4 => .i32_atomic_load,
7556 8 => .i64_atomic_load,7236 8 => .i64_atomic_load,
7557 else => |size| return func.fail("TODO: @atomicLoad for types with abi size {d}", .{size}),7237 else => |size| return cg.fail("TODO: @atomicLoad for types with abi size {d}", .{size}),
7558 };7238 };
7559 try func.emitWValue(ptr);7239 try cg.emitWValue(ptr);
7560 try func.addAtomicMemArg(tag, .{7240 try cg.addAtomicMemArg(tag, .{
7561 .offset = ptr.offset(),7241 .offset = ptr.offset(),
7562 .alignment = @intCast(ty.abiAlignment(pt.zcu).toByteUnits().?),7242 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
7563 });7243 });
7564 } else {7244 } else {
7565 _ = try func.load(ptr, ty, 0);7245 _ = try cg.load(ptr, ty, 0);
7566 }7246 }
75677247
7568 return func.finishAir(inst, .stack, &.{atomic_load.ptr});7248 return cg.finishAir(inst, .stack, &.{atomic_load.ptr});
7569}7249}
75707250
7571fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {7251fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7572 const pt = func.pt;7252 const zcu = cg.pt.zcu;
7573 const zcu = pt.zcu;7253 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
7574 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;7254 const extra = cg.air.extraData(Air.AtomicRmw, pl_op.payload).data;
7575 const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data;
75767255
7577 const ptr = try func.resolveInst(pl_op.operand);7256 const ptr = try cg.resolveInst(pl_op.operand);
7578 const operand = try func.resolveInst(extra.operand);7257 const operand = try cg.resolveInst(extra.operand);
7579 const ty = func.typeOfIndex(inst);7258 const ty = cg.typeOfIndex(inst);
7580 const op: std.builtin.AtomicRmwOp = extra.op();7259 const op: std.builtin.AtomicRmwOp = extra.op();
75817260
7582 if (func.useAtomicFeature()) {7261 if (cg.useAtomicFeature()) {
7583 switch (op) {7262 switch (op) {
7584 .Max,7263 .Max,
7585 .Min,7264 .Min,
7586 .Nand,7265 .Nand,
7587 => {7266 => {
7588 const tmp = try func.load(ptr, ty, 0);7267 const tmp = try cg.load(ptr, ty, 0);
7589 const value = try tmp.toLocal(func, ty);7268 const value = try tmp.toLocal(cg, ty);
75907269
7591 // create a loop to cmpxchg the new value7270 // create a loop to cmpxchg the new value
7592 try func.startBlock(.loop, wasm.block_empty);7271 try cg.startBlock(.loop, .empty);
75937272
7594 try func.emitWValue(ptr);7273 try cg.emitWValue(ptr);
7595 try func.emitWValue(value);7274 try cg.emitWValue(value);
7596 if (op == .Nand) {7275 if (op == .Nand) {
7597 const wasm_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?;7276 const wasm_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?;
75987277
7599 const and_res = try func.binOp(value, operand, ty, .@"and");7278 const and_res = try cg.binOp(value, operand, ty, .@"and");
7600 if (wasm_bits == 32)7279 if (wasm_bits == 32)
7601 try func.addImm32(~@as(u32, 0))7280 try cg.addImm32(~@as(u32, 0))
7602 else if (wasm_bits == 64)7281 else if (wasm_bits == 64)
7603 try func.addImm64(~@as(u64, 0))7282 try cg.addImm64(~@as(u64, 0))
7604 else7283 else
7605 return func.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{});7284 return cg.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{});
7606 _ = try func.binOp(and_res, .stack, ty, .xor);7285 _ = try cg.binOp(and_res, .stack, ty, .xor);
7607 } else {7286 } else {
7608 try func.emitWValue(value);7287 try cg.emitWValue(value);
7609 try func.emitWValue(operand);7288 try cg.emitWValue(operand);
7610 _ = try func.cmp(value, operand, ty, if (op == .Max) .gt else .lt);7289 _ = try cg.cmp(value, operand, ty, if (op == .Max) .gt else .lt);
7611 try func.addTag(.select);7290 try cg.addTag(.select);
7612 }7291 }
7613 try func.addAtomicMemArg(7292 try cg.addAtomicMemArg(
7614 switch (ty.abiSize(zcu)) {7293 switch (ty.abiSize(zcu)) {
7615 1 => .i32_atomic_rmw8_cmpxchg_u,7294 1 => .i32_atomic_rmw8_cmpxchg_u,
7616 2 => .i32_atomic_rmw16_cmpxchg_u,7295 2 => .i32_atomic_rmw16_cmpxchg_u,
7617 4 => .i32_atomic_rmw_cmpxchg,7296 4 => .i32_atomic_rmw_cmpxchg,
7618 8 => .i64_atomic_rmw_cmpxchg,7297 8 => .i64_atomic_rmw_cmpxchg,
7619 else => return func.fail("TODO: implement `@atomicRmw` with operation `{s}` for types larger than 64 bits", .{@tagName(op)}),7298 else => return cg.fail("TODO: implement `@atomicRmw` with operation `{s}` for types larger than 64 bits", .{@tagName(op)}),
7620 },7299 },
7621 .{7300 .{
7622 .offset = ptr.offset(),7301 .offset = ptr.offset(),
7623 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),7302 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
7624 },7303 },
7625 );7304 );
7626 const select_res = try func.allocLocal(ty);7305 const select_res = try cg.allocLocal(ty);
7627 try func.addLabel(.local_tee, select_res.local.value);7306 try cg.addLocal(.local_tee, select_res.local.value);
7628 _ = try func.cmp(.stack, value, ty, .neq); // leave on stack so we can use it for br_if7307 _ = try cg.cmp(.stack, value, ty, .neq); // leave on stack so we can use it for br_if
76297308
7630 try func.emitWValue(select_res);7309 try cg.emitWValue(select_res);
7631 try func.addLabel(.local_set, value.local.value);7310 try cg.addLocal(.local_set, value.local.value);
76327311
7633 try func.addLabel(.br_if, 0);7312 try cg.addLabel(.br_if, 0);
7634 try func.endBlock();7313 try cg.endBlock();
7635 return func.finishAir(inst, value, &.{ pl_op.operand, extra.operand });7314 return cg.finishAir(inst, value, &.{ pl_op.operand, extra.operand });
7636 },7315 },
76377316
7638 // the other operations have their own instructions for Wasm.7317 // the other operations have their own instructions for Wasm.
7639 else => {7318 else => {
7640 try func.emitWValue(ptr);7319 try cg.emitWValue(ptr);
7641 try func.emitWValue(operand);7320 try cg.emitWValue(operand);
7642 const tag: wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {7321 const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {
7643 1 => switch (op) {7322 1 => switch (op) {
7644 .Xchg => .i32_atomic_rmw8_xchg_u,7323 .Xchg => .i32_atomic_rmw8_xchg_u,
7645 .Add => .i32_atomic_rmw8_add_u,7324 .Add => .i32_atomic_rmw8_add_u,
...@@ -7676,22 +7355,22 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7676,22 +7355,22 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7676 .Xor => .i64_atomic_rmw_xor,7355 .Xor => .i64_atomic_rmw_xor,
7677 else => unreachable,7356 else => unreachable,
7678 },7357 },
7679 else => |size| return func.fail("TODO: Implement `@atomicRmw` for types with abi size {d}", .{size}),7358 else => |size| return cg.fail("TODO: Implement `@atomicRmw` for types with abi size {d}", .{size}),
7680 };7359 };
7681 try func.addAtomicMemArg(tag, .{7360 try cg.addAtomicMemArg(tag, .{
7682 .offset = ptr.offset(),7361 .offset = ptr.offset(),
7683 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),7362 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
7684 });7363 });
7685 return func.finishAir(inst, .stack, &.{ pl_op.operand, extra.operand });7364 return cg.finishAir(inst, .stack, &.{ pl_op.operand, extra.operand });
7686 },7365 },
7687 }7366 }
7688 } else {7367 } else {
7689 const loaded = try func.load(ptr, ty, 0);7368 const loaded = try cg.load(ptr, ty, 0);
7690 const result = try loaded.toLocal(func, ty);7369 const result = try loaded.toLocal(cg, ty);
76917370
7692 switch (op) {7371 switch (op) {
7693 .Xchg => {7372 .Xchg => {
7694 try func.store(ptr, operand, ty, 0);7373 try cg.store(ptr, operand, ty, 0);
7695 },7374 },
7696 .Add,7375 .Add,
7697 .Sub,7376 .Sub,
...@@ -7699,8 +7378,8 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7699,8 +7378,8 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7699 .Or,7378 .Or,
7700 .Xor,7379 .Xor,
7701 => {7380 => {
7702 try func.emitWValue(ptr);7381 try cg.emitWValue(ptr);
7703 _ = try func.binOp(result, operand, ty, switch (op) {7382 _ = try cg.binOp(result, operand, ty, switch (op) {
7704 .Add => .add,7383 .Add => .add,
7705 .Sub => .sub,7384 .Sub => .sub,
7706 .And => .@"and",7385 .And => .@"and",
...@@ -7709,87 +7388,123 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7709,87 +7388,123 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7709 else => unreachable,7388 else => unreachable,
7710 });7389 });
7711 if (ty.isInt(zcu) and (op == .Add or op == .Sub)) {7390 if (ty.isInt(zcu) and (op == .Add or op == .Sub)) {
7712 _ = try func.wrapOperand(.stack, ty);7391 _ = try cg.wrapOperand(.stack, ty);
7713 }7392 }
7714 try func.store(.stack, .stack, ty, ptr.offset());7393 try cg.store(.stack, .stack, ty, ptr.offset());
7715 },7394 },
7716 .Max,7395 .Max,
7717 .Min,7396 .Min,
7718 => {7397 => {
7719 try func.emitWValue(ptr);7398 try cg.emitWValue(ptr);
7720 try func.emitWValue(result);7399 try cg.emitWValue(result);
7721 try func.emitWValue(operand);7400 try cg.emitWValue(operand);
7722 _ = try func.cmp(result, operand, ty, if (op == .Max) .gt else .lt);7401 _ = try cg.cmp(result, operand, ty, if (op == .Max) .gt else .lt);
7723 try func.addTag(.select);7402 try cg.addTag(.select);
7724 try func.store(.stack, .stack, ty, ptr.offset());7403 try cg.store(.stack, .stack, ty, ptr.offset());
7725 },7404 },
7726 .Nand => {7405 .Nand => {
7727 const wasm_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?;7406 const wasm_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?;
77287407
7729 try func.emitWValue(ptr);7408 try cg.emitWValue(ptr);
7730 const and_res = try func.binOp(result, operand, ty, .@"and");7409 const and_res = try cg.binOp(result, operand, ty, .@"and");
7731 if (wasm_bits == 32)7410 if (wasm_bits == 32)
7732 try func.addImm32(~@as(u32, 0))7411 try cg.addImm32(~@as(u32, 0))
7733 else if (wasm_bits == 64)7412 else if (wasm_bits == 64)
7734 try func.addImm64(~@as(u64, 0))7413 try cg.addImm64(~@as(u64, 0))
7735 else7414 else
7736 return func.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{});7415 return cg.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{});
7737 _ = try func.binOp(and_res, .stack, ty, .xor);7416 _ = try cg.binOp(and_res, .stack, ty, .xor);
7738 try func.store(.stack, .stack, ty, ptr.offset());7417 try cg.store(.stack, .stack, ty, ptr.offset());
7739 },7418 },
7740 }7419 }
77417420
7742 return func.finishAir(inst, result, &.{ pl_op.operand, extra.operand });7421 return cg.finishAir(inst, result, &.{ pl_op.operand, extra.operand });
7743 }7422 }
7744}7423}
77457424
7746fn airAtomicStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {7425fn airAtomicStore(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7747 const pt = func.pt;7426 const zcu = cg.pt.zcu;
7748 const zcu = pt.zcu;7427 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
7749 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
77507428
7751 const ptr = try func.resolveInst(bin_op.lhs);7429 const ptr = try cg.resolveInst(bin_op.lhs);
7752 const operand = try func.resolveInst(bin_op.rhs);7430 const operand = try cg.resolveInst(bin_op.rhs);
7753 const ptr_ty = func.typeOf(bin_op.lhs);7431 const ptr_ty = cg.typeOf(bin_op.lhs);
7754 const ty = ptr_ty.childType(zcu);7432 const ty = ptr_ty.childType(zcu);
77557433
7756 if (func.useAtomicFeature()) {7434 if (cg.useAtomicFeature()) {
7757 const tag: wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {7435 const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {
7758 1 => .i32_atomic_store8,7436 1 => .i32_atomic_store8,
7759 2 => .i32_atomic_store16,7437 2 => .i32_atomic_store16,
7760 4 => .i32_atomic_store,7438 4 => .i32_atomic_store,
7761 8 => .i64_atomic_store,7439 8 => .i64_atomic_store,
7762 else => |size| return func.fail("TODO: @atomicLoad for types with abi size {d}", .{size}),7440 else => |size| return cg.fail("TODO: @atomicLoad for types with abi size {d}", .{size}),
7763 };7441 };
7764 try func.emitWValue(ptr);7442 try cg.emitWValue(ptr);
7765 try func.lowerToStack(operand);7443 try cg.lowerToStack(operand);
7766 try func.addAtomicMemArg(tag, .{7444 try cg.addAtomicMemArg(tag, .{
7767 .offset = ptr.offset(),7445 .offset = ptr.offset(),
7768 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),7446 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
7769 });7447 });
7770 } else {7448 } else {
7771 try func.store(ptr, operand, ty, 0);7449 try cg.store(ptr, operand, ty, 0);
7772 }7450 }
77737451
7774 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });7452 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
7775}7453}
77767454
7777fn airFrameAddress(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {7455fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7778 if (func.initial_stack_value == .none) {7456 if (cg.initial_stack_value == .none) {
7779 try func.initializeStack();7457 try cg.initializeStack();
7780 }7458 }
7781 try func.emitWValue(func.bottom_stack_value);7459 try cg.emitWValue(cg.bottom_stack_value);
7782 return func.finishAir(inst, .stack, &.{});7460 return cg.finishAir(inst, .stack, &.{});
7783}7461}
77847462
7785fn typeOf(func: *CodeGen, inst: Air.Inst.Ref) Type {7463fn typeOf(cg: *CodeGen, inst: Air.Inst.Ref) Type {
7786 const pt = func.pt;7464 const zcu = cg.pt.zcu;
7787 const zcu = pt.zcu;7465 return cg.air.typeOf(inst, &zcu.intern_pool);
7788 return func.air.typeOf(inst, &zcu.intern_pool);
7789}7466}
77907467
7791fn typeOfIndex(func: *CodeGen, inst: Air.Inst.Index) Type {7468fn typeOfIndex(cg: *CodeGen, inst: Air.Inst.Index) Type {
7792 const pt = func.pt;7469 const zcu = cg.pt.zcu;
7793 const zcu = pt.zcu;7470 return cg.air.typeOfIndex(inst, &zcu.intern_pool);
7794 return func.air.typeOfIndex(inst, &zcu.intern_pool);7471}
7472
7473fn floatCmpIntrinsic(op: std.math.CompareOperator, bits: u16) Mir.Intrinsic {
7474 return switch (op) {
7475 .lt => switch (bits) {
7476 80 => .__ltxf2,
7477 128 => .__lttf2,
7478 else => unreachable,
7479 },
7480 .lte => switch (bits) {
7481 80 => .__lexf2,
7482 128 => .__letf2,
7483 else => unreachable,
7484 },
7485 .eq => switch (bits) {
7486 80 => .__eqxf2,
7487 128 => .__eqtf2,
7488 else => unreachable,
7489 },
7490 .neq => switch (bits) {
7491 80 => .__nexf2,
7492 128 => .__netf2,
7493 else => unreachable,
7494 },
7495 .gte => switch (bits) {
7496 80 => .__gexf2,
7497 128 => .__getf2,
7498 else => unreachable,
7499 },
7500 .gt => switch (bits) {
7501 80 => .__gtxf2,
7502 128 => .__gttf2,
7503 else => unreachable,
7504 },
7505 };
7506}
7507
7508fn extraLen(cg: *const CodeGen) u32 {
7509 return @intCast(cg.mir_extra.items.len - cg.start_mir_extra_off);
7795}7510}
src/arch/wasm/Emit.zig+920-620
...@@ -1,673 +1,973 @@...@@ -1,673 +1,973 @@
1//! Contains all logic to lower wasm MIR into its binary
2//! or textual representation.
3
4const Emit = @This();1const Emit = @This();
2
5const std = @import("std");3const std = @import("std");
4const assert = std.debug.assert;
5const Allocator = std.mem.Allocator;
6const leb = std.leb;
7
8const Wasm = link.File.Wasm;
6const Mir = @import("Mir.zig");9const Mir = @import("Mir.zig");
7const link = @import("../../link.zig");10const link = @import("../../link.zig");
8const Zcu = @import("../../Zcu.zig");11const Zcu = @import("../../Zcu.zig");
9const InternPool = @import("../../InternPool.zig");12const InternPool = @import("../../InternPool.zig");
10const codegen = @import("../../codegen.zig");13const codegen = @import("../../codegen.zig");
11const leb128 = std.leb;
1214
13/// Contains our list of instructions
14mir: Mir,15mir: Mir,
15/// Reference to the Wasm module linker16wasm: *Wasm,
16bin_file: *link.File.Wasm,17/// The binary representation that will be emitted by this module.
17/// Possible error message. When set, the value is allocated and18code: *std.ArrayListUnmanaged(u8),
18/// must be freed manually.19
19error_msg: ?*Zcu.ErrorMsg = null,20pub const Error = error{
20/// The binary representation that will be emit by this module.
21code: *std.ArrayList(u8),
22/// List of allocated locals.
23locals: []const u8,
24/// The declaration that code is being generated for.
25owner_nav: InternPool.Nav.Index,
26
27// Debug information
28/// Holds the debug information for this emission
29dbg_output: link.File.DebugInfoOutput,
30/// Previous debug info line
31prev_di_line: u32,
32/// Previous debug info column
33prev_di_column: u32,
34/// Previous offset relative to code section
35prev_di_offset: u32,
36
37const InnerError = error{
38 OutOfMemory,21 OutOfMemory,
39 EmitFail,
40};22};
4123
42pub fn emitMir(emit: *Emit) InnerError!void {24pub fn lowerToCode(emit: *Emit) Error!void {
43 const mir_tags = emit.mir.instructions.items(.tag);25 const mir = &emit.mir;
44 // write the locals in the prologue of the function body26 const code = emit.code;
45 // before we emit the function body when lowering MIR27 const wasm = emit.wasm;
46 try emit.emitLocals();28 const comp = wasm.base.comp;
47
48 for (mir_tags, 0..) |tag, index| {
49 const inst = @as(u32, @intCast(index));
50 switch (tag) {
51 // block instructions
52 .block => try emit.emitBlock(tag, inst),
53 .loop => try emit.emitBlock(tag, inst),
54
55 .dbg_line => try emit.emitDbgLine(inst),
56 .dbg_epilogue_begin => try emit.emitDbgEpilogueBegin(),
57 .dbg_prologue_end => try emit.emitDbgPrologueEnd(),
58
59 // branch instructions
60 .br_if => try emit.emitLabel(tag, inst),
61 .br_table => try emit.emitBrTable(inst),
62 .br => try emit.emitLabel(tag, inst),
63
64 // relocatables
65 .call => try emit.emitCall(inst),
66 .call_indirect => try emit.emitCallIndirect(inst),
67 .global_get => try emit.emitGlobal(tag, inst),
68 .global_set => try emit.emitGlobal(tag, inst),
69 .function_index => try emit.emitFunctionIndex(inst),
70 .memory_address => try emit.emitMemAddress(inst),
71
72 // immediates
73 .f32_const => try emit.emitFloat32(inst),
74 .f64_const => try emit.emitFloat64(inst),
75 .i32_const => try emit.emitImm32(inst),
76 .i64_const => try emit.emitImm64(inst),
77
78 // memory instructions
79 .i32_load => try emit.emitMemArg(tag, inst),
80 .i64_load => try emit.emitMemArg(tag, inst),
81 .f32_load => try emit.emitMemArg(tag, inst),
82 .f64_load => try emit.emitMemArg(tag, inst),
83 .i32_load8_s => try emit.emitMemArg(tag, inst),
84 .i32_load8_u => try emit.emitMemArg(tag, inst),
85 .i32_load16_s => try emit.emitMemArg(tag, inst),
86 .i32_load16_u => try emit.emitMemArg(tag, inst),
87 .i64_load8_s => try emit.emitMemArg(tag, inst),
88 .i64_load8_u => try emit.emitMemArg(tag, inst),
89 .i64_load16_s => try emit.emitMemArg(tag, inst),
90 .i64_load16_u => try emit.emitMemArg(tag, inst),
91 .i64_load32_s => try emit.emitMemArg(tag, inst),
92 .i64_load32_u => try emit.emitMemArg(tag, inst),
93 .i32_store => try emit.emitMemArg(tag, inst),
94 .i64_store => try emit.emitMemArg(tag, inst),
95 .f32_store => try emit.emitMemArg(tag, inst),
96 .f64_store => try emit.emitMemArg(tag, inst),
97 .i32_store8 => try emit.emitMemArg(tag, inst),
98 .i32_store16 => try emit.emitMemArg(tag, inst),
99 .i64_store8 => try emit.emitMemArg(tag, inst),
100 .i64_store16 => try emit.emitMemArg(tag, inst),
101 .i64_store32 => try emit.emitMemArg(tag, inst),
102
103 // Instructions with an index that do not require relocations
104 .local_get => try emit.emitLabel(tag, inst),
105 .local_set => try emit.emitLabel(tag, inst),
106 .local_tee => try emit.emitLabel(tag, inst),
107 .memory_grow => try emit.emitLabel(tag, inst),
108 .memory_size => try emit.emitLabel(tag, inst),
109
110 // no-ops
111 .end => try emit.emitTag(tag),
112 .@"return" => try emit.emitTag(tag),
113 .@"unreachable" => try emit.emitTag(tag),
114
115 .select => try emit.emitTag(tag),
116
117 // arithmetic
118 .i32_eqz => try emit.emitTag(tag),
119 .i32_eq => try emit.emitTag(tag),
120 .i32_ne => try emit.emitTag(tag),
121 .i32_lt_s => try emit.emitTag(tag),
122 .i32_lt_u => try emit.emitTag(tag),
123 .i32_gt_s => try emit.emitTag(tag),
124 .i32_gt_u => try emit.emitTag(tag),
125 .i32_le_s => try emit.emitTag(tag),
126 .i32_le_u => try emit.emitTag(tag),
127 .i32_ge_s => try emit.emitTag(tag),
128 .i32_ge_u => try emit.emitTag(tag),
129 .i64_eqz => try emit.emitTag(tag),
130 .i64_eq => try emit.emitTag(tag),
131 .i64_ne => try emit.emitTag(tag),
132 .i64_lt_s => try emit.emitTag(tag),
133 .i64_lt_u => try emit.emitTag(tag),
134 .i64_gt_s => try emit.emitTag(tag),
135 .i64_gt_u => try emit.emitTag(tag),
136 .i64_le_s => try emit.emitTag(tag),
137 .i64_le_u => try emit.emitTag(tag),
138 .i64_ge_s => try emit.emitTag(tag),
139 .i64_ge_u => try emit.emitTag(tag),
140 .f32_eq => try emit.emitTag(tag),
141 .f32_ne => try emit.emitTag(tag),
142 .f32_lt => try emit.emitTag(tag),
143 .f32_gt => try emit.emitTag(tag),
144 .f32_le => try emit.emitTag(tag),
145 .f32_ge => try emit.emitTag(tag),
146 .f64_eq => try emit.emitTag(tag),
147 .f64_ne => try emit.emitTag(tag),
148 .f64_lt => try emit.emitTag(tag),
149 .f64_gt => try emit.emitTag(tag),
150 .f64_le => try emit.emitTag(tag),
151 .f64_ge => try emit.emitTag(tag),
152 .i32_add => try emit.emitTag(tag),
153 .i32_sub => try emit.emitTag(tag),
154 .i32_mul => try emit.emitTag(tag),
155 .i32_div_s => try emit.emitTag(tag),
156 .i32_div_u => try emit.emitTag(tag),
157 .i32_and => try emit.emitTag(tag),
158 .i32_or => try emit.emitTag(tag),
159 .i32_xor => try emit.emitTag(tag),
160 .i32_shl => try emit.emitTag(tag),
161 .i32_shr_s => try emit.emitTag(tag),
162 .i32_shr_u => try emit.emitTag(tag),
163 .i64_add => try emit.emitTag(tag),
164 .i64_sub => try emit.emitTag(tag),
165 .i64_mul => try emit.emitTag(tag),
166 .i64_div_s => try emit.emitTag(tag),
167 .i64_div_u => try emit.emitTag(tag),
168 .i64_and => try emit.emitTag(tag),
169 .i64_or => try emit.emitTag(tag),
170 .i64_xor => try emit.emitTag(tag),
171 .i64_shl => try emit.emitTag(tag),
172 .i64_shr_s => try emit.emitTag(tag),
173 .i64_shr_u => try emit.emitTag(tag),
174 .f32_abs => try emit.emitTag(tag),
175 .f32_neg => try emit.emitTag(tag),
176 .f32_ceil => try emit.emitTag(tag),
177 .f32_floor => try emit.emitTag(tag),
178 .f32_trunc => try emit.emitTag(tag),
179 .f32_nearest => try emit.emitTag(tag),
180 .f32_sqrt => try emit.emitTag(tag),
181 .f32_add => try emit.emitTag(tag),
182 .f32_sub => try emit.emitTag(tag),
183 .f32_mul => try emit.emitTag(tag),
184 .f32_div => try emit.emitTag(tag),
185 .f32_min => try emit.emitTag(tag),
186 .f32_max => try emit.emitTag(tag),
187 .f32_copysign => try emit.emitTag(tag),
188 .f64_abs => try emit.emitTag(tag),
189 .f64_neg => try emit.emitTag(tag),
190 .f64_ceil => try emit.emitTag(tag),
191 .f64_floor => try emit.emitTag(tag),
192 .f64_trunc => try emit.emitTag(tag),
193 .f64_nearest => try emit.emitTag(tag),
194 .f64_sqrt => try emit.emitTag(tag),
195 .f64_add => try emit.emitTag(tag),
196 .f64_sub => try emit.emitTag(tag),
197 .f64_mul => try emit.emitTag(tag),
198 .f64_div => try emit.emitTag(tag),
199 .f64_min => try emit.emitTag(tag),
200 .f64_max => try emit.emitTag(tag),
201 .f64_copysign => try emit.emitTag(tag),
202 .i32_wrap_i64 => try emit.emitTag(tag),
203 .i64_extend_i32_s => try emit.emitTag(tag),
204 .i64_extend_i32_u => try emit.emitTag(tag),
205 .i32_extend8_s => try emit.emitTag(tag),
206 .i32_extend16_s => try emit.emitTag(tag),
207 .i64_extend8_s => try emit.emitTag(tag),
208 .i64_extend16_s => try emit.emitTag(tag),
209 .i64_extend32_s => try emit.emitTag(tag),
210 .f32_demote_f64 => try emit.emitTag(tag),
211 .f64_promote_f32 => try emit.emitTag(tag),
212 .i32_reinterpret_f32 => try emit.emitTag(tag),
213 .i64_reinterpret_f64 => try emit.emitTag(tag),
214 .f32_reinterpret_i32 => try emit.emitTag(tag),
215 .f64_reinterpret_i64 => try emit.emitTag(tag),
216 .i32_trunc_f32_s => try emit.emitTag(tag),
217 .i32_trunc_f32_u => try emit.emitTag(tag),
218 .i32_trunc_f64_s => try emit.emitTag(tag),
219 .i32_trunc_f64_u => try emit.emitTag(tag),
220 .i64_trunc_f32_s => try emit.emitTag(tag),
221 .i64_trunc_f32_u => try emit.emitTag(tag),
222 .i64_trunc_f64_s => try emit.emitTag(tag),
223 .i64_trunc_f64_u => try emit.emitTag(tag),
224 .f32_convert_i32_s => try emit.emitTag(tag),
225 .f32_convert_i32_u => try emit.emitTag(tag),
226 .f32_convert_i64_s => try emit.emitTag(tag),
227 .f32_convert_i64_u => try emit.emitTag(tag),
228 .f64_convert_i32_s => try emit.emitTag(tag),
229 .f64_convert_i32_u => try emit.emitTag(tag),
230 .f64_convert_i64_s => try emit.emitTag(tag),
231 .f64_convert_i64_u => try emit.emitTag(tag),
232 .i32_rem_s => try emit.emitTag(tag),
233 .i32_rem_u => try emit.emitTag(tag),
234 .i64_rem_s => try emit.emitTag(tag),
235 .i64_rem_u => try emit.emitTag(tag),
236 .i32_popcnt => try emit.emitTag(tag),
237 .i64_popcnt => try emit.emitTag(tag),
238 .i32_clz => try emit.emitTag(tag),
239 .i32_ctz => try emit.emitTag(tag),
240 .i64_clz => try emit.emitTag(tag),
241 .i64_ctz => try emit.emitTag(tag),
242
243 .misc_prefix => try emit.emitExtended(inst),
244 .simd_prefix => try emit.emitSimd(inst),
245 .atomics_prefix => try emit.emitAtomic(inst),
246 }
247 }
248}
249
250fn offset(self: Emit) u32 {
251 return @as(u32, @intCast(self.code.items.len));
252}
253
254fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
255 @branchHint(.cold);
256 std.debug.assert(emit.error_msg == null);
257 const comp = emit.bin_file.base.comp;
258 const zcu = comp.zcu.?;
259 const gpa = comp.gpa;29 const gpa = comp.gpa;
260 emit.error_msg = try Zcu.ErrorMsg.create(gpa, zcu.navSrcLoc(emit.owner_nav), format, args);30 const is_obj = comp.config.output_mode == .Obj;
261 return error.EmitFail;31 const target = &comp.root_mod.resolved_target.result;
262}32 const is_wasm32 = target.cpu.arch == .wasm32;
263
264fn emitLocals(emit: *Emit) !void {
265 const writer = emit.code.writer();
266 try leb128.writeUleb128(writer, @as(u32, @intCast(emit.locals.len)));
267 // emit the actual locals amount
268 for (emit.locals) |local| {
269 try leb128.writeUleb128(writer, @as(u32, 1));
270 try writer.writeByte(local);
271 }
272}
273
274fn emitTag(emit: *Emit, tag: Mir.Inst.Tag) !void {
275 try emit.code.append(@intFromEnum(tag));
276}
277
278fn emitBlock(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
279 const block_type = emit.mir.instructions.items(.data)[inst].block_type;
280 try emit.code.append(@intFromEnum(tag));
281 try emit.code.append(block_type);
282}
283
284fn emitBrTable(emit: *Emit, inst: Mir.Inst.Index) !void {
285 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
286 const extra = emit.mir.extraData(Mir.JumpTable, extra_index);
287 const labels = emit.mir.extra[extra.end..][0..extra.data.length];
288 const writer = emit.code.writer();
28933
290 try emit.code.append(std.wasm.opcode(.br_table));34 const tags = mir.instruction_tags;
291 try leb128.writeUleb128(writer, extra.data.length - 1); // Default label is not part of length/depth35 const datas = mir.instruction_datas;
292 for (labels) |label| {36 var inst: u32 = 0;
293 try leb128.writeUleb128(writer, label);
294 }
295}
29637
297fn emitLabel(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {38 loop: switch (tags[inst]) {
298 const label = emit.mir.instructions.items(.data)[inst].label;39 .dbg_epilogue_begin => {
299 try emit.code.append(@intFromEnum(tag));40 return;
300 try leb128.writeUleb128(emit.code.writer(), label);41 },
301}42 .block, .loop => {
43 const block_type = datas[inst].block_type;
44 try code.ensureUnusedCapacity(gpa, 2);
45 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
46 code.appendAssumeCapacity(@intFromEnum(block_type));
47
48 inst += 1;
49 continue :loop tags[inst];
50 },
51 .uav_ref => {
52 if (is_obj) {
53 try uavRefOffObj(wasm, code, .{ .uav_obj = datas[inst].uav_obj, .offset = 0 }, is_wasm32);
54 } else {
55 try uavRefOffExe(wasm, code, .{ .uav_exe = datas[inst].uav_exe, .offset = 0 }, is_wasm32);
56 }
57 inst += 1;
58 continue :loop tags[inst];
59 },
60 .uav_ref_off => {
61 if (is_obj) {
62 try uavRefOffObj(wasm, code, mir.extraData(Mir.UavRefOffObj, datas[inst].payload).data, is_wasm32);
63 } else {
64 try uavRefOffExe(wasm, code, mir.extraData(Mir.UavRefOffExe, datas[inst].payload).data, is_wasm32);
65 }
66 inst += 1;
67 continue :loop tags[inst];
68 },
69 .nav_ref => {
70 try navRefOff(wasm, code, .{ .nav_index = datas[inst].nav_index, .offset = 0 }, is_wasm32);
71 inst += 1;
72 continue :loop tags[inst];
73 },
74 .nav_ref_off => {
75 try navRefOff(wasm, code, mir.extraData(Mir.NavRefOff, datas[inst].payload).data, is_wasm32);
76 inst += 1;
77 continue :loop tags[inst];
78 },
79 .func_ref => {
80 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
81 if (is_obj) {
82 @panic("TODO");
83 } else {
84 leb.writeUleb128(code.fixedWriter(), 1 + @intFromEnum(datas[inst].indirect_function_table_index)) catch unreachable;
85 }
86 inst += 1;
87 continue :loop tags[inst];
88 },
89 .dbg_line => {
90 inst += 1;
91 continue :loop tags[inst];
92 },
93 .errors_len => {
94 try code.ensureUnusedCapacity(gpa, 6);
95 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
96 // MIR is lowered during flush, so there is indeed only one thread at this time.
97 const errors_len = 1 + comp.zcu.?.intern_pool.global_error_set.getNamesFromMainThread().len;
98 leb.writeIleb128(code.fixedWriter(), errors_len) catch unreachable;
99
100 inst += 1;
101 continue :loop tags[inst];
102 },
103 .error_name_table_ref => {
104 try code.ensureUnusedCapacity(gpa, 11);
105 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
106 code.appendAssumeCapacity(@intFromEnum(opcode));
107 if (is_obj) {
108 try wasm.out_relocs.append(gpa, .{
109 .offset = @intCast(code.items.len),
110 .pointee = .{ .symbol_index = try wasm.errorNameTableSymbolIndex() },
111 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
112 .addend = 0,
113 });
114 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
115
116 inst += 1;
117 continue :loop tags[inst];
118 } else {
119 const addr: u32 = wasm.errorNameTableAddr();
120 leb.writeIleb128(code.fixedWriter(), addr) catch unreachable;
121
122 inst += 1;
123 continue :loop tags[inst];
124 }
125 },
126 .br_if, .br, .memory_grow, .memory_size => {
127 try code.ensureUnusedCapacity(gpa, 11);
128 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
129 leb.writeUleb128(code.fixedWriter(), datas[inst].label) catch unreachable;
302130
303fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {131 inst += 1;
304 const comp = emit.bin_file.base.comp;132 continue :loop tags[inst];
305 const gpa = comp.gpa;133 },
306 const label = emit.mir.instructions.items(.data)[inst].label;
307 try emit.code.append(@intFromEnum(tag));
308 var buf: [5]u8 = undefined;
309 leb128.writeUnsignedFixed(5, &buf, label);
310 const global_offset = emit.offset();
311 try emit.code.appendSlice(&buf);
312
313 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;
314 const atom = emit.bin_file.getAtomPtr(atom_index);
315 try atom.relocs.append(gpa, .{
316 .index = label,
317 .offset = global_offset,
318 .relocation_type = .R_WASM_GLOBAL_INDEX_LEB,
319 });
320}
321134
322fn emitImm32(emit: *Emit, inst: Mir.Inst.Index) !void {135 .local_get, .local_set, .local_tee => {
323 const value: i32 = emit.mir.instructions.items(.data)[inst].imm32;136 try code.ensureUnusedCapacity(gpa, 11);
324 try emit.code.append(std.wasm.opcode(.i32_const));137 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
325 try leb128.writeIleb128(emit.code.writer(), value);138 leb.writeUleb128(code.fixedWriter(), datas[inst].local) catch unreachable;
326}
327139
328fn emitImm64(emit: *Emit, inst: Mir.Inst.Index) !void {140 inst += 1;
329 const extra_index = emit.mir.instructions.items(.data)[inst].payload;141 continue :loop tags[inst];
330 const value = emit.mir.extraData(Mir.Imm64, extra_index);142 },
331 try emit.code.append(std.wasm.opcode(.i64_const));
332 try leb128.writeIleb128(emit.code.writer(), @as(i64, @bitCast(value.data.toU64())));
333}
334143
335fn emitFloat32(emit: *Emit, inst: Mir.Inst.Index) !void {144 .br_table => {
336 const value: f32 = emit.mir.instructions.items(.data)[inst].float32;145 const extra_index = datas[inst].payload;
337 try emit.code.append(std.wasm.opcode(.f32_const));146 const extra = mir.extraData(Mir.JumpTable, extra_index);
338 try emit.code.writer().writeInt(u32, @bitCast(value), .little);147 const labels = mir.extra[extra.end..][0..extra.data.length];
339}148 try code.ensureUnusedCapacity(gpa, 11 + 10 * labels.len);
149 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_table));
150 // -1 because default label is not part of length/depth.
151 leb.writeUleb128(code.fixedWriter(), extra.data.length - 1) catch unreachable;
152 for (labels) |label| leb.writeUleb128(code.fixedWriter(), label) catch unreachable;
153
154 inst += 1;
155 continue :loop tags[inst];
156 },
340157
341fn emitFloat64(emit: *Emit, inst: Mir.Inst.Index) !void {158 .call_nav => {
342 const extra_index = emit.mir.instructions.items(.data)[inst].payload;159 try code.ensureUnusedCapacity(gpa, 6);
343 const value = emit.mir.extraData(Mir.Float64, extra_index);160 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
344 try emit.code.append(std.wasm.opcode(.f64_const));161 if (is_obj) {
345 try emit.code.writer().writeInt(u64, value.data.toU64(), .little);162 try wasm.out_relocs.append(gpa, .{
346}163 .offset = @intCast(code.items.len),
164 .pointee = .{ .symbol_index = try wasm.navSymbolIndex(datas[inst].nav_index) },
165 .tag = .function_index_leb,
166 .addend = 0,
167 });
168 code.appendNTimesAssumeCapacity(0, 5);
169 } else {
170 appendOutputFunctionIndex(code, .fromIpNav(wasm, datas[inst].nav_index));
171 }
172
173 inst += 1;
174 continue :loop tags[inst];
175 },
347176
348fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {177 .call_indirect => {
349 const extra_index = emit.mir.instructions.items(.data)[inst].payload;178 try code.ensureUnusedCapacity(gpa, 11);
350 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;179 const func_ty_index = datas[inst].func_ty;
351 try emit.code.append(@intFromEnum(tag));180 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect));
352 try encodeMemArg(mem_arg, emit.code.writer());181 if (is_obj) {
353}182 try wasm.out_relocs.append(gpa, .{
183 .offset = @intCast(code.items.len),
184 .pointee = .{ .type_index = func_ty_index },
185 .tag = .type_index_leb,
186 .addend = 0,
187 });
188 code.appendNTimesAssumeCapacity(0, 5);
189 } else {
190 const index: Wasm.Flush.FuncTypeIndex = .fromTypeIndex(func_ty_index, &wasm.flush_buffer);
191 leb.writeUleb128(code.fixedWriter(), @intFromEnum(index)) catch unreachable;
192 }
193 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // table index
194
195 inst += 1;
196 continue :loop tags[inst];
197 },
354198
355fn encodeMemArg(mem_arg: Mir.MemArg, writer: anytype) !void {199 .call_tag_name => {
356 // wasm encodes alignment as power of 2, rather than natural alignment200 try code.ensureUnusedCapacity(gpa, 6);
357 const encoded_alignment = @ctz(mem_arg.alignment);201 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
358 try leb128.writeUleb128(writer, encoded_alignment);202 if (is_obj) {
359 try leb128.writeUleb128(writer, mem_arg.offset);203 try wasm.out_relocs.append(gpa, .{
360}204 .offset = @intCast(code.items.len),
205 .pointee = .{ .symbol_index = try wasm.tagNameSymbolIndex(datas[inst].ip_index) },
206 .tag = .function_index_leb,
207 .addend = 0,
208 });
209 code.appendNTimesAssumeCapacity(0, 5);
210 } else {
211 appendOutputFunctionIndex(code, .fromTagNameType(wasm, datas[inst].ip_index));
212 }
213
214 inst += 1;
215 continue :loop tags[inst];
216 },
361217
362fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {218 .call_intrinsic => {
363 const comp = emit.bin_file.base.comp;219 // Although this currently uses `wasm.internString`, note that it
364 const gpa = comp.gpa;220 // *could* be changed to directly index into a preloaded strings
365 const label = emit.mir.instructions.items(.data)[inst].label;221 // table initialized based on the `Mir.Intrinsic` enum.
366 try emit.code.append(std.wasm.opcode(.call));222 const symbol_name = try wasm.internString(@tagName(datas[inst].intrinsic));
367 const call_offset = emit.offset();223
368 var buf: [5]u8 = undefined;224 try code.ensureUnusedCapacity(gpa, 6);
369 leb128.writeUnsignedFixed(5, &buf, label);225 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
370 try emit.code.appendSlice(&buf);226 if (is_obj) {
371227 try wasm.out_relocs.append(gpa, .{
372 if (label != 0) {228 .offset = @intCast(code.items.len),
373 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;229 .pointee = .{ .symbol_index = try wasm.symbolNameIndex(symbol_name) },
374 const atom = emit.bin_file.getAtomPtr(atom_index);230 .tag = .function_index_leb,
375 try atom.relocs.append(gpa, .{231 .addend = 0,
376 .offset = call_offset,232 });
377 .index = label,233 code.appendNTimesAssumeCapacity(0, 5);
378 .relocation_type = .R_WASM_FUNCTION_INDEX_LEB,234 } else {
379 });235 appendOutputFunctionIndex(code, .fromSymbolName(wasm, symbol_name));
380 }236 }
381}237
238 inst += 1;
239 continue :loop tags[inst];
240 },
382241
383fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {242 .global_set_sp => {
384 const type_index = emit.mir.instructions.items(.data)[inst].label;243 try code.ensureUnusedCapacity(gpa, 6);
385 try emit.code.append(std.wasm.opcode(.call_indirect));244 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
386 // NOTE: If we remove unused function types in the future for incremental245 if (is_obj) {
387 // linking, we must also emit a relocation for this `type_index`246 try wasm.out_relocs.append(gpa, .{
388 const call_offset = emit.offset();247 .offset = @intCast(code.items.len),
389 var buf: [5]u8 = undefined;248 .pointee = .{ .symbol_index = try wasm.stackPointerSymbolIndex() },
390 leb128.writeUnsignedFixed(5, &buf, type_index);249 .tag = .global_index_leb,
391 try emit.code.appendSlice(&buf);250 .addend = 0,
392 if (type_index != 0) {251 });
393 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;252 code.appendNTimesAssumeCapacity(0, 5);
394 const atom = emit.bin_file.getAtomPtr(atom_index);253 } else {
395 try atom.relocs.append(emit.bin_file.base.comp.gpa, .{254 const sp_global: Wasm.GlobalIndex = .stack_pointer;
396 .offset = call_offset,255 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
397 .index = type_index,256 }
398 .relocation_type = .R_WASM_TYPE_INDEX_LEB,257
399 });258 inst += 1;
400 }259 continue :loop tags[inst];
401 try leb128.writeUleb128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index260 },
402}
403261
404fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {262 .f32_const => {
405 const comp = emit.bin_file.base.comp;263 try code.ensureUnusedCapacity(gpa, 5);
406 const gpa = comp.gpa;264 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f32_const));
407 const symbol_index = emit.mir.instructions.items(.data)[inst].label;265 std.mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @bitCast(datas[inst].float32), .little);
408 try emit.code.append(std.wasm.opcode(.i32_const));
409 const index_offset = emit.offset();
410 var buf: [5]u8 = undefined;
411 leb128.writeUnsignedFixed(5, &buf, symbol_index);
412 try emit.code.appendSlice(&buf);
413
414 if (symbol_index != 0) {
415 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;
416 const atom = emit.bin_file.getAtomPtr(atom_index);
417 try atom.relocs.append(gpa, .{
418 .offset = index_offset,
419 .index = symbol_index,
420 .relocation_type = .R_WASM_TABLE_INDEX_SLEB,
421 });
422 }
423}
424266
425fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {267 inst += 1;
426 const extra_index = emit.mir.instructions.items(.data)[inst].payload;268 continue :loop tags[inst];
427 const mem = emit.mir.extraData(Mir.Memory, extra_index).data;269 },
428 const mem_offset = emit.offset() + 1;
429 const comp = emit.bin_file.base.comp;
430 const gpa = comp.gpa;
431 const target = comp.root_mod.resolved_target.result;
432 const is_wasm32 = target.cpu.arch == .wasm32;
433 if (is_wasm32) {
434 try emit.code.append(std.wasm.opcode(.i32_const));
435 var buf: [5]u8 = undefined;
436 leb128.writeUnsignedFixed(5, &buf, mem.pointer);
437 try emit.code.appendSlice(&buf);
438 } else {
439 try emit.code.append(std.wasm.opcode(.i64_const));
440 var buf: [10]u8 = undefined;
441 leb128.writeUnsignedFixed(10, &buf, mem.pointer);
442 try emit.code.appendSlice(&buf);
443 }
444270
445 if (mem.pointer != 0) {271 .f64_const => {
446 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;272 try code.ensureUnusedCapacity(gpa, 9);
447 const atom = emit.bin_file.getAtomPtr(atom_index);273 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f64_const));
448 try atom.relocs.append(gpa, .{274 const float64 = mir.extraData(Mir.Float64, datas[inst].payload).data;
449 .offset = mem_offset,275 std.mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), float64.toInt(), .little);
450 .index = mem.pointer,
451 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,
452 .addend = @as(i32, @intCast(mem.offset)),
453 });
454 }
455}
456276
457fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {277 inst += 1;
458 const extra_index = emit.mir.instructions.items(.data)[inst].payload;278 continue :loop tags[inst];
459 const opcode = emit.mir.extra[extra_index];
460 const writer = emit.code.writer();
461 try emit.code.append(std.wasm.opcode(.misc_prefix));
462 try leb128.writeUleb128(writer, opcode);
463 switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) {
464 // bulk-memory opcodes
465 .data_drop => {
466 const segment = emit.mir.extra[extra_index + 1];
467 try leb128.writeUleb128(writer, segment);
468 },
469 .memory_init => {
470 const segment = emit.mir.extra[extra_index + 1];
471 try leb128.writeUleb128(writer, segment);
472 try leb128.writeUleb128(writer, @as(u32, 0)); // memory index
473 },279 },
474 .memory_fill => {280 .i32_const => {
475 try leb128.writeUleb128(writer, @as(u32, 0)); // memory index281 try code.ensureUnusedCapacity(gpa, 6);
282 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
283 leb.writeIleb128(code.fixedWriter(), datas[inst].imm32) catch unreachable;
284
285 inst += 1;
286 continue :loop tags[inst];
476 },287 },
477 .memory_copy => {288 .i64_const => {
478 try leb128.writeUleb128(writer, @as(u32, 0)); // dst memory index289 try code.ensureUnusedCapacity(gpa, 11);
479 try leb128.writeUleb128(writer, @as(u32, 0)); // src memory index290 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const));
291 const int64: i64 = @bitCast(mir.extraData(Mir.Imm64, datas[inst].payload).data.toInt());
292 leb.writeIleb128(code.fixedWriter(), int64) catch unreachable;
293
294 inst += 1;
295 continue :loop tags[inst];
480 },296 },
481297
482 // nontrapping-float-to-int-conversion opcodes298 .i32_load,
483 .i32_trunc_sat_f32_s,299 .i64_load,
484 .i32_trunc_sat_f32_u,300 .f32_load,
485 .i32_trunc_sat_f64_s,301 .f64_load,
486 .i32_trunc_sat_f64_u,302 .i32_load8_s,
487 .i64_trunc_sat_f32_s,303 .i32_load8_u,
488 .i64_trunc_sat_f32_u,304 .i32_load16_s,
489 .i64_trunc_sat_f64_s,305 .i32_load16_u,
490 .i64_trunc_sat_f64_u,306 .i64_load8_s,
491 => {}, // opcode already written307 .i64_load8_u,
492 else => |tag| return emit.fail("TODO: Implement extension instruction: {s}\n", .{@tagName(tag)}),308 .i64_load16_s,
493 }309 .i64_load16_u,
494}310 .i64_load32_s,
495311 .i64_load32_u,
496fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {312 .i32_store,
497 const extra_index = emit.mir.instructions.items(.data)[inst].payload;313 .i64_store,
498 const opcode = emit.mir.extra[extra_index];314 .f32_store,
499 const writer = emit.code.writer();315 .f64_store,
500 try emit.code.append(std.wasm.opcode(.simd_prefix));316 .i32_store8,
501 try leb128.writeUleb128(writer, opcode);317 .i32_store16,
502 switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) {318 .i64_store8,
503 .v128_store,319 .i64_store16,
504 .v128_load,320 .i64_store32,
505 .v128_load8_splat,
506 .v128_load16_splat,
507 .v128_load32_splat,
508 .v128_load64_splat,
509 => {321 => {
510 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data;322 try code.ensureUnusedCapacity(gpa, 1 + 20);
511 try encodeMemArg(mem_arg, writer);323 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
324 encodeMemArg(code, mir.extraData(Mir.MemArg, datas[inst].payload).data);
325 inst += 1;
326 continue :loop tags[inst];
512 },327 },
513 .v128_const,328
514 .i8x16_shuffle,329 .end,
515 => {330 .@"return",
516 const simd_value = emit.mir.extra[extra_index + 1 ..][0..4];331 .@"unreachable",
517 try writer.writeAll(std.mem.asBytes(simd_value));332 .select,
518 },333 .i32_eqz,
519 .i8x16_extract_lane_s,334 .i32_eq,
520 .i8x16_extract_lane_u,335 .i32_ne,
521 .i8x16_replace_lane,336 .i32_lt_s,
522 .i16x8_extract_lane_s,337 .i32_lt_u,
523 .i16x8_extract_lane_u,338 .i32_gt_s,
524 .i16x8_replace_lane,339 .i32_gt_u,
525 .i32x4_extract_lane,340 .i32_le_s,
526 .i32x4_replace_lane,341 .i32_le_u,
527 .i64x2_extract_lane,342 .i32_ge_s,
528 .i64x2_replace_lane,343 .i32_ge_u,
529 .f32x4_extract_lane,344 .i64_eqz,
530 .f32x4_replace_lane,345 .i64_eq,
531 .f64x2_extract_lane,346 .i64_ne,
532 .f64x2_replace_lane,347 .i64_lt_s,
348 .i64_lt_u,
349 .i64_gt_s,
350 .i64_gt_u,
351 .i64_le_s,
352 .i64_le_u,
353 .i64_ge_s,
354 .i64_ge_u,
355 .f32_eq,
356 .f32_ne,
357 .f32_lt,
358 .f32_gt,
359 .f32_le,
360 .f32_ge,
361 .f64_eq,
362 .f64_ne,
363 .f64_lt,
364 .f64_gt,
365 .f64_le,
366 .f64_ge,
367 .i32_add,
368 .i32_sub,
369 .i32_mul,
370 .i32_div_s,
371 .i32_div_u,
372 .i32_and,
373 .i32_or,
374 .i32_xor,
375 .i32_shl,
376 .i32_shr_s,
377 .i32_shr_u,
378 .i64_add,
379 .i64_sub,
380 .i64_mul,
381 .i64_div_s,
382 .i64_div_u,
383 .i64_and,
384 .i64_or,
385 .i64_xor,
386 .i64_shl,
387 .i64_shr_s,
388 .i64_shr_u,
389 .f32_abs,
390 .f32_neg,
391 .f32_ceil,
392 .f32_floor,
393 .f32_trunc,
394 .f32_nearest,
395 .f32_sqrt,
396 .f32_add,
397 .f32_sub,
398 .f32_mul,
399 .f32_div,
400 .f32_min,
401 .f32_max,
402 .f32_copysign,
403 .f64_abs,
404 .f64_neg,
405 .f64_ceil,
406 .f64_floor,
407 .f64_trunc,
408 .f64_nearest,
409 .f64_sqrt,
410 .f64_add,
411 .f64_sub,
412 .f64_mul,
413 .f64_div,
414 .f64_min,
415 .f64_max,
416 .f64_copysign,
417 .i32_wrap_i64,
418 .i64_extend_i32_s,
419 .i64_extend_i32_u,
420 .i32_extend8_s,
421 .i32_extend16_s,
422 .i64_extend8_s,
423 .i64_extend16_s,
424 .i64_extend32_s,
425 .f32_demote_f64,
426 .f64_promote_f32,
427 .i32_reinterpret_f32,
428 .i64_reinterpret_f64,
429 .f32_reinterpret_i32,
430 .f64_reinterpret_i64,
431 .i32_trunc_f32_s,
432 .i32_trunc_f32_u,
433 .i32_trunc_f64_s,
434 .i32_trunc_f64_u,
435 .i64_trunc_f32_s,
436 .i64_trunc_f32_u,
437 .i64_trunc_f64_s,
438 .i64_trunc_f64_u,
439 .f32_convert_i32_s,
440 .f32_convert_i32_u,
441 .f32_convert_i64_s,
442 .f32_convert_i64_u,
443 .f64_convert_i32_s,
444 .f64_convert_i32_u,
445 .f64_convert_i64_s,
446 .f64_convert_i64_u,
447 .i32_rem_s,
448 .i32_rem_u,
449 .i64_rem_s,
450 .i64_rem_u,
451 .i32_popcnt,
452 .i64_popcnt,
453 .i32_clz,
454 .i32_ctz,
455 .i64_clz,
456 .i64_ctz,
533 => {457 => {
534 try writer.writeByte(@as(u8, @intCast(emit.mir.extra[extra_index + 1])));458 try code.append(gpa, @intFromEnum(tags[inst]));
459 inst += 1;
460 continue :loop tags[inst];
535 },461 },
536 .i8x16_splat,
537 .i16x8_splat,
538 .i32x4_splat,
539 .i64x2_splat,
540 .f32x4_splat,
541 .f64x2_splat,
542 => {}, // opcode already written
543 else => |tag| return emit.fail("TODO: Implement simd instruction: {s}", .{@tagName(tag)}),
544 }
545}
546462
547fn emitAtomic(emit: *Emit, inst: Mir.Inst.Index) !void {463 .misc_prefix => {
548 const extra_index = emit.mir.instructions.items(.data)[inst].payload;464 try code.ensureUnusedCapacity(gpa, 6 + 6);
549 const opcode = emit.mir.extra[extra_index];465 const extra_index = datas[inst].payload;
550 const writer = emit.code.writer();466 const opcode = mir.extra[extra_index];
551 try emit.code.append(std.wasm.opcode(.atomics_prefix));467 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix));
552 try leb128.writeUleb128(writer, opcode);468 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;
553 switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) {469 switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) {
554 .i32_atomic_load,470 // bulk-memory opcodes
555 .i64_atomic_load,471 .data_drop => {
556 .i32_atomic_load8_u,472 const segment = mir.extra[extra_index + 1];
557 .i32_atomic_load16_u,473 leb.writeUleb128(code.fixedWriter(), segment) catch unreachable;
558 .i64_atomic_load8_u,474
559 .i64_atomic_load16_u,475 inst += 1;
560 .i64_atomic_load32_u,476 continue :loop tags[inst];
561 .i32_atomic_store,477 },
562 .i64_atomic_store,478 .memory_init => {
563 .i32_atomic_store8,479 const segment = mir.extra[extra_index + 1];
564 .i32_atomic_store16,480 leb.writeUleb128(code.fixedWriter(), segment) catch unreachable;
565 .i64_atomic_store8,481 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index
566 .i64_atomic_store16,482
567 .i64_atomic_store32,483 inst += 1;
568 .i32_atomic_rmw_add,484 continue :loop tags[inst];
569 .i64_atomic_rmw_add,485 },
570 .i32_atomic_rmw8_add_u,486 .memory_fill => {
571 .i32_atomic_rmw16_add_u,487 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index
572 .i64_atomic_rmw8_add_u,488
573 .i64_atomic_rmw16_add_u,489 inst += 1;
574 .i64_atomic_rmw32_add_u,490 continue :loop tags[inst];
575 .i32_atomic_rmw_sub,491 },
576 .i64_atomic_rmw_sub,492 .memory_copy => {
577 .i32_atomic_rmw8_sub_u,493 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // dst memory index
578 .i32_atomic_rmw16_sub_u,494 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // src memory index
579 .i64_atomic_rmw8_sub_u,495
580 .i64_atomic_rmw16_sub_u,496 inst += 1;
581 .i64_atomic_rmw32_sub_u,497 continue :loop tags[inst];
582 .i32_atomic_rmw_and,498 },
583 .i64_atomic_rmw_and,499
584 .i32_atomic_rmw8_and_u,500 // nontrapping-float-to-int-conversion opcodes
585 .i32_atomic_rmw16_and_u,501 .i32_trunc_sat_f32_s,
586 .i64_atomic_rmw8_and_u,502 .i32_trunc_sat_f32_u,
587 .i64_atomic_rmw16_and_u,503 .i32_trunc_sat_f64_s,
588 .i64_atomic_rmw32_and_u,504 .i32_trunc_sat_f64_u,
589 .i32_atomic_rmw_or,505 .i64_trunc_sat_f32_s,
590 .i64_atomic_rmw_or,506 .i64_trunc_sat_f32_u,
591 .i32_atomic_rmw8_or_u,507 .i64_trunc_sat_f64_s,
592 .i32_atomic_rmw16_or_u,508 .i64_trunc_sat_f64_u,
593 .i64_atomic_rmw8_or_u,509 => {
594 .i64_atomic_rmw16_or_u,510 inst += 1;
595 .i64_atomic_rmw32_or_u,511 continue :loop tags[inst];
596 .i32_atomic_rmw_xor,512 },
597 .i64_atomic_rmw_xor,513
598 .i32_atomic_rmw8_xor_u,514 .table_init => @panic("TODO"),
599 .i32_atomic_rmw16_xor_u,515 .elem_drop => @panic("TODO"),
600 .i64_atomic_rmw8_xor_u,516 .table_copy => @panic("TODO"),
601 .i64_atomic_rmw16_xor_u,517 .table_grow => @panic("TODO"),
602 .i64_atomic_rmw32_xor_u,518 .table_size => @panic("TODO"),
603 .i32_atomic_rmw_xchg,519 .table_fill => @panic("TODO"),
604 .i64_atomic_rmw_xchg,520
605 .i32_atomic_rmw8_xchg_u,521 _ => unreachable,
606 .i32_atomic_rmw16_xchg_u,522 }
607 .i64_atomic_rmw8_xchg_u,523 comptime unreachable;
608 .i64_atomic_rmw16_xchg_u,
609 .i64_atomic_rmw32_xchg_u,
610
611 .i32_atomic_rmw_cmpxchg,
612 .i64_atomic_rmw_cmpxchg,
613 .i32_atomic_rmw8_cmpxchg_u,
614 .i32_atomic_rmw16_cmpxchg_u,
615 .i64_atomic_rmw8_cmpxchg_u,
616 .i64_atomic_rmw16_cmpxchg_u,
617 .i64_atomic_rmw32_cmpxchg_u,
618 => {
619 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data;
620 try encodeMemArg(mem_arg, writer);
621 },524 },
622 .atomic_fence => {525 .simd_prefix => {
623 // TODO: When multi-memory proposal is accepted and implemented in the compiler,526 try code.ensureUnusedCapacity(gpa, 6 + 20);
624 // change this to (user-)specified index, rather than hardcode it to memory index 0.527 const extra_index = datas[inst].payload;
625 const memory_index: u32 = 0;528 const opcode = mir.extra[extra_index];
626 try leb128.writeUleb128(writer, memory_index);529 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.simd_prefix));
530 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;
531 switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) {
532 .v128_store,
533 .v128_load,
534 .v128_load8_splat,
535 .v128_load16_splat,
536 .v128_load32_splat,
537 .v128_load64_splat,
538 => {
539 encodeMemArg(code, mir.extraData(Mir.MemArg, extra_index + 1).data);
540 inst += 1;
541 continue :loop tags[inst];
542 },
543 .v128_const, .i8x16_shuffle => {
544 code.appendSliceAssumeCapacity(std.mem.asBytes(mir.extra[extra_index + 1 ..][0..4]));
545 inst += 1;
546 continue :loop tags[inst];
547 },
548 .i8x16_extract_lane_s,
549 .i8x16_extract_lane_u,
550 .i8x16_replace_lane,
551 .i16x8_extract_lane_s,
552 .i16x8_extract_lane_u,
553 .i16x8_replace_lane,
554 .i32x4_extract_lane,
555 .i32x4_replace_lane,
556 .i64x2_extract_lane,
557 .i64x2_replace_lane,
558 .f32x4_extract_lane,
559 .f32x4_replace_lane,
560 .f64x2_extract_lane,
561 .f64x2_replace_lane,
562 => {
563 code.appendAssumeCapacity(@intCast(mir.extra[extra_index + 1]));
564 inst += 1;
565 continue :loop tags[inst];
566 },
567 .i8x16_splat,
568 .i16x8_splat,
569 .i32x4_splat,
570 .i64x2_splat,
571 .f32x4_splat,
572 .f64x2_splat,
573 => {
574 inst += 1;
575 continue :loop tags[inst];
576 },
577
578 .v128_load8x8_s => @panic("TODO"),
579 .v128_load8x8_u => @panic("TODO"),
580 .v128_load16x4_s => @panic("TODO"),
581 .v128_load16x4_u => @panic("TODO"),
582 .v128_load32x2_s => @panic("TODO"),
583 .v128_load32x2_u => @panic("TODO"),
584 .i8x16_swizzle => @panic("TODO"),
585 .i8x16_eq => @panic("TODO"),
586 .i16x8_eq => @panic("TODO"),
587 .i32x4_eq => @panic("TODO"),
588 .i8x16_ne => @panic("TODO"),
589 .i16x8_ne => @panic("TODO"),
590 .i32x4_ne => @panic("TODO"),
591 .i8x16_lt_s => @panic("TODO"),
592 .i16x8_lt_s => @panic("TODO"),
593 .i32x4_lt_s => @panic("TODO"),
594 .i8x16_lt_u => @panic("TODO"),
595 .i16x8_lt_u => @panic("TODO"),
596 .i32x4_lt_u => @panic("TODO"),
597 .i8x16_gt_s => @panic("TODO"),
598 .i16x8_gt_s => @panic("TODO"),
599 .i32x4_gt_s => @panic("TODO"),
600 .i8x16_gt_u => @panic("TODO"),
601 .i16x8_gt_u => @panic("TODO"),
602 .i32x4_gt_u => @panic("TODO"),
603 .i8x16_le_s => @panic("TODO"),
604 .i16x8_le_s => @panic("TODO"),
605 .i32x4_le_s => @panic("TODO"),
606 .i8x16_le_u => @panic("TODO"),
607 .i16x8_le_u => @panic("TODO"),
608 .i32x4_le_u => @panic("TODO"),
609 .i8x16_ge_s => @panic("TODO"),
610 .i16x8_ge_s => @panic("TODO"),
611 .i32x4_ge_s => @panic("TODO"),
612 .i8x16_ge_u => @panic("TODO"),
613 .i16x8_ge_u => @panic("TODO"),
614 .i32x4_ge_u => @panic("TODO"),
615 .f32x4_eq => @panic("TODO"),
616 .f64x2_eq => @panic("TODO"),
617 .f32x4_ne => @panic("TODO"),
618 .f64x2_ne => @panic("TODO"),
619 .f32x4_lt => @panic("TODO"),
620 .f64x2_lt => @panic("TODO"),
621 .f32x4_gt => @panic("TODO"),
622 .f64x2_gt => @panic("TODO"),
623 .f32x4_le => @panic("TODO"),
624 .f64x2_le => @panic("TODO"),
625 .f32x4_ge => @panic("TODO"),
626 .f64x2_ge => @panic("TODO"),
627 .v128_not => @panic("TODO"),
628 .v128_and => @panic("TODO"),
629 .v128_andnot => @panic("TODO"),
630 .v128_or => @panic("TODO"),
631 .v128_xor => @panic("TODO"),
632 .v128_bitselect => @panic("TODO"),
633 .v128_any_true => @panic("TODO"),
634 .v128_load8_lane => @panic("TODO"),
635 .v128_load16_lane => @panic("TODO"),
636 .v128_load32_lane => @panic("TODO"),
637 .v128_load64_lane => @panic("TODO"),
638 .v128_store8_lane => @panic("TODO"),
639 .v128_store16_lane => @panic("TODO"),
640 .v128_store32_lane => @panic("TODO"),
641 .v128_store64_lane => @panic("TODO"),
642 .v128_load32_zero => @panic("TODO"),
643 .v128_load64_zero => @panic("TODO"),
644 .f32x4_demote_f64x2_zero => @panic("TODO"),
645 .f64x2_promote_low_f32x4 => @panic("TODO"),
646 .i8x16_abs => @panic("TODO"),
647 .i16x8_abs => @panic("TODO"),
648 .i32x4_abs => @panic("TODO"),
649 .i64x2_abs => @panic("TODO"),
650 .i8x16_neg => @panic("TODO"),
651 .i16x8_neg => @panic("TODO"),
652 .i32x4_neg => @panic("TODO"),
653 .i64x2_neg => @panic("TODO"),
654 .i8x16_popcnt => @panic("TODO"),
655 .i16x8_q15mulr_sat_s => @panic("TODO"),
656 .i8x16_all_true => @panic("TODO"),
657 .i16x8_all_true => @panic("TODO"),
658 .i32x4_all_true => @panic("TODO"),
659 .i64x2_all_true => @panic("TODO"),
660 .i8x16_bitmask => @panic("TODO"),
661 .i16x8_bitmask => @panic("TODO"),
662 .i32x4_bitmask => @panic("TODO"),
663 .i64x2_bitmask => @panic("TODO"),
664 .i8x16_narrow_i16x8_s => @panic("TODO"),
665 .i16x8_narrow_i32x4_s => @panic("TODO"),
666 .i8x16_narrow_i16x8_u => @panic("TODO"),
667 .i16x8_narrow_i32x4_u => @panic("TODO"),
668 .f32x4_ceil => @panic("TODO"),
669 .i16x8_extend_low_i8x16_s => @panic("TODO"),
670 .i32x4_extend_low_i16x8_s => @panic("TODO"),
671 .i64x2_extend_low_i32x4_s => @panic("TODO"),
672 .f32x4_floor => @panic("TODO"),
673 .i16x8_extend_high_i8x16_s => @panic("TODO"),
674 .i32x4_extend_high_i16x8_s => @panic("TODO"),
675 .i64x2_extend_high_i32x4_s => @panic("TODO"),
676 .f32x4_trunc => @panic("TODO"),
677 .i16x8_extend_low_i8x16_u => @panic("TODO"),
678 .i32x4_extend_low_i16x8_u => @panic("TODO"),
679 .i64x2_extend_low_i32x4_u => @panic("TODO"),
680 .f32x4_nearest => @panic("TODO"),
681 .i16x8_extend_high_i8x16_u => @panic("TODO"),
682 .i32x4_extend_high_i16x8_u => @panic("TODO"),
683 .i64x2_extend_high_i32x4_u => @panic("TODO"),
684 .i8x16_shl => @panic("TODO"),
685 .i16x8_shl => @panic("TODO"),
686 .i32x4_shl => @panic("TODO"),
687 .i64x2_shl => @panic("TODO"),
688 .i8x16_shr_s => @panic("TODO"),
689 .i16x8_shr_s => @panic("TODO"),
690 .i32x4_shr_s => @panic("TODO"),
691 .i64x2_shr_s => @panic("TODO"),
692 .i8x16_shr_u => @panic("TODO"),
693 .i16x8_shr_u => @panic("TODO"),
694 .i32x4_shr_u => @panic("TODO"),
695 .i64x2_shr_u => @panic("TODO"),
696 .i8x16_add => @panic("TODO"),
697 .i16x8_add => @panic("TODO"),
698 .i32x4_add => @panic("TODO"),
699 .i64x2_add => @panic("TODO"),
700 .i8x16_add_sat_s => @panic("TODO"),
701 .i16x8_add_sat_s => @panic("TODO"),
702 .i8x16_add_sat_u => @panic("TODO"),
703 .i16x8_add_sat_u => @panic("TODO"),
704 .i8x16_sub => @panic("TODO"),
705 .i16x8_sub => @panic("TODO"),
706 .i32x4_sub => @panic("TODO"),
707 .i64x2_sub => @panic("TODO"),
708 .i8x16_sub_sat_s => @panic("TODO"),
709 .i16x8_sub_sat_s => @panic("TODO"),
710 .i8x16_sub_sat_u => @panic("TODO"),
711 .i16x8_sub_sat_u => @panic("TODO"),
712 .f64x2_ceil => @panic("TODO"),
713 .f64x2_nearest => @panic("TODO"),
714 .f64x2_floor => @panic("TODO"),
715 .i16x8_mul => @panic("TODO"),
716 .i32x4_mul => @panic("TODO"),
717 .i64x2_mul => @panic("TODO"),
718 .i8x16_min_s => @panic("TODO"),
719 .i16x8_min_s => @panic("TODO"),
720 .i32x4_min_s => @panic("TODO"),
721 .i64x2_eq => @panic("TODO"),
722 .i8x16_min_u => @panic("TODO"),
723 .i16x8_min_u => @panic("TODO"),
724 .i32x4_min_u => @panic("TODO"),
725 .i64x2_ne => @panic("TODO"),
726 .i8x16_max_s => @panic("TODO"),
727 .i16x8_max_s => @panic("TODO"),
728 .i32x4_max_s => @panic("TODO"),
729 .i64x2_lt_s => @panic("TODO"),
730 .i8x16_max_u => @panic("TODO"),
731 .i16x8_max_u => @panic("TODO"),
732 .i32x4_max_u => @panic("TODO"),
733 .i64x2_gt_s => @panic("TODO"),
734 .f64x2_trunc => @panic("TODO"),
735 .i32x4_dot_i16x8_s => @panic("TODO"),
736 .i64x2_le_s => @panic("TODO"),
737 .i8x16_avgr_u => @panic("TODO"),
738 .i16x8_avgr_u => @panic("TODO"),
739 .i64x2_ge_s => @panic("TODO"),
740 .i16x8_extadd_pairwise_i8x16_s => @panic("TODO"),
741 .i16x8_extmul_low_i8x16_s => @panic("TODO"),
742 .i32x4_extmul_low_i16x8_s => @panic("TODO"),
743 .i64x2_extmul_low_i32x4_s => @panic("TODO"),
744 .i16x8_extadd_pairwise_i8x16_u => @panic("TODO"),
745 .i16x8_extmul_high_i8x16_s => @panic("TODO"),
746 .i32x4_extmul_high_i16x8_s => @panic("TODO"),
747 .i64x2_extmul_high_i32x4_s => @panic("TODO"),
748 .i32x4_extadd_pairwise_i16x8_s => @panic("TODO"),
749 .i16x8_extmul_low_i8x16_u => @panic("TODO"),
750 .i32x4_extmul_low_i16x8_u => @panic("TODO"),
751 .i64x2_extmul_low_i32x4_u => @panic("TODO"),
752 .i32x4_extadd_pairwise_i16x8_u => @panic("TODO"),
753 .i16x8_extmul_high_i8x16_u => @panic("TODO"),
754 .i32x4_extmul_high_i16x8_u => @panic("TODO"),
755 .i64x2_extmul_high_i32x4_u => @panic("TODO"),
756 .f32x4_abs => @panic("TODO"),
757 .f64x2_abs => @panic("TODO"),
758 .f32x4_neg => @panic("TODO"),
759 .f64x2_neg => @panic("TODO"),
760 .f32x4_sqrt => @panic("TODO"),
761 .f64x2_sqrt => @panic("TODO"),
762 .f32x4_add => @panic("TODO"),
763 .f64x2_add => @panic("TODO"),
764 .f32x4_sub => @panic("TODO"),
765 .f64x2_sub => @panic("TODO"),
766 .f32x4_mul => @panic("TODO"),
767 .f64x2_mul => @panic("TODO"),
768 .f32x4_div => @panic("TODO"),
769 .f64x2_div => @panic("TODO"),
770 .f32x4_min => @panic("TODO"),
771 .f64x2_min => @panic("TODO"),
772 .f32x4_max => @panic("TODO"),
773 .f64x2_max => @panic("TODO"),
774 .f32x4_pmin => @panic("TODO"),
775 .f64x2_pmin => @panic("TODO"),
776 .f32x4_pmax => @panic("TODO"),
777 .f64x2_pmax => @panic("TODO"),
778 .i32x4_trunc_sat_f32x4_s => @panic("TODO"),
779 .i32x4_trunc_sat_f32x4_u => @panic("TODO"),
780 .f32x4_convert_i32x4_s => @panic("TODO"),
781 .f32x4_convert_i32x4_u => @panic("TODO"),
782 .i32x4_trunc_sat_f64x2_s_zero => @panic("TODO"),
783 .i32x4_trunc_sat_f64x2_u_zero => @panic("TODO"),
784 .f64x2_convert_low_i32x4_s => @panic("TODO"),
785 .f64x2_convert_low_i32x4_u => @panic("TODO"),
786 .i8x16_relaxed_swizzle => @panic("TODO"),
787 .i32x4_relaxed_trunc_f32x4_s => @panic("TODO"),
788 .i32x4_relaxed_trunc_f32x4_u => @panic("TODO"),
789 .i32x4_relaxed_trunc_f64x2_s_zero => @panic("TODO"),
790 .i32x4_relaxed_trunc_f64x2_u_zero => @panic("TODO"),
791 .f32x4_relaxed_madd => @panic("TODO"),
792 .f32x4_relaxed_nmadd => @panic("TODO"),
793 .f64x2_relaxed_madd => @panic("TODO"),
794 .f64x2_relaxed_nmadd => @panic("TODO"),
795 .i8x16_relaxed_laneselect => @panic("TODO"),
796 .i16x8_relaxed_laneselect => @panic("TODO"),
797 .i32x4_relaxed_laneselect => @panic("TODO"),
798 .i64x2_relaxed_laneselect => @panic("TODO"),
799 .f32x4_relaxed_min => @panic("TODO"),
800 .f32x4_relaxed_max => @panic("TODO"),
801 .f64x2_relaxed_min => @panic("TODO"),
802 .f64x2_relaxed_max => @panic("TODO"),
803 .i16x8_relaxed_q15mulr_s => @panic("TODO"),
804 .i16x8_relaxed_dot_i8x16_i7x16_s => @panic("TODO"),
805 .i32x4_relaxed_dot_i8x16_i7x16_add_s => @panic("TODO"),
806 .f32x4_relaxed_dot_bf16x8_add_f32x4 => @panic("TODO"),
807 }
808 comptime unreachable;
809 },
810 .atomics_prefix => {
811 try code.ensureUnusedCapacity(gpa, 6 + 20);
812
813 const extra_index = datas[inst].payload;
814 const opcode = mir.extra[extra_index];
815 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));
816 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;
817 switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) {
818 .i32_atomic_load,
819 .i64_atomic_load,
820 .i32_atomic_load8_u,
821 .i32_atomic_load16_u,
822 .i64_atomic_load8_u,
823 .i64_atomic_load16_u,
824 .i64_atomic_load32_u,
825 .i32_atomic_store,
826 .i64_atomic_store,
827 .i32_atomic_store8,
828 .i32_atomic_store16,
829 .i64_atomic_store8,
830 .i64_atomic_store16,
831 .i64_atomic_store32,
832 .i32_atomic_rmw_add,
833 .i64_atomic_rmw_add,
834 .i32_atomic_rmw8_add_u,
835 .i32_atomic_rmw16_add_u,
836 .i64_atomic_rmw8_add_u,
837 .i64_atomic_rmw16_add_u,
838 .i64_atomic_rmw32_add_u,
839 .i32_atomic_rmw_sub,
840 .i64_atomic_rmw_sub,
841 .i32_atomic_rmw8_sub_u,
842 .i32_atomic_rmw16_sub_u,
843 .i64_atomic_rmw8_sub_u,
844 .i64_atomic_rmw16_sub_u,
845 .i64_atomic_rmw32_sub_u,
846 .i32_atomic_rmw_and,
847 .i64_atomic_rmw_and,
848 .i32_atomic_rmw8_and_u,
849 .i32_atomic_rmw16_and_u,
850 .i64_atomic_rmw8_and_u,
851 .i64_atomic_rmw16_and_u,
852 .i64_atomic_rmw32_and_u,
853 .i32_atomic_rmw_or,
854 .i64_atomic_rmw_or,
855 .i32_atomic_rmw8_or_u,
856 .i32_atomic_rmw16_or_u,
857 .i64_atomic_rmw8_or_u,
858 .i64_atomic_rmw16_or_u,
859 .i64_atomic_rmw32_or_u,
860 .i32_atomic_rmw_xor,
861 .i64_atomic_rmw_xor,
862 .i32_atomic_rmw8_xor_u,
863 .i32_atomic_rmw16_xor_u,
864 .i64_atomic_rmw8_xor_u,
865 .i64_atomic_rmw16_xor_u,
866 .i64_atomic_rmw32_xor_u,
867 .i32_atomic_rmw_xchg,
868 .i64_atomic_rmw_xchg,
869 .i32_atomic_rmw8_xchg_u,
870 .i32_atomic_rmw16_xchg_u,
871 .i64_atomic_rmw8_xchg_u,
872 .i64_atomic_rmw16_xchg_u,
873 .i64_atomic_rmw32_xchg_u,
874
875 .i32_atomic_rmw_cmpxchg,
876 .i64_atomic_rmw_cmpxchg,
877 .i32_atomic_rmw8_cmpxchg_u,
878 .i32_atomic_rmw16_cmpxchg_u,
879 .i64_atomic_rmw8_cmpxchg_u,
880 .i64_atomic_rmw16_cmpxchg_u,
881 .i64_atomic_rmw32_cmpxchg_u,
882 => {
883 const mem_arg = mir.extraData(Mir.MemArg, extra_index + 1).data;
884 encodeMemArg(code, mem_arg);
885 inst += 1;
886 continue :loop tags[inst];
887 },
888 .atomic_fence => {
889 // Hard-codes memory index 0 since multi-memory proposal is
890 // not yet accepted nor implemented.
891 const memory_index: u32 = 0;
892 leb.writeUleb128(code.fixedWriter(), memory_index) catch unreachable;
893 inst += 1;
894 continue :loop tags[inst];
895 },
896 .memory_atomic_notify => @panic("TODO"),
897 .memory_atomic_wait32 => @panic("TODO"),
898 .memory_atomic_wait64 => @panic("TODO"),
899 }
900 comptime unreachable;
627 },901 },
628 else => |tag| return emit.fail("TODO: Implement atomic instruction: {s}", .{@tagName(tag)}),
629 }902 }
903 comptime unreachable;
630}904}
631905
632fn emitMemFill(emit: *Emit) !void {906/// Asserts 20 unused capacity.
633 try emit.code.append(0xFC);907fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {
634 try emit.code.append(0x0B);908 assert(code.unusedCapacitySlice().len >= 20);
635 // When multi-memory proposal reaches phase 4, we909 // Wasm encodes alignment as power of 2, rather than natural alignment.
636 // can emit a different memory index here.910 const encoded_alignment = @ctz(mem_arg.alignment);
637 // For now we will always emit index 0.911 leb.writeUleb128(code.fixedWriter(), encoded_alignment) catch unreachable;
638 try leb128.writeUleb128(emit.code.writer(), @as(u32, 0));912 leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable;
639}
640
641fn emitDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
642 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
643 const dbg_line = emit.mir.extraData(Mir.DbgLineColumn, extra_index).data;
644 try emit.dbgAdvancePCAndLine(dbg_line.line, dbg_line.column);
645}913}
646914
647fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {915fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffObj, is_wasm32: bool) !void {
648 if (emit.dbg_output != .dwarf) return;916 const comp = wasm.base.comp;
917 const gpa = comp.gpa;
918 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
649919
650 const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line));920 try code.ensureUnusedCapacity(gpa, 11);
651 const delta_pc = emit.offset() - emit.prev_di_offset;921 code.appendAssumeCapacity(@intFromEnum(opcode));
652 // TODO: This must emit a relocation to calculate the offset relative
653 // to the code section start.
654 try emit.dbg_output.dwarf.advancePCAndLine(delta_line, delta_pc);
655922
656 emit.prev_di_line = line;923 try wasm.out_relocs.append(gpa, .{
657 emit.prev_di_column = column;924 .offset = @intCast(code.items.len),
658 emit.prev_di_offset = emit.offset();925 .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(data.uav_obj.key(wasm).*) },
926 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
927 .addend = data.offset,
928 });
929 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
659}930}
660931
661fn emitDbgPrologueEnd(emit: *Emit) !void {932fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffExe, is_wasm32: bool) !void {
662 if (emit.dbg_output != .dwarf) return;933 const comp = wasm.base.comp;
934 const gpa = comp.gpa;
935 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
936
937 try code.ensureUnusedCapacity(gpa, 11);
938 code.appendAssumeCapacity(@intFromEnum(opcode));
663939
664 try emit.dbg_output.dwarf.setPrologueEnd();940 const addr = wasm.uavAddr(data.uav_exe);
665 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);941 leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable;
666}942}
667943
668fn emitDbgEpilogueBegin(emit: *Emit) !void {944fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff, is_wasm32: bool) !void {
669 if (emit.dbg_output != .dwarf) return;945 const comp = wasm.base.comp;
946 const zcu = comp.zcu.?;
947 const ip = &zcu.intern_pool;
948 const gpa = comp.gpa;
949 const is_obj = comp.config.output_mode == .Obj;
950 const nav_ty = ip.getNav(data.nav_index).typeOf(ip);
951 assert(!ip.isFunctionType(nav_ty));
952
953 try code.ensureUnusedCapacity(gpa, 11);
954
955 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
956 code.appendAssumeCapacity(@intFromEnum(opcode));
957 if (is_obj) {
958 try wasm.out_relocs.append(gpa, .{
959 .offset = @intCast(code.items.len),
960 .pointee = .{ .symbol_index = try wasm.navSymbolIndex(data.nav_index) },
961 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
962 .addend = data.offset,
963 });
964 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
965 } else {
966 const addr = wasm.navAddr(data.nav_index);
967 leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable;
968 }
969}
670970
671 try emit.dbg_output.dwarf.setEpilogueBegin();971fn appendOutputFunctionIndex(code: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) void {
672 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);972 leb.writeUleb128(code.fixedWriter(), @intFromEnum(i)) catch unreachable;
673}973}
src/arch/wasm/Mir.zig+338-102
...@@ -7,11 +7,15 @@...@@ -7,11 +7,15 @@
7//! and known jump labels for blocks.7//! and known jump labels for blocks.
88
9const Mir = @This();9const Mir = @This();
10const InternPool = @import("../../InternPool.zig");
11const Wasm = @import("../../link/Wasm.zig");
1012
13const builtin = @import("builtin");
11const std = @import("std");14const std = @import("std");
15const assert = std.debug.assert;
1216
13/// A struct of array that represents each individual wasm17instruction_tags: []const Inst.Tag,
14instructions: std.MultiArrayList(Inst).Slice,18instruction_datas: []const Inst.Data,
15/// A slice of indexes where the meaning of the data is determined by the19/// A slice of indexes where the meaning of the data is determined by the
16/// `Inst.Tag` value.20/// `Inst.Tag` value.
17extra: []const u32,21extra: []const u32,
...@@ -26,16 +30,14 @@ pub const Inst = struct {...@@ -26,16 +30,14 @@ pub const Inst = struct {
26 /// The position of a given MIR isntruction with the instruction list.30 /// The position of a given MIR isntruction with the instruction list.
27 pub const Index = u32;31 pub const Index = u32;
2832
29 /// Contains all possible wasm opcodes the Zig compiler may emit33 /// Some tags match wasm opcode values to facilitate trivial lowering.
30 /// Rather than re-using std.wasm.Opcode, we only declare the opcodes
31 /// we need, and also use this possibility to document how to access
32 /// their payload.
33 ///
34 /// Note: Uses its actual opcode value representation to easily convert
35 /// to and from its binary representation.
36 pub const Tag = enum(u8) {34 pub const Tag = enum(u8) {
37 /// Uses `nop`35 /// Uses `tag`.
38 @"unreachable" = 0x00,36 @"unreachable" = 0x00,
37 /// Emits epilogue begin debug information. Marks the end of the function.
38 ///
39 /// Uses `tag` (no additional data).
40 dbg_epilogue_begin,
39 /// Creates a new block that can be jump from.41 /// Creates a new block that can be jump from.
40 ///42 ///
41 /// Type of the block is given in data `block_type`43 /// Type of the block is given in data `block_type`
...@@ -44,56 +46,92 @@ pub const Inst = struct {...@@ -44,56 +46,92 @@ pub const Inst = struct {
44 ///46 ///
45 /// Type of the loop is given in data `block_type`47 /// Type of the loop is given in data `block_type`
46 loop = 0x03,48 loop = 0x03,
49 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
50 /// memory address of an unnamed constant. When emitting an object
51 /// file, this adds a relocation.
52 ///
53 /// This may not refer to a function.
54 ///
55 /// Uses `ip_index`.
56 uav_ref,
57 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
58 /// memory address of an unnamed constant, offset by an integer value.
59 /// When emitting an object file, this adds a relocation.
60 ///
61 /// This may not refer to a function.
62 ///
63 /// Uses `payload` pointing to a `UavRefOff`.
64 uav_ref_off,
65 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
66 /// memory address of a named constant.
67 ///
68 /// May not refer to a function.
69 ///
70 /// Uses `nav_index`.
71 nav_ref,
72 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
73 /// memory address of named constant, offset by an integer value.
74 /// When emitting an object file, this adds a relocation.
75 ///
76 /// May not refer to a function.
77 ///
78 /// Uses `payload` pointing to a `NavRefOff`.
79 nav_ref_off,
80 /// Lowers to an i32_const which is the index of the function in the
81 /// table section.
82 ///
83 /// Uses `indirect_function_table_index`.
84 func_ref,
47 /// Inserts debug information about the current line and column85 /// Inserts debug information about the current line and column
48 /// of the source code86 /// of the source code
49 ///87 ///
50 /// Uses `payload` of which the payload type is `DbgLineColumn`88 /// Uses `payload` of which the payload type is `DbgLineColumn`
51 dbg_line = 0x06,89 dbg_line,
52 /// Emits epilogue begin debug information90 /// Lowers to an i32_const containing the number of unique Zig error
53 ///91 /// names.
54 /// Uses `nop`92 /// Uses `tag`.
55 dbg_epilogue_begin = 0x07,93 errors_len,
56 /// Emits prologue end debug information
57 ///
58 /// Uses `nop`
59 dbg_prologue_end = 0x08,
60 /// Represents the end of a function body or an initialization expression94 /// Represents the end of a function body or an initialization expression
61 ///95 ///
62 /// Payload is `nop`96 /// Uses `tag` (no additional data).
63 end = 0x0B,97 end = 0x0B,
64 /// Breaks from the current block to a label98 /// Breaks from the current block to a label
65 ///99 ///
66 /// Data is `label` where index represents the label to jump to100 /// Uses `label` where index represents the label to jump to
67 br = 0x0C,101 br = 0x0C,
68 /// Breaks from the current block if the stack value is non-zero102 /// Breaks from the current block if the stack value is non-zero
69 ///103 ///
70 /// Data is `label` where index represents the label to jump to104 /// Uses `label` where index represents the label to jump to
71 br_if = 0x0D,105 br_if = 0x0D,
72 /// Jump table that takes the stack value as an index where each value106 /// Jump table that takes the stack value as an index where each value
73 /// represents the label to jump to.107 /// represents the label to jump to.
74 ///108 ///
75 /// Data is extra of which the Payload's type is `JumpTable`109 /// Data is extra of which the Payload's type is `JumpTable`
76 br_table = 0x0E,110 br_table,
77 /// Returns from the function111 /// Returns from the function
78 ///112 ///
79 /// Uses `nop`113 /// Uses `tag`.
80 @"return" = 0x0F,114 @"return" = 0x0F,
81 /// Calls a function by its index115 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) containing
82 ///116 /// the base address of the table of error code names, with each
83 /// Uses `label`117 /// element being a null-terminated slice.
84 call = 0x10,118 ///
119 /// Uses `tag`.
120 error_name_table_ref,
121 /// Calls a function using `nav_index`.
122 call_nav,
85 /// Calls a function pointer by its function signature123 /// Calls a function pointer by its function signature
86 /// and index into the function table.124 /// and index into the function table.
87 ///125 ///
88 /// Uses `label`126 /// Uses `func_ty`
89 call_indirect = 0x11,127 call_indirect,
90 /// Contains a symbol to a function pointer128 /// Calls a function by its index.
91 /// uses `label`129 ///
92 ///130 /// The function is the auto-generated tag name function for the type
93 /// Note: This uses `0x16` as value which is reserved by the WebAssembly131 /// provided in `ip_index`.
94 /// specification but unused, meaning we must update this if the specification were to132 call_tag_name,
95 /// use this value.133 /// Lowers to a `call` instruction, using `intrinsic`.
96 function_index = 0x16,134 call_intrinsic,
97 /// Pops three values from the stack and pushes135 /// Pops three values from the stack and pushes
98 /// the first or second value dependent on the third value.136 /// the first or second value dependent on the third value.
99 /// Uses `tag`137 /// Uses `tag`
...@@ -112,15 +150,11 @@ pub const Inst = struct {...@@ -112,15 +150,11 @@ pub const Inst = struct {
112 ///150 ///
113 /// Uses `label`151 /// Uses `label`
114 local_tee = 0x22,152 local_tee = 0x22,
115 /// Loads a (mutable) global at given index onto the stack153 /// Pops a value from the stack and sets the stack pointer global.
116 ///154 /// The value must be the same type as the stack pointer global.
117 /// Uses `label`
118 global_get = 0x23,
119 /// Pops a value from the stack and sets the global at given index.
120 /// Note: Both types must be equal and global must be marked mutable.
121 ///155 ///
122 /// Uses `label`.156 /// Uses `tag` (no additional data).
123 global_set = 0x24,157 global_set_sp,
124 /// Loads a 32-bit integer from memory (data section) onto the stack158 /// Loads a 32-bit integer from memory (data section) onto the stack
125 /// Pops the value from the stack which represents the offset into memory.159 /// Pops the value from the stack which represents the offset into memory.
126 ///160 ///
...@@ -256,19 +290,19 @@ pub const Inst = struct {...@@ -256,19 +290,19 @@ pub const Inst = struct {
256 /// Loads a 32-bit signed immediate value onto the stack290 /// Loads a 32-bit signed immediate value onto the stack
257 ///291 ///
258 /// Uses `imm32`292 /// Uses `imm32`
259 i32_const = 0x41,293 i32_const,
260 /// Loads a i64-bit signed immediate value onto the stack294 /// Loads a i64-bit signed immediate value onto the stack
261 ///295 ///
262 /// uses `payload` of type `Imm64`296 /// uses `payload` of type `Imm64`
263 i64_const = 0x42,297 i64_const,
264 /// Loads a 32-bit float value onto the stack.298 /// Loads a 32-bit float value onto the stack.
265 ///299 ///
266 /// Uses `float32`300 /// Uses `float32`
267 f32_const = 0x43,301 f32_const,
268 /// Loads a 64-bit float value onto the stack.302 /// Loads a 64-bit float value onto the stack.
269 ///303 ///
270 /// Uses `payload` of type `Float64`304 /// Uses `payload` of type `Float64`
271 f64_const = 0x44,305 f64_const,
272 /// Uses `tag`306 /// Uses `tag`
273 i32_eqz = 0x45,307 i32_eqz = 0x45,
274 /// Uses `tag`308 /// Uses `tag`
...@@ -522,25 +556,19 @@ pub const Inst = struct {...@@ -522,25 +556,19 @@ pub const Inst = struct {
522 ///556 ///
523 /// The `data` field depends on the extension instruction and557 /// The `data` field depends on the extension instruction and
524 /// may contain additional data.558 /// may contain additional data.
525 misc_prefix = 0xFC,559 misc_prefix,
526 /// The instruction consists of a simd opcode.560 /// The instruction consists of a simd opcode.
527 /// The actual simd-opcode is found at payload's index.561 /// The actual simd-opcode is found at payload's index.
528 ///562 ///
529 /// The `data` field depends on the simd instruction and563 /// The `data` field depends on the simd instruction and
530 /// may contain additional data.564 /// may contain additional data.
531 simd_prefix = 0xFD,565 simd_prefix,
532 /// The instruction consists of an atomics opcode.566 /// The instruction consists of an atomics opcode.
533 /// The actual atomics-opcode is found at payload's index.567 /// The actual atomics-opcode is found at payload's index.
534 ///568 ///
535 /// The `data` field depends on the atomics instruction and569 /// The `data` field depends on the atomics instruction and
536 /// may contain additional data.570 /// may contain additional data.
537 atomics_prefix = 0xFE,571 atomics_prefix = 0xFE,
538 /// Contains a symbol to a memory address
539 /// Uses `label`
540 ///
541 /// Note: This uses `0xFF` as value as it is unused and not reserved
542 /// by the wasm specification, making it safe to use.
543 memory_address = 0xFF,
544572
545 /// From a given wasm opcode, returns a MIR tag.573 /// From a given wasm opcode, returns a MIR tag.
546 pub fn fromOpcode(opcode: std.wasm.Opcode) Tag {574 pub fn fromOpcode(opcode: std.wasm.Opcode) Tag {
...@@ -560,26 +588,41 @@ pub const Inst = struct {...@@ -560,26 +588,41 @@ pub const Inst = struct {
560 /// Uses no additional data588 /// Uses no additional data
561 tag: void,589 tag: void,
562 /// Contains the result type of a block590 /// Contains the result type of a block
563 ///591 block_type: std.wasm.BlockType,
564 /// Used by `block` and `loop`592 /// Label: Each structured control instruction introduces an implicit label.
565 block_type: u8,593 /// Labels are targets for branch instructions that reference them with
566 /// Contains an u32 index into a wasm section entry, such as a local.594 /// label indices. Unlike with other index spaces, indexing of labels
567 /// Note: This is not an index to another instruction.595 /// is relative by nesting depth, that is, label 0 refers to the
568 ///596 /// innermost structured control instruction enclosing the referring
569 /// Used by e.g. `local_get`, `local_set`, etc.597 /// branch instruction, while increasing indices refer to those farther
598 /// out. Consequently, labels can only be referenced from within the
599 /// associated structured control instruction.
570 label: u32,600 label: u32,
601 /// Local: The index space for locals is only accessible inside a function and
602 /// includes the parameters of that function, which precede the local
603 /// variables.
604 local: u32,
571 /// A 32-bit immediate value.605 /// A 32-bit immediate value.
572 ///
573 /// Used by `i32_const`
574 imm32: i32,606 imm32: i32,
575 /// A 32-bit float value607 /// A 32-bit float value
576 ///
577 /// Used by `f32_float`
578 float32: f32,608 float32: f32,
579 /// Index into `extra`. Meaning of what can be found there is context-dependent.609 /// Index into `extra`. Meaning of what can be found there is context-dependent.
580 ///
581 /// Used by e.g. `br_table`
582 payload: u32,610 payload: u32,
611
612 ip_index: InternPool.Index,
613 nav_index: InternPool.Nav.Index,
614 func_ty: Wasm.FunctionType.Index,
615 intrinsic: Intrinsic,
616 uav_obj: Wasm.UavsObjIndex,
617 uav_exe: Wasm.UavsExeIndex,
618 indirect_function_table_index: Wasm.ZcuIndirectFunctionSetIndex,
619
620 comptime {
621 switch (builtin.mode) {
622 .Debug, .ReleaseSafe => {},
623 .ReleaseFast, .ReleaseSmall => assert(@sizeOf(Data) == 4),
624 }
625 }
583 };626 };
584};627};
585628
...@@ -596,6 +639,11 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data...@@ -596,6 +639,11 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data
596 inline for (fields) |field| {639 inline for (fields) |field| {
597 @field(result, field.name) = switch (field.type) {640 @field(result, field.name) = switch (field.type) {
598 u32 => self.extra[i],641 u32 => self.extra[i],
642 i32 => @bitCast(self.extra[i]),
643 Wasm.UavsObjIndex,
644 Wasm.UavsExeIndex,
645 InternPool.Nav.Index,
646 => @enumFromInt(self.extra[i]),
599 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),647 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
600 };648 };
601 i += 1;649 i += 1;
...@@ -609,28 +657,19 @@ pub const JumpTable = struct {...@@ -609,28 +657,19 @@ pub const JumpTable = struct {
609 length: u32,657 length: u32,
610};658};
611659
612/// Stores an unsigned 64bit integer
613/// into a 32bit most significant bits field
614/// and a 32bit least significant bits field.
615///
616/// This uses an unsigned integer rather than a signed integer
617/// as we can easily store those into `extra`
618pub const Imm64 = struct {660pub const Imm64 = struct {
619 msb: u32,661 msb: u32,
620 lsb: u32,662 lsb: u32,
621663
622 pub fn fromU64(imm: u64) Imm64 {664 pub fn init(full: u64) Imm64 {
623 return .{665 return .{
624 .msb = @as(u32, @truncate(imm >> 32)),666 .msb = @truncate(full >> 32),
625 .lsb = @as(u32, @truncate(imm)),667 .lsb = @truncate(full),
626 };668 };
627 }669 }
628670
629 pub fn toU64(self: Imm64) u64 {671 pub fn toInt(i: Imm64) u64 {
630 var result: u64 = 0;672 return (@as(u64, i.msb) << 32) | @as(u64, i.lsb);
631 result |= @as(u64, self.msb) << 32;
632 result |= @as(u64, self.lsb);
633 return result;
634 }673 }
635};674};
636675
...@@ -638,23 +677,16 @@ pub const Float64 = struct {...@@ -638,23 +677,16 @@ pub const Float64 = struct {
638 msb: u32,677 msb: u32,
639 lsb: u32,678 lsb: u32,
640679
641 pub fn fromFloat64(float: f64) Float64 {680 pub fn init(f: f64) Float64 {
642 const tmp = @as(u64, @bitCast(float));681 const int: u64 = @bitCast(f);
643 return .{682 return .{
644 .msb = @as(u32, @truncate(tmp >> 32)),683 .msb = @truncate(int >> 32),
645 .lsb = @as(u32, @truncate(tmp)),684 .lsb = @truncate(int),
646 };685 };
647 }686 }
648687
649 pub fn toF64(self: Float64) f64 {688 pub fn toInt(f: Float64) u64 {
650 @as(f64, @bitCast(self.toU64()));689 return (@as(u64, f.msb) << 32) | @as(u64, f.lsb);
651 }
652
653 pub fn toU64(self: Float64) u64 {
654 var result: u64 = 0;
655 result |= @as(u64, self.msb) << 32;
656 result |= @as(u64, self.lsb);
657 return result;
658 }690 }
659};691};
660692
...@@ -663,11 +695,19 @@ pub const MemArg = struct {...@@ -663,11 +695,19 @@ pub const MemArg = struct {
663 alignment: u32,695 alignment: u32,
664};696};
665697
666/// Represents a memory address, which holds both the pointer698pub const UavRefOffObj = struct {
667/// or the parent pointer and the offset to it.699 uav_obj: Wasm.UavsObjIndex,
668pub const Memory = struct {700 offset: i32,
669 pointer: u32,701};
670 offset: u32,702
703pub const UavRefOffExe = struct {
704 uav_exe: Wasm.UavsExeIndex,
705 offset: i32,
706};
707
708pub const NavRefOff = struct {
709 nav_index: InternPool.Nav.Index,
710 offset: i32,
671};711};
672712
673/// Maps a source line with wasm bytecode713/// Maps a source line with wasm bytecode
...@@ -675,3 +715,199 @@ pub const DbgLineColumn = struct {...@@ -675,3 +715,199 @@ pub const DbgLineColumn = struct {
675 line: u32,715 line: u32,
676 column: u32,716 column: u32,
677};717};
718
719/// Tag names exactly match the corresponding symbol name.
720pub const Intrinsic = enum(u32) {
721 __addhf3,
722 __addtf3,
723 __addxf3,
724 __ashlti3,
725 __ashrti3,
726 __bitreversedi2,
727 __bitreversesi2,
728 __bswapdi2,
729 __bswapsi2,
730 __ceilh,
731 __ceilx,
732 __cosh,
733 __cosx,
734 __divhf3,
735 __divtf3,
736 __divti3,
737 __divxf3,
738 __eqtf2,
739 __eqxf2,
740 __exp2h,
741 __exp2x,
742 __exph,
743 __expx,
744 __extenddftf2,
745 __extenddfxf2,
746 __extendhfsf2,
747 __extendhftf2,
748 __extendhfxf2,
749 __extendsftf2,
750 __extendsfxf2,
751 __extendxftf2,
752 __fabsh,
753 __fabsx,
754 __fixdfdi,
755 __fixdfsi,
756 __fixdfti,
757 __fixhfdi,
758 __fixhfsi,
759 __fixhfti,
760 __fixsfdi,
761 __fixsfsi,
762 __fixsfti,
763 __fixtfdi,
764 __fixtfsi,
765 __fixtfti,
766 __fixunsdfdi,
767 __fixunsdfsi,
768 __fixunsdfti,
769 __fixunshfdi,
770 __fixunshfsi,
771 __fixunshfti,
772 __fixunssfdi,
773 __fixunssfsi,
774 __fixunssfti,
775 __fixunstfdi,
776 __fixunstfsi,
777 __fixunstfti,
778 __fixunsxfdi,
779 __fixunsxfsi,
780 __fixunsxfti,
781 __fixxfdi,
782 __fixxfsi,
783 __fixxfti,
784 __floatdidf,
785 __floatdihf,
786 __floatdisf,
787 __floatditf,
788 __floatdixf,
789 __floatsidf,
790 __floatsihf,
791 __floatsisf,
792 __floatsitf,
793 __floatsixf,
794 __floattidf,
795 __floattihf,
796 __floattisf,
797 __floattitf,
798 __floattixf,
799 __floatundidf,
800 __floatundihf,
801 __floatundisf,
802 __floatunditf,
803 __floatundixf,
804 __floatunsidf,
805 __floatunsihf,
806 __floatunsisf,
807 __floatunsitf,
808 __floatunsixf,
809 __floatuntidf,
810 __floatuntihf,
811 __floatuntisf,
812 __floatuntitf,
813 __floatuntixf,
814 __floorh,
815 __floorx,
816 __fmah,
817 __fmax,
818 __fmaxh,
819 __fmaxx,
820 __fminh,
821 __fminx,
822 __fmodh,
823 __fmodx,
824 __getf2,
825 __gexf2,
826 __gttf2,
827 __gtxf2,
828 __letf2,
829 __lexf2,
830 __log10h,
831 __log10x,
832 __log2h,
833 __log2x,
834 __logh,
835 __logx,
836 __lshrti3,
837 __lttf2,
838 __ltxf2,
839 __modti3,
840 __mulhf3,
841 __mulodi4,
842 __muloti4,
843 __multf3,
844 __multi3,
845 __mulxf3,
846 __netf2,
847 __nexf2,
848 __roundh,
849 __roundx,
850 __sinh,
851 __sinx,
852 __sqrth,
853 __sqrtx,
854 __subhf3,
855 __subtf3,
856 __subxf3,
857 __tanh,
858 __tanx,
859 __trunch,
860 __truncsfhf2,
861 __trunctfdf2,
862 __trunctfhf2,
863 __trunctfsf2,
864 __trunctfxf2,
865 __truncx,
866 __truncxfdf2,
867 __truncxfhf2,
868 __truncxfsf2,
869 __udivti3,
870 __umodti3,
871 ceilq,
872 cos,
873 cosf,
874 cosq,
875 exp,
876 exp2,
877 exp2f,
878 exp2q,
879 expf,
880 expq,
881 fabsq,
882 floorq,
883 fma,
884 fmaf,
885 fmaq,
886 fmax,
887 fmaxf,
888 fmaxq,
889 fmin,
890 fminf,
891 fminq,
892 fmod,
893 fmodf,
894 fmodq,
895 log,
896 log10,
897 log10f,
898 log10q,
899 log2,
900 log2f,
901 log2q,
902 logf,
903 logq,
904 roundq,
905 sin,
906 sinf,
907 sinq,
908 sqrtq,
909 tan,
910 tanf,
911 tanq,
912 truncq,
913};
src/arch/wasm/abi.zig+1-1
...@@ -22,7 +22,7 @@ const direct: [2]Class = .{ .direct, .none };...@@ -22,7 +22,7 @@ const direct: [2]Class = .{ .direct, .none };
22/// Classifies a given Zig type to determine how they must be passed22/// Classifies a given Zig type to determine how they must be passed
23/// or returned as value within a wasm function.23/// or returned as value within a wasm function.
24/// When all elements result in `.none`, no value must be passed in or returned.24/// When all elements result in `.none`, no value must be passed in or returned.
25pub fn classifyType(ty: Type, zcu: *Zcu) [2]Class {25pub fn classifyType(ty: Type, zcu: *const Zcu) [2]Class {
26 const ip = &zcu.intern_pool;26 const ip = &zcu.intern_pool;
27 const target = zcu.getTarget();27 const target = zcu.getTarget();
28 if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return none;28 if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return none;
src/arch/x86_64/CodeGen.zig+36-85
...@@ -19,7 +19,6 @@ const Allocator = mem.Allocator;...@@ -19,7 +19,6 @@ const Allocator = mem.Allocator;
19const CodeGenError = codegen.CodeGenError;19const CodeGenError = codegen.CodeGenError;
20const Compilation = @import("../../Compilation.zig");20const Compilation = @import("../../Compilation.zig");
21const ErrorMsg = Zcu.ErrorMsg;21const ErrorMsg = Zcu.ErrorMsg;
22const Result = codegen.Result;
23const Emit = @import("Emit.zig");22const Emit = @import("Emit.zig");
24const Liveness = @import("../../Liveness.zig");23const Liveness = @import("../../Liveness.zig");
25const Lower = @import("Lower.zig");24const Lower = @import("Lower.zig");
...@@ -59,7 +58,6 @@ target: *const std.Target,...@@ -59,7 +58,6 @@ target: *const std.Target,
59owner: Owner,58owner: Owner,
60inline_func: InternPool.Index,59inline_func: InternPool.Index,
61mod: *Package.Module,60mod: *Package.Module,
62err_msg: ?*ErrorMsg,
63arg_index: u32,61arg_index: u32,
64args: []MCValue,62args: []MCValue,
65va_info: union {63va_info: union {
...@@ -819,9 +817,9 @@ pub fn generate(...@@ -819,9 +817,9 @@ pub fn generate(
819 func_index: InternPool.Index,817 func_index: InternPool.Index,
820 air: Air,818 air: Air,
821 liveness: Liveness,819 liveness: Liveness,
822 code: *std.ArrayList(u8),820 code: *std.ArrayListUnmanaged(u8),
823 debug_output: link.File.DebugInfoOutput,821 debug_output: link.File.DebugInfoOutput,
824) CodeGenError!Result {822) CodeGenError!void {
825 const zcu = pt.zcu;823 const zcu = pt.zcu;
826 const comp = zcu.comp;824 const comp = zcu.comp;
827 const gpa = zcu.gpa;825 const gpa = zcu.gpa;
...@@ -841,7 +839,6 @@ pub fn generate(...@@ -841,7 +839,6 @@ pub fn generate(
841 .debug_output = debug_output,839 .debug_output = debug_output,
842 .owner = .{ .nav_index = func.owner_nav },840 .owner = .{ .nav_index = func.owner_nav },
843 .inline_func = func_index,841 .inline_func = func_index,
844 .err_msg = null,
845 .arg_index = undefined,842 .arg_index = undefined,
846 .args = undefined, // populated after `resolveCallingConventionValues`843 .args = undefined, // populated after `resolveCallingConventionValues`
847 .va_info = undefined, // populated after `resolveCallingConventionValues`844 .va_info = undefined, // populated after `resolveCallingConventionValues`
...@@ -881,15 +878,7 @@ pub fn generate(...@@ -881,15 +878,7 @@ pub fn generate(
881 const fn_info = zcu.typeToFunc(fn_type).?;878 const fn_info = zcu.typeToFunc(fn_type).?;
882 const cc = abi.resolveCallingConvention(fn_info.cc, function.target.*);879 const cc = abi.resolveCallingConvention(fn_info.cc, function.target.*);
883 var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) {880 var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) {
884 error.CodegenFail => return Result{ .fail = function.err_msg.? },881 error.CodegenFail => return error.CodegenFail,
885 error.OutOfRegisters => return Result{
886 .fail = try ErrorMsg.create(
887 gpa,
888 src_loc,
889 "CodeGen ran out of registers. This is a bug in the Zig compiler.",
890 .{},
891 ),
892 },
893 else => |e| return e,882 else => |e| return e,
894 };883 };
895 defer call_info.deinit(&function);884 defer call_info.deinit(&function);
...@@ -926,10 +915,8 @@ pub fn generate(...@@ -926,10 +915,8 @@ pub fn generate(
926 };915 };
927916
928 function.gen() catch |err| switch (err) {917 function.gen() catch |err| switch (err) {
929 error.CodegenFail => return Result{ .fail = function.err_msg.? },918 error.CodegenFail => return error.CodegenFail,
930 error.OutOfRegisters => return Result{919 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
931 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
932 },
933 else => |e| return e,920 else => |e| return e,
934 };921 };
935922
...@@ -953,10 +940,7 @@ pub fn generate(...@@ -953,10 +940,7 @@ pub fn generate(
953 .pic = mod.pic,940 .pic = mod.pic,
954 },941 },
955 .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) {942 .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) {
956 error.CodegenFail => return Result{ .fail = function.err_msg.? },943 error.CodegenFail => return error.CodegenFail,
957 error.OutOfRegisters => return Result{
958 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
959 },
960 else => |e| return e,944 else => |e| return e,
961 },945 },
962 .debug_output = debug_output,946 .debug_output = debug_output,
...@@ -974,29 +958,11 @@ pub fn generate(...@@ -974,29 +958,11 @@ pub fn generate(
974 };958 };
975 defer emit.deinit();959 defer emit.deinit();
976 emit.emitMir() catch |err| switch (err) {960 emit.emitMir() catch |err| switch (err) {
977 error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? },961 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?),
978 error.InvalidInstruction, error.CannotEncode => |e| {
979 const msg = switch (e) {
980 error.InvalidInstruction => "CodeGen failed to find a viable instruction.",
981 error.CannotEncode => "CodeGen failed to encode the instruction.",
982 };
983 return Result{
984 .fail = try ErrorMsg.create(
985 gpa,
986 src_loc,
987 "{s} This is a bug in the Zig compiler.",
988 .{msg},
989 ),
990 };
991 },
992 else => |e| return e,
993 };
994962
995 if (function.err_msg) |em| {963 error.InvalidInstruction, error.CannotEncode => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}),
996 return Result{ .fail = em };964 else => |e| return function.fail("emit MIR failed: {s}", .{@errorName(e)}),
997 } else {965 };
998 return Result.ok;
999 }
1000}966}
1001967
1002pub fn generateLazy(968pub fn generateLazy(
...@@ -1004,9 +970,9 @@ pub fn generateLazy(...@@ -1004,9 +970,9 @@ pub fn generateLazy(
1004 pt: Zcu.PerThread,970 pt: Zcu.PerThread,
1005 src_loc: Zcu.LazySrcLoc,971 src_loc: Zcu.LazySrcLoc,
1006 lazy_sym: link.File.LazySymbol,972 lazy_sym: link.File.LazySymbol,
1007 code: *std.ArrayList(u8),973 code: *std.ArrayListUnmanaged(u8),
1008 debug_output: link.File.DebugInfoOutput,974 debug_output: link.File.DebugInfoOutput,
1009) CodeGenError!Result {975) CodeGenError!void {
1010 const comp = bin_file.comp;976 const comp = bin_file.comp;
1011 const gpa = comp.gpa;977 const gpa = comp.gpa;
1012 // This function is for generating global code, so we use the root module.978 // This function is for generating global code, so we use the root module.
...@@ -1022,7 +988,6 @@ pub fn generateLazy(...@@ -1022,7 +988,6 @@ pub fn generateLazy(
1022 .debug_output = debug_output,988 .debug_output = debug_output,
1023 .owner = .{ .lazy_sym = lazy_sym },989 .owner = .{ .lazy_sym = lazy_sym },
1024 .inline_func = undefined,990 .inline_func = undefined,
1025 .err_msg = null,
1026 .arg_index = undefined,991 .arg_index = undefined,
1027 .args = undefined,992 .args = undefined,
1028 .va_info = undefined,993 .va_info = undefined,
...@@ -1038,10 +1003,8 @@ pub fn generateLazy(...@@ -1038,10 +1003,8 @@ pub fn generateLazy(
1038 }1003 }
10391004
1040 function.genLazy(lazy_sym) catch |err| switch (err) {1005 function.genLazy(lazy_sym) catch |err| switch (err) {
1041 error.CodegenFail => return Result{ .fail = function.err_msg.? },1006 error.CodegenFail => return error.CodegenFail,
1042 error.OutOfRegisters => return Result{1007 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
1043 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
1044 },
1045 else => |e| return e,1008 else => |e| return e,
1046 };1009 };
10471010
...@@ -1065,10 +1028,7 @@ pub fn generateLazy(...@@ -1065,10 +1028,7 @@ pub fn generateLazy(
1065 .pic = mod.pic,1028 .pic = mod.pic,
1066 },1029 },
1067 .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) {1030 .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) {
1068 error.CodegenFail => return Result{ .fail = function.err_msg.? },1031 error.CodegenFail => return error.CodegenFail,
1069 error.OutOfRegisters => return Result{
1070 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
1071 },
1072 else => |e| return e,1032 else => |e| return e,
1073 },1033 },
1074 .debug_output = debug_output,1034 .debug_output = debug_output,
...@@ -1078,29 +1038,11 @@ pub fn generateLazy(...@@ -1078,29 +1038,11 @@ pub fn generateLazy(
1078 };1038 };
1079 defer emit.deinit();1039 defer emit.deinit();
1080 emit.emitMir() catch |err| switch (err) {1040 emit.emitMir() catch |err| switch (err) {
1081 error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? },1041 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?),
1082 error.InvalidInstruction, error.CannotEncode => |e| {1042 error.InvalidInstruction => return function.fail("failed to find a viable x86 instruction (Zig compiler bug)", .{}),
1083 const msg = switch (e) {1043 error.CannotEncode => return function.fail("failed to encode x86 instruction (Zig compiler bug)", .{}),
1084 error.InvalidInstruction => "CodeGen failed to find a viable instruction.",1044 else => |e| return function.fail("failed to emit MIR: {s}", .{@errorName(e)}),
1085 error.CannotEncode => "CodeGen failed to encode the instruction.",
1086 };
1087 return Result{
1088 .fail = try ErrorMsg.create(
1089 gpa,
1090 src_loc,
1091 "{s} This is a bug in the Zig compiler.",
1092 .{msg},
1093 ),
1094 };
1095 },
1096 else => |e| return e,
1097 };1045 };
1098
1099 if (function.err_msg) |em| {
1100 return Result{ .fail = em };
1101 } else {
1102 return Result.ok;
1103 }
1104}1046}
11051047
1106const FormatNavData = struct {1048const FormatNavData = struct {
...@@ -19276,10 +19218,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {...@@ -19276,10 +19218,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
19276 .load_got => |sym_index| .{ .lea_got = sym_index },19218 .load_got => |sym_index| .{ .lea_got = sym_index },
19277 .load_tlv => |sym_index| .{ .lea_tlv = sym_index },19219 .load_tlv => |sym_index| .{ .lea_tlv = sym_index },
19278 },19220 },
19279 .fail => |msg| {19221 .fail => |msg| return self.failMsg(msg),
19280 self.err_msg = msg;
19281 return error.CodegenFail;
19282 },
19283 };19222 };
19284}19223}
1928519224
...@@ -19592,11 +19531,23 @@ fn resolveCallingConventionValues(...@@ -19592,11 +19531,23 @@ fn resolveCallingConventionValues(
19592 return result;19531 return result;
19593}19532}
1959419533
19595fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {19534fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
19596 @branchHint(.cold);19535 @branchHint(.cold);
19597 assert(self.err_msg == null);19536 const zcu = self.pt.zcu;
19598 const gpa = self.gpa;19537 switch (self.owner) {
19599 self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args);19538 .nav_index => |i| return zcu.codegenFail(i, format, args),
19539 .lazy_sym => |s| return zcu.codegenFailType(s.ty, format, args),
19540 }
19541 return error.CodegenFail;
19542}
19543
19544fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } {
19545 @branchHint(.cold);
19546 const zcu = self.pt.zcu;
19547 switch (self.owner) {
19548 .nav_index => |i| return zcu.codegenFailMsg(i, msg),
19549 .lazy_sym => |s| return zcu.codegenFailTypeMsg(s.ty, msg),
19550 }
19600 return error.CodegenFail;19551 return error.CodegenFail;
19601}19552}
1960219553
src/arch/x86_64/Emit.zig+8-7
...@@ -4,7 +4,7 @@ air: Air,...@@ -4,7 +4,7 @@ air: Air,
4lower: Lower,4lower: Lower,
5atom_index: u32,5atom_index: u32,
6debug_output: link.File.DebugInfoOutput,6debug_output: link.File.DebugInfoOutput,
7code: *std.ArrayList(u8),7code: *std.ArrayListUnmanaged(u8),
88
9prev_di_loc: Loc,9prev_di_loc: Loc,
10/// Relative to the beginning of `code`.10/// Relative to the beginning of `code`.
...@@ -18,6 +18,7 @@ pub const Error = Lower.Error || error{...@@ -18,6 +18,7 @@ pub const Error = Lower.Error || error{
18} || link.File.UpdateDebugInfoError;18} || link.File.UpdateDebugInfoError;
1919
20pub fn emitMir(emit: *Emit) Error!void {20pub fn emitMir(emit: *Emit) Error!void {
21 const gpa = emit.lower.bin_file.comp.gpa;
21 for (0..emit.lower.mir.instructions.len) |mir_i| {22 for (0..emit.lower.mir.instructions.len) |mir_i| {
22 const mir_index: Mir.Inst.Index = @intCast(mir_i);23 const mir_index: Mir.Inst.Index = @intCast(mir_i);
23 try emit.code_offset_mapping.putNoClobber(24 try emit.code_offset_mapping.putNoClobber(
...@@ -82,7 +83,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -82,7 +83,7 @@ pub fn emitMir(emit: *Emit) Error!void {
82 }83 }
83 continue;84 continue;
84 }85 }
85 try lowered_inst.encode(emit.code.writer(), .{});86 try lowered_inst.encode(emit.code.writer(gpa), .{});
86 const end_offset: u32 = @intCast(emit.code.items.len);87 const end_offset: u32 = @intCast(emit.code.items.len);
87 while (lowered_relocs.len > 0 and88 while (lowered_relocs.len > 0 and
88 lowered_relocs[0].lowered_inst_index == lowered_index) : ({89 lowered_relocs[0].lowered_inst_index == lowered_index) : ({
...@@ -100,7 +101,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -100,7 +101,7 @@ pub fn emitMir(emit: *Emit) Error!void {
100 const zo = elf_file.zigObjectPtr().?;101 const zo = elf_file.zigObjectPtr().?;
101 const atom_ptr = zo.symbol(emit.atom_index).atom(elf_file).?;102 const atom_ptr = zo.symbol(emit.atom_index).atom(elf_file).?;
102 const r_type = @intFromEnum(std.elf.R_X86_64.PLT32);103 const r_type = @intFromEnum(std.elf.R_X86_64.PLT32);
103 try atom_ptr.addReloc(elf_file.base.comp.gpa, .{104 try atom_ptr.addReloc(gpa, .{
104 .r_offset = end_offset - 4,105 .r_offset = end_offset - 4,
105 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,106 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
106 .r_addend = lowered_relocs[0].off - 4,107 .r_addend = lowered_relocs[0].off - 4,
...@@ -147,7 +148,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -147,7 +148,7 @@ pub fn emitMir(emit: *Emit) Error!void {
147 const zo = elf_file.zigObjectPtr().?;148 const zo = elf_file.zigObjectPtr().?;
148 const atom = zo.symbol(emit.atom_index).atom(elf_file).?;149 const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
149 const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD);150 const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD);
150 try atom.addReloc(elf_file.base.comp.gpa, .{151 try atom.addReloc(gpa, .{
151 .r_offset = end_offset - 4,152 .r_offset = end_offset - 4,
152 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,153 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
153 .r_addend = lowered_relocs[0].off - 4,154 .r_addend = lowered_relocs[0].off - 4,
...@@ -158,7 +159,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -158,7 +159,7 @@ pub fn emitMir(emit: *Emit) Error!void {
158 const zo = elf_file.zigObjectPtr().?;159 const zo = elf_file.zigObjectPtr().?;
159 const atom = zo.symbol(emit.atom_index).atom(elf_file).?;160 const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
160 const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32);161 const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32);
161 try atom.addReloc(elf_file.base.comp.gpa, .{162 try atom.addReloc(gpa, .{
162 .r_offset = end_offset - 4,163 .r_offset = end_offset - 4,
163 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,164 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
164 .r_addend = lowered_relocs[0].off,165 .r_addend = lowered_relocs[0].off,
...@@ -173,7 +174,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -173,7 +174,7 @@ pub fn emitMir(emit: *Emit) Error!void {
173 @intFromEnum(std.elf.R_X86_64.GOTPCREL)174 @intFromEnum(std.elf.R_X86_64.GOTPCREL)
174 else175 else
175 @intFromEnum(std.elf.R_X86_64.PC32);176 @intFromEnum(std.elf.R_X86_64.PC32);
176 try atom.addReloc(elf_file.base.comp.gpa, .{177 try atom.addReloc(gpa, .{
177 .r_offset = end_offset - 4,178 .r_offset = end_offset - 4,
178 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,179 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
179 .r_addend = lowered_relocs[0].off - 4,180 .r_addend = lowered_relocs[0].off - 4,
...@@ -183,7 +184,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -183,7 +184,7 @@ pub fn emitMir(emit: *Emit) Error!void {
183 @intFromEnum(std.elf.R_X86_64.TPOFF32)184 @intFromEnum(std.elf.R_X86_64.TPOFF32)
184 else185 else
185 @intFromEnum(std.elf.R_X86_64.@"32");186 @intFromEnum(std.elf.R_X86_64.@"32");
186 try atom.addReloc(elf_file.base.comp.gpa, .{187 try atom.addReloc(gpa, .{
187 .r_offset = end_offset - 4,188 .r_offset = end_offset - 4,
188 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,189 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
189 .r_addend = lowered_relocs[0].off,190 .r_addend = lowered_relocs[0].off,
src/codegen.zig+199-181
...@@ -2,7 +2,6 @@ const std = @import("std");...@@ -2,7 +2,6 @@ const std = @import("std");
2const build_options = @import("build_options");2const build_options = @import("build_options");
3const builtin = @import("builtin");3const builtin = @import("builtin");
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const leb128 = std.leb;
6const link = @import("link.zig");5const link = @import("link.zig");
7const log = std.log.scoped(.codegen);6const log = std.log.scoped(.codegen);
8const mem = std.mem;7const mem = std.mem;
...@@ -24,19 +23,13 @@ const Zir = std.zig.Zir;...@@ -24,19 +23,13 @@ const Zir = std.zig.Zir;
24const Alignment = InternPool.Alignment;23const Alignment = InternPool.Alignment;
25const dev = @import("dev.zig");24const dev = @import("dev.zig");
2625
27pub const Result = union(enum) {
28 /// The `code` parameter passed to `generateSymbol` has the value ok.
29 ok,
30
31 /// There was a codegen error.
32 fail: *ErrorMsg,
33};
34
35pub const CodeGenError = error{26pub const CodeGenError = error{
36 OutOfMemory,27 OutOfMemory,
28 /// Compiler was asked to operate on a number larger than supported.
37 Overflow,29 Overflow,
30 /// Indicates the error is already stored in Zcu `failed_codegen`.
38 CodegenFail,31 CodegenFail,
39} || link.File.UpdateDebugInfoError;32};
4033
41fn devFeatureForBackend(comptime backend: std.builtin.CompilerBackend) dev.Feature {34fn devFeatureForBackend(comptime backend: std.builtin.CompilerBackend) dev.Feature {
42 comptime assert(mem.startsWith(u8, @tagName(backend), "stage2_"));35 comptime assert(mem.startsWith(u8, @tagName(backend), "stage2_"));
...@@ -49,7 +42,6 @@ fn importBackend(comptime backend: std.builtin.CompilerBackend) type {...@@ -49,7 +42,6 @@ fn importBackend(comptime backend: std.builtin.CompilerBackend) type {
49 .stage2_arm => @import("arch/arm/CodeGen.zig"),42 .stage2_arm => @import("arch/arm/CodeGen.zig"),
50 .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"),43 .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"),
51 .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"),44 .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"),
52 .stage2_wasm => @import("arch/wasm/CodeGen.zig"),
53 .stage2_x86_64 => @import("arch/x86_64/CodeGen.zig"),45 .stage2_x86_64 => @import("arch/x86_64/CodeGen.zig"),
54 else => unreachable,46 else => unreachable,
55 };47 };
...@@ -62,9 +54,9 @@ pub fn generateFunction(...@@ -62,9 +54,9 @@ pub fn generateFunction(
62 func_index: InternPool.Index,54 func_index: InternPool.Index,
63 air: Air,55 air: Air,
64 liveness: Liveness,56 liveness: Liveness,
65 code: *std.ArrayList(u8),57 code: *std.ArrayListUnmanaged(u8),
66 debug_output: link.File.DebugInfoOutput,58 debug_output: link.File.DebugInfoOutput,
67) CodeGenError!Result {59) CodeGenError!void {
68 const zcu = pt.zcu;60 const zcu = pt.zcu;
69 const func = zcu.funcInfo(func_index);61 const func = zcu.funcInfo(func_index);
70 const target = zcu.navFileScope(func.owner_nav).mod.resolved_target.result;62 const target = zcu.navFileScope(func.owner_nav).mod.resolved_target.result;
...@@ -74,7 +66,6 @@ pub fn generateFunction(...@@ -74,7 +66,6 @@ pub fn generateFunction(
74 .stage2_arm,66 .stage2_arm,
75 .stage2_riscv64,67 .stage2_riscv64,
76 .stage2_sparc64,68 .stage2_sparc64,
77 .stage2_wasm,
78 .stage2_x86_64,69 .stage2_x86_64,
79 => |backend| {70 => |backend| {
80 dev.check(devFeatureForBackend(backend));71 dev.check(devFeatureForBackend(backend));
...@@ -88,17 +79,15 @@ pub fn generateLazyFunction(...@@ -88,17 +79,15 @@ pub fn generateLazyFunction(
88 pt: Zcu.PerThread,79 pt: Zcu.PerThread,
89 src_loc: Zcu.LazySrcLoc,80 src_loc: Zcu.LazySrcLoc,
90 lazy_sym: link.File.LazySymbol,81 lazy_sym: link.File.LazySymbol,
91 code: *std.ArrayList(u8),82 code: *std.ArrayListUnmanaged(u8),
92 debug_output: link.File.DebugInfoOutput,83 debug_output: link.File.DebugInfoOutput,
93) CodeGenError!Result {84) CodeGenError!void {
94 const zcu = pt.zcu;85 const zcu = pt.zcu;
95 const file = Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(&zcu.intern_pool);86 const file = Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(&zcu.intern_pool);
96 const target = zcu.fileByIndex(file).mod.resolved_target.result;87 const target = zcu.fileByIndex(file).mod.resolved_target.result;
97 switch (target_util.zigBackend(target, false)) {88 switch (target_util.zigBackend(target, false)) {
98 else => unreachable,89 else => unreachable,
99 inline .stage2_x86_64,90 inline .stage2_x86_64, .stage2_riscv64 => |backend| {
100 .stage2_riscv64,
101 => |backend| {
102 dev.check(devFeatureForBackend(backend));91 dev.check(devFeatureForBackend(backend));
103 return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output);92 return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output);
104 },93 },
...@@ -120,20 +109,21 @@ pub fn generateLazySymbol(...@@ -120,20 +109,21 @@ pub fn generateLazySymbol(
120 lazy_sym: link.File.LazySymbol,109 lazy_sym: link.File.LazySymbol,
121 // TODO don't use an "out" parameter like this; put it in the result instead110 // TODO don't use an "out" parameter like this; put it in the result instead
122 alignment: *Alignment,111 alignment: *Alignment,
123 code: *std.ArrayList(u8),112 code: *std.ArrayListUnmanaged(u8),
124 debug_output: link.File.DebugInfoOutput,113 debug_output: link.File.DebugInfoOutput,
125 reloc_parent: link.File.RelocInfo.Parent,114 reloc_parent: link.File.RelocInfo.Parent,
126) CodeGenError!Result {115) CodeGenError!void {
127 _ = reloc_parent;116 _ = reloc_parent;
128117
129 const tracy = trace(@src());118 const tracy = trace(@src());
130 defer tracy.end();119 defer tracy.end();
131120
132 const comp = bin_file.comp;121 const comp = bin_file.comp;
133 const ip = &pt.zcu.intern_pool;122 const gpa = comp.gpa;
123 const zcu = pt.zcu;
124 const ip = &zcu.intern_pool;
134 const target = comp.root_mod.resolved_target.result;125 const target = comp.root_mod.resolved_target.result;
135 const endian = target.cpu.arch.endian();126 const endian = target.cpu.arch.endian();
136 const gpa = comp.gpa;
137127
138 log.debug("generateLazySymbol: kind = {s}, ty = {}", .{128 log.debug("generateLazySymbol: kind = {s}, ty = {}", .{
139 @tagName(lazy_sym.kind),129 @tagName(lazy_sym.kind),
...@@ -150,52 +140,56 @@ pub fn generateLazySymbol(...@@ -150,52 +140,56 @@ pub fn generateLazySymbol(
150 const err_names = ip.global_error_set.getNamesFromMainThread();140 const err_names = ip.global_error_set.getNamesFromMainThread();
151 var offset_index: u32 = @intCast(code.items.len);141 var offset_index: u32 = @intCast(code.items.len);
152 var string_index: u32 = @intCast(4 * (1 + err_names.len + @intFromBool(err_names.len > 0)));142 var string_index: u32 = @intCast(4 * (1 + err_names.len + @intFromBool(err_names.len > 0)));
153 try code.resize(offset_index + string_index);143 try code.resize(gpa, offset_index + string_index);
154 mem.writeInt(u32, code.items[offset_index..][0..4], @intCast(err_names.len), endian);144 mem.writeInt(u32, code.items[offset_index..][0..4], @intCast(err_names.len), endian);
155 if (err_names.len == 0) return .ok;145 if (err_names.len == 0) return;
156 offset_index += 4;146 offset_index += 4;
157 for (err_names) |err_name_nts| {147 for (err_names) |err_name_nts| {
158 const err_name = err_name_nts.toSlice(ip);148 const err_name = err_name_nts.toSlice(ip);
159 mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian);149 mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian);
160 offset_index += 4;150 offset_index += 4;
161 try code.ensureUnusedCapacity(err_name.len + 1);151 try code.ensureUnusedCapacity(gpa, err_name.len + 1);
162 code.appendSliceAssumeCapacity(err_name);152 code.appendSliceAssumeCapacity(err_name);
163 code.appendAssumeCapacity(0);153 code.appendAssumeCapacity(0);
164 string_index += @intCast(err_name.len + 1);154 string_index += @intCast(err_name.len + 1);
165 }155 }
166 mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian);156 mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian);
167 return .ok;157 } else if (Type.fromInterned(lazy_sym.ty).zigTypeTag(zcu) == .@"enum") {
168 } else if (Type.fromInterned(lazy_sym.ty).zigTypeTag(pt.zcu) == .@"enum") {
169 alignment.* = .@"1";158 alignment.* = .@"1";
170 const enum_ty = Type.fromInterned(lazy_sym.ty);159 const enum_ty = Type.fromInterned(lazy_sym.ty);
171 const tag_names = enum_ty.enumFields(pt.zcu);160 const tag_names = enum_ty.enumFields(zcu);
172 for (0..tag_names.len) |tag_index| {161 for (0..tag_names.len) |tag_index| {
173 const tag_name = tag_names.get(ip)[tag_index].toSlice(ip);162 const tag_name = tag_names.get(ip)[tag_index].toSlice(ip);
174 try code.ensureUnusedCapacity(tag_name.len + 1);163 try code.ensureUnusedCapacity(gpa, tag_name.len + 1);
175 code.appendSliceAssumeCapacity(tag_name);164 code.appendSliceAssumeCapacity(tag_name);
176 code.appendAssumeCapacity(0);165 code.appendAssumeCapacity(0);
177 }166 }
178 return .ok;167 } else {
179 } else return .{ .fail = try .create(168 return zcu.codegenFailType(lazy_sym.ty, "TODO implement generateLazySymbol for {s} {}", .{
180 gpa,169 @tagName(lazy_sym.kind), Type.fromInterned(lazy_sym.ty).fmt(pt),
181 src_loc,170 });
182 "TODO implement generateLazySymbol for {s} {}",171 }
183 .{ @tagName(lazy_sym.kind), Type.fromInterned(lazy_sym.ty).fmt(pt) },
184 ) };
185}172}
186173
174pub const GenerateSymbolError = error{
175 OutOfMemory,
176 /// Compiler was asked to operate on a number larger than supported.
177 Overflow,
178};
179
187pub fn generateSymbol(180pub fn generateSymbol(
188 bin_file: *link.File,181 bin_file: *link.File,
189 pt: Zcu.PerThread,182 pt: Zcu.PerThread,
190 src_loc: Zcu.LazySrcLoc,183 src_loc: Zcu.LazySrcLoc,
191 val: Value,184 val: Value,
192 code: *std.ArrayList(u8),185 code: *std.ArrayListUnmanaged(u8),
193 reloc_parent: link.File.RelocInfo.Parent,186 reloc_parent: link.File.RelocInfo.Parent,
194) CodeGenError!Result {187) GenerateSymbolError!void {
195 const tracy = trace(@src());188 const tracy = trace(@src());
196 defer tracy.end();189 defer tracy.end();
197190
198 const zcu = pt.zcu;191 const zcu = pt.zcu;
192 const gpa = zcu.gpa;
199 const ip = &zcu.intern_pool;193 const ip = &zcu.intern_pool;
200 const ty = val.typeOf(zcu);194 const ty = val.typeOf(zcu);
201195
...@@ -206,8 +200,8 @@ pub fn generateSymbol(...@@ -206,8 +200,8 @@ pub fn generateSymbol(
206200
207 if (val.isUndefDeep(zcu)) {201 if (val.isUndefDeep(zcu)) {
208 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;202 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
209 try code.appendNTimes(0xaa, abi_size);203 try code.appendNTimes(gpa, 0xaa, abi_size);
210 return .ok;204 return;
211 }205 }
212206
213 switch (ip.indexToKey(val.toIntern())) {207 switch (ip.indexToKey(val.toIntern())) {
...@@ -231,14 +225,13 @@ pub fn generateSymbol(...@@ -231,14 +225,13 @@ pub fn generateSymbol(
231225
232 .undef => unreachable, // handled above226 .undef => unreachable, // handled above
233 .simple_value => |simple_value| switch (simple_value) {227 .simple_value => |simple_value| switch (simple_value) {
234 .undefined,228 .undefined => unreachable, // non-runtime value
235 .void,229 .void => unreachable, // non-runtime value
236 .null,230 .null => unreachable, // non-runtime value
237 .empty_tuple,231 .@"unreachable" => unreachable, // non-runtime value
238 .@"unreachable",232 .generic_poison => unreachable, // non-runtime value
239 .generic_poison,233 .empty_tuple => return,
240 => unreachable, // non-runtime values234 .false, .true => try code.append(gpa, switch (simple_value) {
241 .false, .true => try code.append(switch (simple_value) {
242 .false => 0,235 .false => 0,
243 .true => 1,236 .true => 1,
244 else => unreachable,237 else => unreachable,
...@@ -254,11 +247,11 @@ pub fn generateSymbol(...@@ -254,11 +247,11 @@ pub fn generateSymbol(
254 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;247 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
255 var space: Value.BigIntSpace = undefined;248 var space: Value.BigIntSpace = undefined;
256 const int_val = val.toBigInt(&space, zcu);249 const int_val = val.toBigInt(&space, zcu);
257 int_val.writeTwosComplement(try code.addManyAsSlice(abi_size), endian);250 int_val.writeTwosComplement(try code.addManyAsSlice(gpa, abi_size), endian);
258 },251 },
259 .err => |err| {252 .err => |err| {
260 const int = try pt.getErrorValue(err.name);253 const int = try pt.getErrorValue(err.name);
261 try code.writer().writeInt(u16, @intCast(int), endian);254 try code.writer(gpa).writeInt(u16, @intCast(int), endian);
262 },255 },
263 .error_union => |error_union| {256 .error_union => |error_union| {
264 const payload_ty = ty.errorUnionPayload(zcu);257 const payload_ty = ty.errorUnionPayload(zcu);
...@@ -268,8 +261,8 @@ pub fn generateSymbol(...@@ -268,8 +261,8 @@ pub fn generateSymbol(
268 };261 };
269262
270 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {263 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
271 try code.writer().writeInt(u16, err_val, endian);264 try code.writer(gpa).writeInt(u16, err_val, endian);
272 return .ok;265 return;
273 }266 }
274267
275 const payload_align = payload_ty.abiAlignment(zcu);268 const payload_align = payload_ty.abiAlignment(zcu);
...@@ -278,72 +271,57 @@ pub fn generateSymbol(...@@ -278,72 +271,57 @@ pub fn generateSymbol(
278271
279 // error value first when its type is larger than the error union's payload272 // error value first when its type is larger than the error union's payload
280 if (error_align.order(payload_align) == .gt) {273 if (error_align.order(payload_align) == .gt) {
281 try code.writer().writeInt(u16, err_val, endian);274 try code.writer(gpa).writeInt(u16, err_val, endian);
282 }275 }
283276
284 // emit payload part of the error union277 // emit payload part of the error union
285 {278 {
286 const begin = code.items.len;279 const begin = code.items.len;
287 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (error_union.val) {280 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (error_union.val) {
288 .err_name => try pt.intern(.{ .undef = payload_ty.toIntern() }),281 .err_name => try pt.intern(.{ .undef = payload_ty.toIntern() }),
289 .payload => |payload| payload,282 .payload => |payload| payload,
290 }), code, reloc_parent)) {283 }), code, reloc_parent);
291 .ok => {},
292 .fail => |em| return .{ .fail = em },
293 }
294 const unpadded_end = code.items.len - begin;284 const unpadded_end = code.items.len - begin;
295 const padded_end = abi_align.forward(unpadded_end);285 const padded_end = abi_align.forward(unpadded_end);
296 const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow;286 const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow;
297287
298 if (padding > 0) {288 if (padding > 0) {
299 try code.appendNTimes(0, padding);289 try code.appendNTimes(gpa, 0, padding);
300 }290 }
301 }291 }
302292
303 // Payload size is larger than error set, so emit our error set last293 // Payload size is larger than error set, so emit our error set last
304 if (error_align.compare(.lte, payload_align)) {294 if (error_align.compare(.lte, payload_align)) {
305 const begin = code.items.len;295 const begin = code.items.len;
306 try code.writer().writeInt(u16, err_val, endian);296 try code.writer(gpa).writeInt(u16, err_val, endian);
307 const unpadded_end = code.items.len - begin;297 const unpadded_end = code.items.len - begin;
308 const padded_end = abi_align.forward(unpadded_end);298 const padded_end = abi_align.forward(unpadded_end);
309 const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow;299 const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow;
310300
311 if (padding > 0) {301 if (padding > 0) {
312 try code.appendNTimes(0, padding);302 try code.appendNTimes(gpa, 0, padding);
313 }303 }
314 }304 }
315 },305 },
316 .enum_tag => |enum_tag| {306 .enum_tag => |enum_tag| {
317 const int_tag_ty = ty.intTagType(zcu);307 const int_tag_ty = ty.intTagType(zcu);
318 switch (try generateSymbol(bin_file, pt, src_loc, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), code, reloc_parent)) {308 try generateSymbol(bin_file, pt, src_loc, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), code, reloc_parent);
319 .ok => {},
320 .fail => |em| return .{ .fail = em },
321 }
322 },309 },
323 .float => |float| switch (float.storage) {310 .float => |float| switch (float.storage) {
324 .f16 => |f16_val| writeFloat(f16, f16_val, target, endian, try code.addManyAsArray(2)),311 .f16 => |f16_val| writeFloat(f16, f16_val, target, endian, try code.addManyAsArray(gpa, 2)),
325 .f32 => |f32_val| writeFloat(f32, f32_val, target, endian, try code.addManyAsArray(4)),312 .f32 => |f32_val| writeFloat(f32, f32_val, target, endian, try code.addManyAsArray(gpa, 4)),
326 .f64 => |f64_val| writeFloat(f64, f64_val, target, endian, try code.addManyAsArray(8)),313 .f64 => |f64_val| writeFloat(f64, f64_val, target, endian, try code.addManyAsArray(gpa, 8)),
327 .f80 => |f80_val| {314 .f80 => |f80_val| {
328 writeFloat(f80, f80_val, target, endian, try code.addManyAsArray(10));315 writeFloat(f80, f80_val, target, endian, try code.addManyAsArray(gpa, 10));
329 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;316 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
330 try code.appendNTimes(0, abi_size - 10);317 try code.appendNTimes(gpa, 0, abi_size - 10);
331 },318 },
332 .f128 => |f128_val| writeFloat(f128, f128_val, target, endian, try code.addManyAsArray(16)),319 .f128 => |f128_val| writeFloat(f128, f128_val, target, endian, try code.addManyAsArray(gpa, 16)),
333 },
334 .ptr => switch (try lowerPtr(bin_file, pt, src_loc, val.toIntern(), code, reloc_parent, 0)) {
335 .ok => {},
336 .fail => |em| return .{ .fail = em },
337 },320 },
321 .ptr => try lowerPtr(bin_file, pt, src_loc, val.toIntern(), code, reloc_parent, 0),
338 .slice => |slice| {322 .slice => |slice| {
339 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), code, reloc_parent)) {323 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), code, reloc_parent);
340 .ok => {},324 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.len), code, reloc_parent);
341 .fail => |em| return .{ .fail = em },
342 }
343 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.len), code, reloc_parent)) {
344 .ok => {},
345 .fail => |em| return .{ .fail = em },
346 }
347 },325 },
348 .opt => {326 .opt => {
349 const payload_type = ty.optionalChild(zcu);327 const payload_type = ty.optionalChild(zcu);
...@@ -352,12 +330,9 @@ pub fn generateSymbol(...@@ -352,12 +330,9 @@ pub fn generateSymbol(
352330
353 if (ty.optionalReprIsPayload(zcu)) {331 if (ty.optionalReprIsPayload(zcu)) {
354 if (payload_val) |value| {332 if (payload_val) |value| {
355 switch (try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent)) {333 try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent);
356 .ok => {},
357 .fail => |em| return Result{ .fail = em },
358 }
359 } else {334 } else {
360 try code.appendNTimes(0, abi_size);335 try code.appendNTimes(gpa, 0, abi_size);
361 }336 }
362 } else {337 } else {
363 const padding = abi_size - (math.cast(usize, payload_type.abiSize(zcu)) orelse return error.Overflow) - 1;338 const padding = abi_size - (math.cast(usize, payload_type.abiSize(zcu)) orelse return error.Overflow) - 1;
...@@ -365,39 +340,33 @@ pub fn generateSymbol(...@@ -365,39 +340,33 @@ pub fn generateSymbol(
365 const value = payload_val orelse Value.fromInterned(try pt.intern(.{340 const value = payload_val orelse Value.fromInterned(try pt.intern(.{
366 .undef = payload_type.toIntern(),341 .undef = payload_type.toIntern(),
367 }));342 }));
368 switch (try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent)) {343 try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent);
369 .ok => {},
370 .fail => |em| return Result{ .fail = em },
371 }
372 }344 }
373 try code.writer().writeByte(@intFromBool(payload_val != null));345 try code.writer(gpa).writeByte(@intFromBool(payload_val != null));
374 try code.appendNTimes(0, padding);346 try code.appendNTimes(gpa, 0, padding);
375 }347 }
376 },348 },
377 .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) {349 .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) {
378 .array_type => |array_type| switch (aggregate.storage) {350 .array_type => |array_type| switch (aggregate.storage) {
379 .bytes => |bytes| try code.appendSlice(bytes.toSlice(array_type.lenIncludingSentinel(), ip)),351 .bytes => |bytes| try code.appendSlice(gpa, bytes.toSlice(array_type.lenIncludingSentinel(), ip)),
380 .elems, .repeated_elem => {352 .elems, .repeated_elem => {
381 var index: u64 = 0;353 var index: u64 = 0;
382 while (index < array_type.lenIncludingSentinel()) : (index += 1) {354 while (index < array_type.lenIncludingSentinel()) : (index += 1) {
383 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (aggregate.storage) {355 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (aggregate.storage) {
384 .bytes => unreachable,356 .bytes => unreachable,
385 .elems => |elems| elems[@intCast(index)],357 .elems => |elems| elems[@intCast(index)],
386 .repeated_elem => |elem| if (index < array_type.len)358 .repeated_elem => |elem| if (index < array_type.len)
387 elem359 elem
388 else360 else
389 array_type.sentinel,361 array_type.sentinel,
390 }), code, reloc_parent)) {362 }), code, reloc_parent);
391 .ok => {},
392 .fail => |em| return .{ .fail = em },
393 }
394 }363 }
395 },364 },
396 },365 },
397 .vector_type => |vector_type| {366 .vector_type => |vector_type| {
398 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;367 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
399 if (vector_type.child == .bool_type) {368 if (vector_type.child == .bool_type) {
400 const bytes = try code.addManyAsSlice(abi_size);369 const bytes = try code.addManyAsSlice(gpa, abi_size);
401 @memset(bytes, 0xaa);370 @memset(bytes, 0xaa);
402 var index: usize = 0;371 var index: usize = 0;
403 const len = math.cast(usize, vector_type.len) orelse return error.Overflow;372 const len = math.cast(usize, vector_type.len) orelse return error.Overflow;
...@@ -436,20 +405,17 @@ pub fn generateSymbol(...@@ -436,20 +405,17 @@ pub fn generateSymbol(
436 }405 }
437 } else {406 } else {
438 switch (aggregate.storage) {407 switch (aggregate.storage) {
439 .bytes => |bytes| try code.appendSlice(bytes.toSlice(vector_type.len, ip)),408 .bytes => |bytes| try code.appendSlice(gpa, bytes.toSlice(vector_type.len, ip)),
440 .elems, .repeated_elem => {409 .elems, .repeated_elem => {
441 var index: u64 = 0;410 var index: u64 = 0;
442 while (index < vector_type.len) : (index += 1) {411 while (index < vector_type.len) : (index += 1) {
443 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (aggregate.storage) {412 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (aggregate.storage) {
444 .bytes => unreachable,413 .bytes => unreachable,
445 .elems => |elems| elems[414 .elems => |elems| elems[
446 math.cast(usize, index) orelse return error.Overflow415 math.cast(usize, index) orelse return error.Overflow
447 ],416 ],
448 .repeated_elem => |elem| elem,417 .repeated_elem => |elem| elem,
449 }), code, reloc_parent)) {418 }), code, reloc_parent);
450 .ok => {},
451 .fail => |em| return .{ .fail = em },
452 }
453 }419 }
454 },420 },
455 }421 }
...@@ -457,7 +423,7 @@ pub fn generateSymbol(...@@ -457,7 +423,7 @@ pub fn generateSymbol(
457 const padding = abi_size -423 const padding = abi_size -
458 (math.cast(usize, Type.fromInterned(vector_type.child).abiSize(zcu) * vector_type.len) orelse424 (math.cast(usize, Type.fromInterned(vector_type.child).abiSize(zcu) * vector_type.len) orelse
459 return error.Overflow);425 return error.Overflow);
460 if (padding > 0) try code.appendNTimes(0, padding);426 if (padding > 0) try code.appendNTimes(gpa, 0, padding);
461 }427 }
462 },428 },
463 .tuple_type => |tuple| {429 .tuple_type => |tuple| {
...@@ -479,10 +445,7 @@ pub fn generateSymbol(...@@ -479,10 +445,7 @@ pub fn generateSymbol(
479 .repeated_elem => |elem| elem,445 .repeated_elem => |elem| elem,
480 };446 };
481447
482 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent)) {448 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent);
483 .ok => {},
484 .fail => |em| return Result{ .fail = em },
485 }
486 const unpadded_field_end = code.items.len - struct_begin;449 const unpadded_field_end = code.items.len - struct_begin;
487450
488 // Pad struct members if required451 // Pad struct members if required
...@@ -491,7 +454,7 @@ pub fn generateSymbol(...@@ -491,7 +454,7 @@ pub fn generateSymbol(
491 return error.Overflow;454 return error.Overflow;
492455
493 if (padding > 0) {456 if (padding > 0) {
494 try code.appendNTimes(0, padding);457 try code.appendNTimes(gpa, 0, padding);
495 }458 }
496 }459 }
497 },460 },
...@@ -501,7 +464,7 @@ pub fn generateSymbol(...@@ -501,7 +464,7 @@ pub fn generateSymbol(
501 .@"packed" => {464 .@"packed" => {
502 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;465 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
503 const current_pos = code.items.len;466 const current_pos = code.items.len;
504 try code.appendNTimes(0, abi_size);467 try code.appendNTimes(gpa, 0, abi_size);
505 var bits: u16 = 0;468 var bits: u16 = 0;
506469
507 for (struct_type.field_types.get(ip), 0..) |field_ty, index| {470 for (struct_type.field_types.get(ip), 0..) |field_ty, index| {
...@@ -519,12 +482,10 @@ pub fn generateSymbol(...@@ -519,12 +482,10 @@ pub fn generateSymbol(
519 if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .pointer) {482 if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .pointer) {
520 const field_size = math.cast(usize, Type.fromInterned(field_ty).abiSize(zcu)) orelse483 const field_size = math.cast(usize, Type.fromInterned(field_ty).abiSize(zcu)) orelse
521 return error.Overflow;484 return error.Overflow;
522 var tmp_list = try std.ArrayList(u8).initCapacity(code.allocator, field_size);485 var tmp_list = try std.ArrayListUnmanaged(u8).initCapacity(gpa, field_size);
523 defer tmp_list.deinit();486 defer tmp_list.deinit(gpa);
524 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), &tmp_list, reloc_parent)) {487 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), &tmp_list, reloc_parent);
525 .ok => @memcpy(code.items[current_pos..][0..tmp_list.items.len], tmp_list.items),488 @memcpy(code.items[current_pos..][0..tmp_list.items.len], tmp_list.items);
526 .fail => |em| return Result{ .fail = em },
527 }
528 } else {489 } else {
529 Value.fromInterned(field_val).writeToPackedMemory(Type.fromInterned(field_ty), pt, code.items[current_pos..], bits) catch unreachable;490 Value.fromInterned(field_val).writeToPackedMemory(Type.fromInterned(field_ty), pt, code.items[current_pos..], bits) catch unreachable;
530 }491 }
...@@ -554,12 +515,9 @@ pub fn generateSymbol(...@@ -554,12 +515,9 @@ pub fn generateSymbol(
554 usize,515 usize,
555 offsets[field_index] - (code.items.len - struct_begin),516 offsets[field_index] - (code.items.len - struct_begin),
556 ) orelse return error.Overflow;517 ) orelse return error.Overflow;
557 if (padding > 0) try code.appendNTimes(0, padding);518 if (padding > 0) try code.appendNTimes(gpa, 0, padding);
558519
559 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent)) {520 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent);
560 .ok => {},
561 .fail => |em| return Result{ .fail = em },
562 }
563 }521 }
564522
565 const size = struct_type.sizeUnordered(ip);523 const size = struct_type.sizeUnordered(ip);
...@@ -570,7 +528,7 @@ pub fn generateSymbol(...@@ -570,7 +528,7 @@ pub fn generateSymbol(
570 std.mem.alignForward(u64, size, @max(alignment, 1)) -528 std.mem.alignForward(u64, size, @max(alignment, 1)) -
571 (code.items.len - struct_begin),529 (code.items.len - struct_begin),
572 ) orelse return error.Overflow;530 ) orelse return error.Overflow;
573 if (padding > 0) try code.appendNTimes(0, padding);531 if (padding > 0) try code.appendNTimes(gpa, 0, padding);
574 },532 },
575 }533 }
576 },534 },
...@@ -585,10 +543,7 @@ pub fn generateSymbol(...@@ -585,10 +543,7 @@ pub fn generateSymbol(
585543
586 // Check if we should store the tag first.544 // Check if we should store the tag first.
587 if (layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)) {545 if (layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)) {
588 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent)) {546 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent);
589 .ok => {},
590 .fail => |em| return Result{ .fail = em },
591 }
592 }547 }
593548
594 const union_obj = zcu.typeToUnion(ty).?;549 const union_obj = zcu.typeToUnion(ty).?;
...@@ -596,39 +551,29 @@ pub fn generateSymbol(...@@ -596,39 +551,29 @@ pub fn generateSymbol(
596 const field_index = ty.unionTagFieldIndex(Value.fromInterned(un.tag), zcu).?;551 const field_index = ty.unionTagFieldIndex(Value.fromInterned(un.tag), zcu).?;
597 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);552 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
598 if (!field_ty.hasRuntimeBits(zcu)) {553 if (!field_ty.hasRuntimeBits(zcu)) {
599 try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);554 try code.appendNTimes(gpa, 0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);
600 } else {555 } else {
601 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent)) {556 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent);
602 .ok => {},
603 .fail => |em| return Result{ .fail = em },
604 }
605557
606 const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(zcu)) orelse return error.Overflow;558 const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(zcu)) orelse return error.Overflow;
607 if (padding > 0) {559 if (padding > 0) {
608 try code.appendNTimes(0, padding);560 try code.appendNTimes(gpa, 0, padding);
609 }561 }
610 }562 }
611 } else {563 } else {
612 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent)) {564 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent);
613 .ok => {},
614 .fail => |em| return Result{ .fail = em },
615 }
616 }565 }
617566
618 if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) {567 if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) {
619 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent)) {568 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent);
620 .ok => {},
621 .fail => |em| return Result{ .fail = em },
622 }
623569
624 if (layout.padding > 0) {570 if (layout.padding > 0) {
625 try code.appendNTimes(0, layout.padding);571 try code.appendNTimes(gpa, 0, layout.padding);
626 }572 }
627 }573 }
628 },574 },
629 .memoized_call => unreachable,575 .memoized_call => unreachable,
630 }576 }
631 return .ok;
632}577}
633578
634fn lowerPtr(579fn lowerPtr(
...@@ -636,15 +581,15 @@ fn lowerPtr(...@@ -636,15 +581,15 @@ fn lowerPtr(
636 pt: Zcu.PerThread,581 pt: Zcu.PerThread,
637 src_loc: Zcu.LazySrcLoc,582 src_loc: Zcu.LazySrcLoc,
638 ptr_val: InternPool.Index,583 ptr_val: InternPool.Index,
639 code: *std.ArrayList(u8),584 code: *std.ArrayListUnmanaged(u8),
640 reloc_parent: link.File.RelocInfo.Parent,585 reloc_parent: link.File.RelocInfo.Parent,
641 prev_offset: u64,586 prev_offset: u64,
642) CodeGenError!Result {587) GenerateSymbolError!void {
643 const zcu = pt.zcu;588 const zcu = pt.zcu;
644 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;589 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;
645 const offset: u64 = prev_offset + ptr.byte_offset;590 const offset: u64 = prev_offset + ptr.byte_offset;
646 return switch (ptr.base_addr) {591 return switch (ptr.base_addr) {
647 .nav => |nav| try lowerNavRef(bin_file, pt, src_loc, nav, code, reloc_parent, offset),592 .nav => |nav| try lowerNavRef(bin_file, pt, nav, code, reloc_parent, offset),
648 .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, code, reloc_parent, offset),593 .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, code, reloc_parent, offset),
649 .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), code, reloc_parent),594 .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), code, reloc_parent),
650 .eu_payload => |eu_ptr| try lowerPtr(595 .eu_payload => |eu_ptr| try lowerPtr(
...@@ -689,29 +634,62 @@ fn lowerUavRef(...@@ -689,29 +634,62 @@ fn lowerUavRef(
689 pt: Zcu.PerThread,634 pt: Zcu.PerThread,
690 src_loc: Zcu.LazySrcLoc,635 src_loc: Zcu.LazySrcLoc,
691 uav: InternPool.Key.Ptr.BaseAddr.Uav,636 uav: InternPool.Key.Ptr.BaseAddr.Uav,
692 code: *std.ArrayList(u8),637 code: *std.ArrayListUnmanaged(u8),
693 reloc_parent: link.File.RelocInfo.Parent,638 reloc_parent: link.File.RelocInfo.Parent,
694 offset: u64,639 offset: u64,
695) CodeGenError!Result {640) GenerateSymbolError!void {
696 const zcu = pt.zcu;641 const zcu = pt.zcu;
642 const gpa = zcu.gpa;
697 const ip = &zcu.intern_pool;643 const ip = &zcu.intern_pool;
698 const target = lf.comp.root_mod.resolved_target.result;644 const comp = lf.comp;
699645 const target = &comp.root_mod.resolved_target.result;
700 const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8);646 const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8);
647 const is_obj = comp.config.output_mode == .Obj;
701 const uav_val = uav.val;648 const uav_val = uav.val;
702 const uav_ty = Type.fromInterned(ip.typeOf(uav_val));649 const uav_ty = Type.fromInterned(ip.typeOf(uav_val));
703 log.debug("lowerUavRef: ty = {}", .{uav_ty.fmt(pt)});
704 const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn";650 const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn";
651
652 log.debug("lowerUavRef: ty = {}", .{uav_ty.fmt(pt)});
653 try code.ensureUnusedCapacity(gpa, ptr_width_bytes);
654
705 if (!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) {655 if (!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) {
706 try code.appendNTimes(0xaa, ptr_width_bytes);656 code.appendNTimesAssumeCapacity(0xaa, ptr_width_bytes);
707 return Result.ok;657 return;
658 }
659
660 switch (lf.tag) {
661 .c => unreachable,
662 .spirv => unreachable,
663 .nvptx => unreachable,
664 .wasm => {
665 dev.check(link.File.Tag.wasm.devFeature());
666 const wasm = lf.cast(.wasm).?;
667 assert(reloc_parent == .none);
668 if (is_obj) {
669 try wasm.out_relocs.append(gpa, .{
670 .offset = @intCast(code.items.len),
671 .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(uav.val) },
672 .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64,
673 .addend = @intCast(offset),
674 });
675 } else {
676 try wasm.uav_fixups.ensureUnusedCapacity(gpa, 1);
677 wasm.uav_fixups.appendAssumeCapacity(.{
678 .uavs_exe_index = try wasm.refUavExe(uav.val, uav.orig_ty),
679 .offset = @intCast(code.items.len),
680 .addend = @intCast(offset),
681 });
682 }
683 code.appendNTimesAssumeCapacity(0, ptr_width_bytes);
684 return;
685 },
686 else => {},
708 }687 }
709688
710 const uav_align = ip.indexToKey(uav.orig_ty).ptr_type.flags.alignment;689 const uav_align = ip.indexToKey(uav.orig_ty).ptr_type.flags.alignment;
711 const res = try lf.lowerUav(pt, uav_val, uav_align, src_loc);690 switch (try lf.lowerUav(pt, uav_val, uav_align, src_loc)) {
712 switch (res) {
713 .mcv => {},691 .mcv => {},
714 .fail => |em| return .{ .fail = em },692 .fail => |em| std.debug.panic("TODO rework lowerUav. internal error: {s}", .{em.msg}),
715 }693 }
716694
717 const vaddr = try lf.getUavVAddr(uav_val, .{695 const vaddr = try lf.getUavVAddr(uav_val, .{
...@@ -721,51 +699,91 @@ fn lowerUavRef(...@@ -721,51 +699,91 @@ fn lowerUavRef(
721 });699 });
722 const endian = target.cpu.arch.endian();700 const endian = target.cpu.arch.endian();
723 switch (ptr_width_bytes) {701 switch (ptr_width_bytes) {
724 2 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian),702 2 => mem.writeInt(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(vaddr), endian),
725 4 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian),703 4 => mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(vaddr), endian),
726 8 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian),704 8 => mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), vaddr, endian),
727 else => unreachable,705 else => unreachable,
728 }706 }
729
730 return Result.ok;
731}707}
732708
733fn lowerNavRef(709fn lowerNavRef(
734 lf: *link.File,710 lf: *link.File,
735 pt: Zcu.PerThread,711 pt: Zcu.PerThread,
736 src_loc: Zcu.LazySrcLoc,
737 nav_index: InternPool.Nav.Index,712 nav_index: InternPool.Nav.Index,
738 code: *std.ArrayList(u8),713 code: *std.ArrayListUnmanaged(u8),
739 reloc_parent: link.File.RelocInfo.Parent,714 reloc_parent: link.File.RelocInfo.Parent,
740 offset: u64,715 offset: u64,
741) CodeGenError!Result {716) GenerateSymbolError!void {
742 _ = src_loc;
743 const zcu = pt.zcu;717 const zcu = pt.zcu;
718 const gpa = zcu.gpa;
744 const ip = &zcu.intern_pool;719 const ip = &zcu.intern_pool;
745 const target = zcu.navFileScope(nav_index).mod.resolved_target.result;720 const target = zcu.navFileScope(nav_index).mod.resolved_target.result;
746721 const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8);
747 const ptr_width = target.ptrBitWidth();722 const is_obj = lf.comp.config.output_mode == .Obj;
748 const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip));723 const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip));
749 const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn";724 const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn";
725
726 try code.ensureUnusedCapacity(gpa, ptr_width_bytes);
727
750 if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) {728 if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) {
751 try code.appendNTimes(0xaa, @divExact(ptr_width, 8));729 code.appendNTimesAssumeCapacity(0xaa, ptr_width_bytes);
752 return Result.ok;730 return;
753 }731 }
754732
755 const vaddr = try lf.getNavVAddr(pt, nav_index, .{733 switch (lf.tag) {
734 .c => unreachable,
735 .spirv => unreachable,
736 .nvptx => unreachable,
737 .wasm => {
738 dev.check(link.File.Tag.wasm.devFeature());
739 const wasm = lf.cast(.wasm).?;
740 assert(reloc_parent == .none);
741 if (is_fn_body) {
742 const gop = try wasm.zcu_indirect_function_set.getOrPut(gpa, nav_index);
743 if (!gop.found_existing) gop.value_ptr.* = {};
744 if (is_obj) {
745 @panic("TODO add out_reloc for this");
746 } else {
747 try wasm.func_table_fixups.append(gpa, .{
748 .table_index = @enumFromInt(gop.index),
749 .offset = @intCast(code.items.len),
750 });
751 }
752 } else {
753 if (is_obj) {
754 try wasm.out_relocs.append(gpa, .{
755 .offset = @intCast(code.items.len),
756 .pointee = .{ .symbol_index = try wasm.navSymbolIndex(nav_index) },
757 .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64,
758 .addend = @intCast(offset),
759 });
760 } else {
761 try wasm.nav_fixups.ensureUnusedCapacity(gpa, 1);
762 wasm.nav_fixups.appendAssumeCapacity(.{
763 .navs_exe_index = try wasm.refNavExe(nav_index),
764 .offset = @intCast(code.items.len),
765 .addend = @intCast(offset),
766 });
767 }
768 }
769 code.appendNTimesAssumeCapacity(0, ptr_width_bytes);
770 return;
771 },
772 else => {},
773 }
774
775 const vaddr = lf.getNavVAddr(pt, nav_index, .{
756 .parent = reloc_parent,776 .parent = reloc_parent,
757 .offset = code.items.len,777 .offset = code.items.len,
758 .addend = @intCast(offset),778 .addend = @intCast(offset),
759 });779 }) catch @panic("TODO rework getNavVAddr");
760 const endian = target.cpu.arch.endian();780 const endian = target.cpu.arch.endian();
761 switch (ptr_width) {781 switch (ptr_width_bytes) {
762 16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian),782 2 => mem.writeInt(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(vaddr), endian),
763 32 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian),783 4 => mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(vaddr), endian),
764 64 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian),784 8 => mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), vaddr, endian),
765 else => unreachable,785 else => unreachable,
766 }786 }
767
768 return Result.ok;
769}787}
770788
771/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:789/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:
src/codegen/c.zig+4-4
...@@ -3052,12 +3052,12 @@ pub fn genDeclValue(...@@ -3052,12 +3052,12 @@ pub fn genDeclValue(
3052 try w.writeAll(";\n");3052 try w.writeAll(";\n");
3053}3053}
30543054
3055pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const u32) !void {3055pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void {
3056 const zcu = dg.pt.zcu;3056 const zcu = dg.pt.zcu;
3057 const ip = &zcu.intern_pool;3057 const ip = &zcu.intern_pool;
3058 const fwd = dg.fwdDeclWriter();3058 const fwd = dg.fwdDeclWriter();
30593059
3060 const main_name = zcu.all_exports.items[export_indices[0]].opts.name;3060 const main_name = export_indices[0].ptr(zcu).opts.name;
3061 try fwd.writeAll("#define ");3061 try fwd.writeAll("#define ");
3062 switch (exported) {3062 switch (exported) {
3063 .nav => |nav| try dg.renderNavName(fwd, nav),3063 .nav => |nav| try dg.renderNavName(fwd, nav),
...@@ -3069,7 +3069,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const...@@ -3069,7 +3069,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
30693069
3070 const exported_val = exported.getValue(zcu);3070 const exported_val = exported.getValue(zcu);
3071 if (ip.isFunctionType(exported_val.typeOf(zcu).toIntern())) return for (export_indices) |export_index| {3071 if (ip.isFunctionType(exported_val.typeOf(zcu).toIntern())) return for (export_indices) |export_index| {
3072 const @"export" = &zcu.all_exports.items[export_index];3072 const @"export" = export_index.ptr(zcu);
3073 try fwd.writeAll("zig_extern ");3073 try fwd.writeAll("zig_extern ");
3074 if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn ");3074 if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn ");
3075 try dg.renderFunctionSignature(3075 try dg.renderFunctionSignature(
...@@ -3091,7 +3091,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const...@@ -3091,7 +3091,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
3091 else => true,3091 else => true,
3092 };3092 };
3093 for (export_indices) |export_index| {3093 for (export_indices) |export_index| {
3094 const @"export" = &zcu.all_exports.items[export_index];3094 const @"export" = export_index.ptr(zcu);
3095 try fwd.writeAll("zig_extern ");3095 try fwd.writeAll("zig_extern ");
3096 if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage ");3096 if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage ");
3097 const extern_name = @"export".opts.name.toSlice(ip);3097 const extern_name = @"export".opts.name.toSlice(ip);
src/codegen/llvm.zig+22-32
...@@ -1059,9 +1059,10 @@ pub const Object = struct {...@@ -1059,9 +1059,10 @@ pub const Object = struct {
1059 lto: Compilation.Config.LtoMode,1059 lto: Compilation.Config.LtoMode,
1060 };1060 };
10611061
1062 pub fn emit(o: *Object, options: EmitOptions) !void {1062 pub fn emit(o: *Object, options: EmitOptions) error{ LinkFailure, OutOfMemory }!void {
1063 const zcu = o.pt.zcu;1063 const zcu = o.pt.zcu;
1064 const comp = zcu.comp;1064 const comp = zcu.comp;
1065 const diags = &comp.link_diags;
10651066
1066 {1067 {
1067 try o.genErrorNameTable();1068 try o.genErrorNameTable();
...@@ -1223,27 +1224,30 @@ pub const Object = struct {...@@ -1223,27 +1224,30 @@ pub const Object = struct {
1223 o.builder.clearAndFree();1224 o.builder.clearAndFree();
12241225
1225 if (options.pre_bc_path) |path| {1226 if (options.pre_bc_path) |path| {
1226 var file = try std.fs.cwd().createFile(path, .{});1227 var file = std.fs.cwd().createFile(path, .{}) catch |err|
1228 return diags.fail("failed to create '{s}': {s}", .{ path, @errorName(err) });
1227 defer file.close();1229 defer file.close();
12281230
1229 const ptr: [*]const u8 = @ptrCast(bitcode.ptr);1231 const ptr: [*]const u8 = @ptrCast(bitcode.ptr);
1230 try file.writeAll(ptr[0..(bitcode.len * 4)]);1232 file.writeAll(ptr[0..(bitcode.len * 4)]) catch |err|
1233 return diags.fail("failed to write to '{s}': {s}", .{ path, @errorName(err) });
1231 }1234 }
12321235
1233 if (options.asm_path == null and options.bin_path == null and1236 if (options.asm_path == null and options.bin_path == null and
1234 options.post_ir_path == null and options.post_bc_path == null) return;1237 options.post_ir_path == null and options.post_bc_path == null) return;
12351238
1236 if (options.post_bc_path) |path| {1239 if (options.post_bc_path) |path| {
1237 var file = try std.fs.cwd().createFileZ(path, .{});1240 var file = std.fs.cwd().createFileZ(path, .{}) catch |err|
1241 return diags.fail("failed to create '{s}': {s}", .{ path, @errorName(err) });
1238 defer file.close();1242 defer file.close();
12391243
1240 const ptr: [*]const u8 = @ptrCast(bitcode.ptr);1244 const ptr: [*]const u8 = @ptrCast(bitcode.ptr);
1241 try file.writeAll(ptr[0..(bitcode.len * 4)]);1245 file.writeAll(ptr[0..(bitcode.len * 4)]) catch |err|
1246 return diags.fail("failed to write to '{s}': {s}", .{ path, @errorName(err) });
1242 }1247 }
12431248
1244 if (!build_options.have_llvm or !comp.config.use_lib_llvm) {1249 if (!build_options.have_llvm or !comp.config.use_lib_llvm) {
1245 log.err("emitting without libllvm not implemented", .{});1250 return diags.fail("emitting without libllvm not implemented", .{});
1246 return error.FailedToEmit;
1247 }1251 }
12481252
1249 initializeLLVMTarget(comp.root_mod.resolved_target.result.cpu.arch);1253 initializeLLVMTarget(comp.root_mod.resolved_target.result.cpu.arch);
...@@ -1263,8 +1267,7 @@ pub const Object = struct {...@@ -1263,8 +1267,7 @@ pub const Object = struct {
12631267
1264 var module: *llvm.Module = undefined;1268 var module: *llvm.Module = undefined;
1265 if (context.parseBitcodeInContext2(bitcode_memory_buffer, &module).toBool() or context.getBrokenDebugInfo()) {1269 if (context.parseBitcodeInContext2(bitcode_memory_buffer, &module).toBool() or context.getBrokenDebugInfo()) {
1266 log.err("Failed to parse bitcode", .{});1270 return diags.fail("Failed to parse bitcode", .{});
1267 return error.FailedToEmit;
1268 }1271 }
1269 break :emit .{ context, module };1272 break :emit .{ context, module };
1270 };1273 };
...@@ -1274,12 +1277,7 @@ pub const Object = struct {...@@ -1274,12 +1277,7 @@ pub const Object = struct {
1274 var error_message: [*:0]const u8 = undefined;1277 var error_message: [*:0]const u8 = undefined;
1275 if (llvm.Target.getFromTriple(target_triple_sentinel, &target, &error_message).toBool()) {1278 if (llvm.Target.getFromTriple(target_triple_sentinel, &target, &error_message).toBool()) {
1276 defer llvm.disposeMessage(error_message);1279 defer llvm.disposeMessage(error_message);
12771280 return diags.fail("LLVM failed to parse '{s}': {s}", .{ target_triple_sentinel, error_message });
1278 log.err("LLVM failed to parse '{s}': {s}", .{
1279 target_triple_sentinel,
1280 error_message,
1281 });
1282 @panic("Invalid LLVM triple");
1283 }1281 }
12841282
1285 const optimize_mode = comp.root_mod.optimize_mode;1283 const optimize_mode = comp.root_mod.optimize_mode;
...@@ -1374,10 +1372,9 @@ pub const Object = struct {...@@ -1374,10 +1372,9 @@ pub const Object = struct {
1374 if (options.asm_path != null and options.bin_path != null) {1372 if (options.asm_path != null and options.bin_path != null) {
1375 if (target_machine.emitToFile(module, &error_message, &lowered_options)) {1373 if (target_machine.emitToFile(module, &error_message, &lowered_options)) {
1376 defer llvm.disposeMessage(error_message);1374 defer llvm.disposeMessage(error_message);
1377 log.err("LLVM failed to emit bin={s} ir={s}: {s}", .{1375 return diags.fail("LLVM failed to emit bin={s} ir={s}: {s}", .{
1378 emit_bin_msg, post_llvm_ir_msg, error_message,1376 emit_bin_msg, post_llvm_ir_msg, error_message,
1379 });1377 });
1380 return error.FailedToEmit;
1381 }1378 }
1382 lowered_options.bin_filename = null;1379 lowered_options.bin_filename = null;
1383 lowered_options.llvm_ir_filename = null;1380 lowered_options.llvm_ir_filename = null;
...@@ -1386,11 +1383,9 @@ pub const Object = struct {...@@ -1386,11 +1383,9 @@ pub const Object = struct {
1386 lowered_options.asm_filename = options.asm_path;1383 lowered_options.asm_filename = options.asm_path;
1387 if (target_machine.emitToFile(module, &error_message, &lowered_options)) {1384 if (target_machine.emitToFile(module, &error_message, &lowered_options)) {
1388 defer llvm.disposeMessage(error_message);1385 defer llvm.disposeMessage(error_message);
1389 log.err("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{1386 return diags.fail("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{
1390 emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg,1387 emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, error_message,
1391 error_message,
1392 });1388 });
1393 return error.FailedToEmit;
1394 }1389 }
1395 }1390 }
13961391
...@@ -1815,7 +1810,7 @@ pub const Object = struct {...@@ -1815,7 +1810,7 @@ pub const Object = struct {
1815 self: *Object,1810 self: *Object,
1816 pt: Zcu.PerThread,1811 pt: Zcu.PerThread,
1817 exported: Zcu.Exported,1812 exported: Zcu.Exported,
1818 export_indices: []const u32,1813 export_indices: []const Zcu.Export.Index,
1819 ) link.File.UpdateExportsError!void {1814 ) link.File.UpdateExportsError!void {
1820 assert(std.meta.eql(pt, self.pt));1815 assert(std.meta.eql(pt, self.pt));
1821 const zcu = pt.zcu;1816 const zcu = pt.zcu;
...@@ -1843,11 +1838,11 @@ pub const Object = struct {...@@ -1843,11 +1838,11 @@ pub const Object = struct {
1843 o: *Object,1838 o: *Object,
1844 zcu: *Zcu,1839 zcu: *Zcu,
1845 exported_value: InternPool.Index,1840 exported_value: InternPool.Index,
1846 export_indices: []const u32,1841 export_indices: []const Zcu.Export.Index,
1847 ) link.File.UpdateExportsError!void {1842 ) link.File.UpdateExportsError!void {
1848 const gpa = zcu.gpa;1843 const gpa = zcu.gpa;
1849 const ip = &zcu.intern_pool;1844 const ip = &zcu.intern_pool;
1850 const main_exp_name = try o.builder.strtabString(zcu.all_exports.items[export_indices[0]].opts.name.toSlice(ip));1845 const main_exp_name = try o.builder.strtabString(export_indices[0].ptr(zcu).opts.name.toSlice(ip));
1851 const global_index = i: {1846 const global_index = i: {
1852 const gop = try o.uav_map.getOrPut(gpa, exported_value);1847 const gop = try o.uav_map.getOrPut(gpa, exported_value);
1853 if (gop.found_existing) {1848 if (gop.found_existing) {
...@@ -1878,11 +1873,11 @@ pub const Object = struct {...@@ -1878,11 +1873,11 @@ pub const Object = struct {
1878 o: *Object,1873 o: *Object,
1879 zcu: *Zcu,1874 zcu: *Zcu,
1880 global_index: Builder.Global.Index,1875 global_index: Builder.Global.Index,
1881 export_indices: []const u32,1876 export_indices: []const Zcu.Export.Index,
1882 ) link.File.UpdateExportsError!void {1877 ) link.File.UpdateExportsError!void {
1883 const comp = zcu.comp;1878 const comp = zcu.comp;
1884 const ip = &zcu.intern_pool;1879 const ip = &zcu.intern_pool;
1885 const first_export = zcu.all_exports.items[export_indices[0]];1880 const first_export = export_indices[0].ptr(zcu);
18861881
1887 // We will rename this global to have a name matching `first_export`.1882 // We will rename this global to have a name matching `first_export`.
1888 // Successive exports become aliases.1883 // Successive exports become aliases.
...@@ -1939,7 +1934,7 @@ pub const Object = struct {...@@ -1939,7 +1934,7 @@ pub const Object = struct {
1939 // Until then we iterate over existing aliases and make them point1934 // Until then we iterate over existing aliases and make them point
1940 // to the correct decl, or otherwise add a new alias. Old aliases are leaked.1935 // to the correct decl, or otherwise add a new alias. Old aliases are leaked.
1941 for (export_indices[1..]) |export_idx| {1936 for (export_indices[1..]) |export_idx| {
1942 const exp = zcu.all_exports.items[export_idx];1937 const exp = export_idx.ptr(zcu);
1943 const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip));1938 const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip));
1944 if (o.builder.getGlobal(exp_name)) |global| {1939 if (o.builder.getGlobal(exp_name)) |global| {
1945 switch (global.ptrConst(&o.builder).kind) {1940 switch (global.ptrConst(&o.builder).kind) {
...@@ -1967,11 +1962,6 @@ pub const Object = struct {...@@ -1967,11 +1962,6 @@ pub const Object = struct {
1967 }1962 }
1968 }1963 }
19691964
1970 pub fn freeDecl(self: *Object, decl_index: InternPool.DeclIndex) void {
1971 const global = self.decl_map.get(decl_index) orelse return;
1972 global.delete(&self.builder);
1973 }
1974
1975 fn getDebugFile(o: *Object, file_index: Zcu.File.Index) Allocator.Error!Builder.Metadata {1965 fn getDebugFile(o: *Object, file_index: Zcu.File.Index) Allocator.Error!Builder.Metadata {
1976 const gpa = o.gpa;1966 const gpa = o.gpa;
1977 const gop = try o.debug_file_map.getOrPut(gpa, file_index);1967 const gop = try o.debug_file_map.getOrPut(gpa, file_index);
src/dev.zig+12
...@@ -30,6 +30,10 @@ pub const Env = enum {...@@ -30,6 +30,10 @@ pub const Env = enum {
30 /// - `zig build-* -fno-llvm -fno-lld -target riscv64-linux`30 /// - `zig build-* -fno-llvm -fno-lld -target riscv64-linux`
31 @"riscv64-linux",31 @"riscv64-linux",
3232
33 /// - sema
34 /// - `zig build-* -fno-llvm -fno-lld -target wasm32-* --listen=-`
35 wasm,
36
33 pub inline fn supports(comptime dev_env: Env, comptime feature: Feature) bool {37 pub inline fn supports(comptime dev_env: Env, comptime feature: Feature) bool {
34 return switch (dev_env) {38 return switch (dev_env) {
35 .full => true,39 .full => true,
...@@ -144,6 +148,14 @@ pub const Env = enum {...@@ -144,6 +148,14 @@ pub const Env = enum {
144 => true,148 => true,
145 else => Env.sema.supports(feature),149 else => Env.sema.supports(feature),
146 },150 },
151 .wasm => switch (feature) {
152 .stdio_listen,
153 .incremental,
154 .wasm_backend,
155 .wasm_linker,
156 => true,
157 else => Env.sema.supports(feature),
158 },
147 };159 };
148 }160 }
149161
src/glibc.zig+12
...@@ -1217,6 +1217,18 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi...@@ -1217,6 +1217,18 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
1217 });1217 });
1218}1218}
12191219
1220pub fn sharedObjectsCount(target: *const std.Target) u8 {
1221 const target_version = target.os.versionRange().gnuLibCVersion() orelse return 0;
1222 var count: u8 = 0;
1223 for (libs) |lib| {
1224 if (lib.removed_in) |rem_in| {
1225 if (target_version.order(rem_in) != .lt) continue;
1226 }
1227 count += 1;
1228 }
1229 return count;
1230}
1231
1220fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {1232fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
1221 const target_version = comp.getTarget().os.versionRange().gnuLibCVersion().?;1233 const target_version = comp.getTarget().os.versionRange().gnuLibCVersion().?;
12221234
src/link.zig+181-156
...@@ -38,6 +38,11 @@ pub const Diags = struct {...@@ -38,6 +38,11 @@ pub const Diags = struct {
38 flags: Flags,38 flags: Flags,
39 lld: std.ArrayListUnmanaged(Lld),39 lld: std.ArrayListUnmanaged(Lld),
4040
41 pub const SourceLocation = union(enum) {
42 none,
43 wasm: File.Wasm.SourceLocation,
44 };
45
41 pub const Flags = packed struct {46 pub const Flags = packed struct {
42 no_entry_point_found: bool = false,47 no_entry_point_found: bool = false,
43 missing_libc: bool = false,48 missing_libc: bool = false,
...@@ -70,9 +75,25 @@ pub const Diags = struct {...@@ -70,9 +75,25 @@ pub const Diags = struct {
70 };75 };
7176
72 pub const Msg = struct {77 pub const Msg = struct {
78 source_location: SourceLocation = .none,
73 msg: []const u8,79 msg: []const u8,
74 notes: []Msg = &.{},80 notes: []Msg = &.{},
7581
82 fn string(
83 msg: *const Msg,
84 bundle: *std.zig.ErrorBundle.Wip,
85 base: ?*File,
86 ) Allocator.Error!std.zig.ErrorBundle.String {
87 return switch (msg.source_location) {
88 .none => try bundle.addString(msg.msg),
89 .wasm => |sl| {
90 dev.check(.wasm_linker);
91 const wasm = base.?.cast(.wasm).?;
92 return sl.string(msg.msg, bundle, wasm);
93 },
94 };
95 }
96
76 pub fn deinit(self: *Msg, gpa: Allocator) void {97 pub fn deinit(self: *Msg, gpa: Allocator) void {
77 for (self.notes) |*note| note.deinit(gpa);98 for (self.notes) |*note| note.deinit(gpa);
78 gpa.free(self.notes);99 gpa.free(self.notes);
...@@ -97,15 +118,12 @@ pub const Diags = struct {...@@ -97,15 +118,12 @@ pub const Diags = struct {
97 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);118 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
98 }119 }
99120
100 pub fn addNote(121 pub fn addNote(err: *ErrorWithNotes, comptime format: []const u8, args: anytype) void {
101 err: *ErrorWithNotes,
102 comptime format: []const u8,
103 args: anytype,
104 ) error{OutOfMemory}!void {
105 const gpa = err.diags.gpa;122 const gpa = err.diags.gpa;
123 const msg = std.fmt.allocPrint(gpa, format, args) catch return err.diags.setAllocFailure();
106 const err_msg = &err.diags.msgs.items[err.index];124 const err_msg = &err.diags.msgs.items[err.index];
107 assert(err.note_slot < err_msg.notes.len);125 assert(err.note_slot < err_msg.notes.len);
108 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };126 err_msg.notes[err.note_slot] = .{ .msg = msg };
109 err.note_slot += 1;127 err.note_slot += 1;
110 }128 }
111 };129 };
...@@ -196,22 +214,35 @@ pub const Diags = struct {...@@ -196,22 +214,35 @@ pub const Diags = struct {
196 return error.LinkFailure;214 return error.LinkFailure;
197 }215 }
198216
217 pub fn failSourceLocation(diags: *Diags, sl: SourceLocation, comptime format: []const u8, args: anytype) error{LinkFailure} {
218 @branchHint(.cold);
219 addErrorSourceLocation(diags, sl, format, args);
220 return error.LinkFailure;
221 }
222
199 pub fn addError(diags: *Diags, comptime format: []const u8, args: anytype) void {223 pub fn addError(diags: *Diags, comptime format: []const u8, args: anytype) void {
224 return addErrorSourceLocation(diags, .none, format, args);
225 }
226
227 pub fn addErrorSourceLocation(diags: *Diags, sl: SourceLocation, comptime format: []const u8, args: anytype) void {
200 @branchHint(.cold);228 @branchHint(.cold);
201 const gpa = diags.gpa;229 const gpa = diags.gpa;
202 const eu_main_msg = std.fmt.allocPrint(gpa, format, args);230 const eu_main_msg = std.fmt.allocPrint(gpa, format, args);
203 diags.mutex.lock();231 diags.mutex.lock();
204 defer diags.mutex.unlock();232 defer diags.mutex.unlock();
205 addErrorLockedFallible(diags, eu_main_msg) catch |err| switch (err) {233 addErrorLockedFallible(diags, sl, eu_main_msg) catch |err| switch (err) {
206 error.OutOfMemory => diags.setAllocFailureLocked(),234 error.OutOfMemory => diags.setAllocFailureLocked(),
207 };235 };
208 }236 }
209237
210 fn addErrorLockedFallible(diags: *Diags, eu_main_msg: Allocator.Error![]u8) Allocator.Error!void {238 fn addErrorLockedFallible(diags: *Diags, sl: SourceLocation, eu_main_msg: Allocator.Error![]u8) Allocator.Error!void {
211 const gpa = diags.gpa;239 const gpa = diags.gpa;
212 const main_msg = try eu_main_msg;240 const main_msg = try eu_main_msg;
213 errdefer gpa.free(main_msg);241 errdefer gpa.free(main_msg);
214 try diags.msgs.append(gpa, .{ .msg = main_msg });242 try diags.msgs.append(gpa, .{
243 .msg = main_msg,
244 .source_location = sl,
245 });
215 }246 }
216247
217 pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes {248 pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
...@@ -329,16 +360,16 @@ pub const Diags = struct {...@@ -329,16 +360,16 @@ pub const Diags = struct {
329 diags.flags.alloc_failure_occurred = true;360 diags.flags.alloc_failure_occurred = true;
330 }361 }
331362
332 pub fn addMessagesToBundle(diags: *const Diags, bundle: *std.zig.ErrorBundle.Wip) Allocator.Error!void {363 pub fn addMessagesToBundle(diags: *const Diags, bundle: *std.zig.ErrorBundle.Wip, base: ?*File) Allocator.Error!void {
333 for (diags.msgs.items) |link_err| {364 for (diags.msgs.items) |link_err| {
334 try bundle.addRootErrorMessage(.{365 try bundle.addRootErrorMessage(.{
335 .msg = try bundle.addString(link_err.msg),366 .msg = try link_err.string(bundle, base),
336 .notes_len = @intCast(link_err.notes.len),367 .notes_len = @intCast(link_err.notes.len),
337 });368 });
338 const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));369 const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
339 for (link_err.notes, 0..) |note, i| {370 for (link_err.notes, 0..) |note, i| {
340 bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{371 bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
341 .msg = try bundle.addString(note.msg),372 .msg = try note.string(bundle, base),
342 }));373 }));
343 }374 }
344 }375 }
...@@ -364,6 +395,7 @@ pub const File = struct {...@@ -364,6 +395,7 @@ pub const File = struct {
364 build_id: std.zig.BuildId,395 build_id: std.zig.BuildId,
365 allow_shlib_undefined: bool,396 allow_shlib_undefined: bool,
366 stack_size: u64,397 stack_size: u64,
398 post_prelink: bool = false,
367399
368 /// Prevents other processes from clobbering files in the output directory400 /// Prevents other processes from clobbering files in the output directory
369 /// of this linking operation.401 /// of this linking operation.
...@@ -400,6 +432,7 @@ pub const File = struct {...@@ -400,6 +432,7 @@ pub const File = struct {
400 export_table: bool,432 export_table: bool,
401 initial_memory: ?u64,433 initial_memory: ?u64,
402 max_memory: ?u64,434 max_memory: ?u64,
435 object_host_name: ?[]const u8,
403 export_symbol_names: []const []const u8,436 export_symbol_names: []const []const u8,
404 global_base: ?u64,437 global_base: ?u64,
405 build_id: std.zig.BuildId,438 build_id: std.zig.BuildId,
...@@ -632,43 +665,15 @@ pub const File = struct {...@@ -632,43 +665,15 @@ pub const File = struct {
632 pub const UpdateDebugInfoError = Dwarf.UpdateError;665 pub const UpdateDebugInfoError = Dwarf.UpdateError;
633 pub const FlushDebugInfoError = Dwarf.FlushError;666 pub const FlushDebugInfoError = Dwarf.FlushError;
634667
668 /// Note that `LinkFailure` is not a member of this error set because the error message
669 /// must be attached to `Zcu.failed_codegen` rather than `Compilation.link_diags`.
635 pub const UpdateNavError = error{670 pub const UpdateNavError = error{
636 OutOfMemory,
637 Overflow,671 Overflow,
638 Underflow,672 OutOfMemory,
639 FileTooBig,673 /// Indicates the error is already reported and stored in
640 InputOutput,674 /// `failed_codegen` on the Zcu.
641 FilesOpenedWithWrongFlags,
642 IsDir,
643 NoSpaceLeft,
644 Unseekable,
645 PermissionDenied,
646 SwapFile,
647 CorruptedData,
648 SystemResources,
649 OperationAborted,
650 BrokenPipe,
651 ConnectionResetByPeer,
652 ConnectionTimedOut,
653 SocketNotConnected,
654 NotOpenForReading,
655 WouldBlock,
656 Canceled,
657 AccessDenied,
658 Unexpected,
659 DiskQuota,
660 NotOpenForWriting,
661 AnalysisFail,
662 CodegenFail,675 CodegenFail,
663 EmitFail,676 };
664 NameTooLong,
665 CurrentWorkingDirectoryUnlinked,
666 LockViolation,
667 NetNameDeleted,
668 DeviceBusy,
669 InvalidArgument,
670 HotSwapUnavailableOnHostOperatingSystem,
671 } || UpdateDebugInfoError;
672677
673 /// Called from within CodeGen to retrieve the symbol index of a global symbol.678 /// Called from within CodeGen to retrieve the symbol index of a global symbol.
674 /// If no symbol exists yet with this name, a new undefined global symbol will679 /// If no symbol exists yet with this name, a new undefined global symbol will
...@@ -701,7 +706,13 @@ pub const File = struct {...@@ -701,7 +706,13 @@ pub const File = struct {
701 }706 }
702 }707 }
703708
704 pub fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateNavError!void {709 pub const UpdateContainerTypeError = error{
710 OutOfMemory,
711 /// `Zcu.failed_types` is already populated with the error message.
712 TypeFailureReported,
713 };
714
715 pub fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void {
705 switch (base.tag) {716 switch (base.tag) {
706 else => {},717 else => {},
707 inline .elf => |tag| {718 inline .elf => |tag| {
...@@ -727,9 +738,15 @@ pub const File = struct {...@@ -727,9 +738,15 @@ pub const File = struct {
727 }738 }
728 }739 }
729740
741 pub const UpdateLineNumberError = error{
742 OutOfMemory,
743 Overflow,
744 LinkFailure,
745 };
746
730 /// On an incremental update, fixup the line number of all `Nav`s at the given `TrackedInst`, because747 /// On an incremental update, fixup the line number of all `Nav`s at the given `TrackedInst`, because
731 /// its line number has changed. The ZIR instruction `ti_id` has tag `.declaration`.748 /// its line number has changed. The ZIR instruction `ti_id` has tag `.declaration`.
732 pub fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) UpdateNavError!void {749 pub fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) UpdateLineNumberError!void {
733 {750 {
734 const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?;751 const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?;
735 const file = pt.zcu.fileByIndex(ti.file);752 const file = pt.zcu.fileByIndex(ti.file);
...@@ -771,83 +788,11 @@ pub const File = struct {...@@ -771,83 +788,11 @@ pub const File = struct {
771 }788 }
772 }789 }
773790
774 /// TODO audit this error set. most of these should be collapsed into one error,
775 /// and Diags.Flags should be updated to convey the meaning to the user.
776 pub const FlushError = error{791 pub const FlushError = error{
777 CacheCheckFailed,792 /// Indicates an error will be present in `Compilation.link_diags`.
778 CurrentWorkingDirectoryUnlinked,
779 DivisionByZero,
780 DllImportLibraryNotFound,
781 ExpectedFuncType,
782 FailedToEmit,
783 FileSystem,
784 FilesOpenedWithWrongFlags,
785 /// Deprecated. Use `LinkFailure` instead.
786 /// Formerly used to indicate an error will be present in `Compilation.link_errors`.
787 FlushFailure,
788 /// Indicates an error will be present in `Compilation.link_errors`.
789 LinkFailure,793 LinkFailure,
790 FunctionSignatureMismatch,
791 GlobalTypeMismatch,
792 HotSwapUnavailableOnHostOperatingSystem,
793 InvalidCharacter,
794 InvalidEntryKind,
795 InvalidFeatureSet,
796 InvalidFormat,
797 InvalidIndex,
798 InvalidInitFunc,
799 InvalidMagicByte,
800 InvalidWasmVersion,
801 LLDCrashed,
802 LLDReportedFailure,
803 LLD_LinkingIsTODO_ForSpirV,
804 LibCInstallationMissingCrtDir,
805 LibCInstallationNotAvailable,
806 LinkingWithoutZigSourceUnimplemented,
807 MalformedArchive,
808 MalformedDwarf,
809 MalformedSection,
810 MemoryTooBig,
811 MemoryTooSmall,
812 MissAlignment,
813 MissingEndForBody,
814 MissingEndForExpression,
815 MissingSymbol,
816 MissingTableSymbols,
817 ModuleNameMismatch,
818 NoObjectsToLink,
819 NotObjectFile,
820 NotSupported,
821 OutOfMemory,794 OutOfMemory,
822 Overflow,795 };
823 PermissionDenied,
824 StreamTooLong,
825 SwapFile,
826 SymbolCollision,
827 SymbolMismatchingType,
828 TODOImplementPlan9Objs,
829 TODOImplementWritingLibFiles,
830 UnableToSpawnSelf,
831 UnableToSpawnWasm,
832 UnableToWriteArchive,
833 UndefinedLocal,
834 UndefinedSymbol,
835 Underflow,
836 UnexpectedRemainder,
837 UnexpectedTable,
838 UnexpectedValue,
839 UnknownFeature,
840 UnrecognizedVolume,
841 Unseekable,
842 UnsupportedCpuArchitecture,
843 UnsupportedVersion,
844 UnexpectedEndOfFile,
845 } ||
846 fs.File.WriteFileError ||
847 fs.File.OpenError ||
848 std.process.Child.SpawnError ||
849 fs.Dir.CopyFileError ||
850 FlushDebugInfoError;
851796
852 /// Commit pending changes and write headers. Takes into account final output mode797 /// Commit pending changes and write headers. Takes into account final output mode
853 /// and `use_lld`, not only `effectiveOutputMode`.798 /// and `use_lld`, not only `effectiveOutputMode`.
...@@ -864,10 +809,17 @@ pub const File = struct {...@@ -864,10 +809,17 @@ pub const File = struct {
864 assert(comp.c_object_table.count() == 1);809 assert(comp.c_object_table.count() == 1);
865 const the_key = comp.c_object_table.keys()[0];810 const the_key = comp.c_object_table.keys()[0];
866 const cached_pp_file_path = the_key.status.success.object_path;811 const cached_pp_file_path = the_key.status.success.object_path;
867 try cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{});812 cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}) catch |err| {
813 const diags = &base.comp.link_diags;
814 return diags.fail("failed to copy '{'}' to '{'}': {s}", .{
815 @as(Path, cached_pp_file_path), @as(Path, emit), @errorName(err),
816 });
817 };
868 return;818 return;
869 }819 }
870820
821 assert(base.post_prelink);
822
871 const use_lld = build_options.have_llvm and comp.config.use_lld;823 const use_lld = build_options.have_llvm and comp.config.use_lld;
872 const output_mode = comp.config.output_mode;824 const output_mode = comp.config.output_mode;
873 const link_mode = comp.config.link_mode;825 const link_mode = comp.config.link_mode;
...@@ -893,16 +845,6 @@ pub const File = struct {...@@ -893,16 +845,6 @@ pub const File = struct {
893 }845 }
894 }846 }
895847
896 /// Called when a Decl is deleted from the Zcu.
897 pub fn freeDecl(base: *File, decl_index: InternPool.DeclIndex) void {
898 switch (base.tag) {
899 inline else => |tag| {
900 dev.check(tag.devFeature());
901 @as(*tag.Type(), @fieldParentPtr("base", base)).freeDecl(decl_index);
902 },
903 }
904 }
905
906 pub const UpdateExportsError = error{848 pub const UpdateExportsError = error{
907 OutOfMemory,849 OutOfMemory,
908 AnalysisFail,850 AnalysisFail,
...@@ -916,7 +858,7 @@ pub const File = struct {...@@ -916,7 +858,7 @@ pub const File = struct {
916 base: *File,858 base: *File,
917 pt: Zcu.PerThread,859 pt: Zcu.PerThread,
918 exported: Zcu.Exported,860 exported: Zcu.Exported,
919 export_indices: []const u32,861 export_indices: []const Zcu.Export.Index,
920 ) UpdateExportsError!void {862 ) UpdateExportsError!void {
921 switch (base.tag) {863 switch (base.tag) {
922 inline else => |tag| {864 inline else => |tag| {
...@@ -932,6 +874,7 @@ pub const File = struct {...@@ -932,6 +874,7 @@ pub const File = struct {
932 addend: u32,874 addend: u32,
933875
934 pub const Parent = union(enum) {876 pub const Parent = union(enum) {
877 none,
935 atom_index: u32,878 atom_index: u32,
936 debug_output: DebugInfoOutput,879 debug_output: DebugInfoOutput,
937 };880 };
...@@ -948,6 +891,7 @@ pub const File = struct {...@@ -948,6 +891,7 @@ pub const File = struct {
948 .c => unreachable,891 .c => unreachable,
949 .spirv => unreachable,892 .spirv => unreachable,
950 .nvptx => unreachable,893 .nvptx => unreachable,
894 .wasm => unreachable,
951 inline else => |tag| {895 inline else => |tag| {
952 dev.check(tag.devFeature());896 dev.check(tag.devFeature());
953 return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);897 return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);
...@@ -966,6 +910,7 @@ pub const File = struct {...@@ -966,6 +910,7 @@ pub const File = struct {
966 .c => unreachable,910 .c => unreachable,
967 .spirv => unreachable,911 .spirv => unreachable,
968 .nvptx => unreachable,912 .nvptx => unreachable,
913 .wasm => unreachable,
969 inline else => |tag| {914 inline else => |tag| {
970 dev.check(tag.devFeature());915 dev.check(tag.devFeature());
971 return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc);916 return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc);
...@@ -978,6 +923,7 @@ pub const File = struct {...@@ -978,6 +923,7 @@ pub const File = struct {
978 .c => unreachable,923 .c => unreachable,
979 .spirv => unreachable,924 .spirv => unreachable,
980 .nvptx => unreachable,925 .nvptx => unreachable,
926 .wasm => unreachable,
981 inline else => |tag| {927 inline else => |tag| {
982 dev.check(tag.devFeature());928 dev.check(tag.devFeature());
983 return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);929 return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);
...@@ -1099,12 +1045,44 @@ pub const File = struct {...@@ -1099,12 +1045,44 @@ pub const File = struct {
1099 }1045 }
1100 }1046 }
11011047
1048 /// Called when all linker inputs have been sent via `loadInput`. After
1049 /// this, `loadInput` will not be called anymore.
1050 pub fn prelink(base: *File, prog_node: std.Progress.Node) FlushError!void {
1051 assert(!base.post_prelink);
1052 const use_lld = build_options.have_llvm and base.comp.config.use_lld;
1053 if (use_lld) return;
1054
1055 // In this case, an object file is created by the LLVM backend, so
1056 // there is no prelink phase. The Zig code is linked as a standard
1057 // object along with the others.
1058 if (base.zcu_object_sub_path != null) return;
1059
1060 switch (base.tag) {
1061 inline .wasm => |tag| {
1062 dev.check(tag.devFeature());
1063 return @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(prog_node);
1064 },
1065 else => {},
1066 }
1067 }
1068
1102 pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void {1069 pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void {
1103 dev.check(.lld_linker);1070 dev.check(.lld_linker);
11041071
1105 const tracy = trace(@src());1072 const tracy = trace(@src());
1106 defer tracy.end();1073 defer tracy.end();
11071074
1075 const comp = base.comp;
1076 const diags = &comp.link_diags;
1077
1078 return linkAsArchiveInner(base, arena, tid, prog_node) catch |err| switch (err) {
1079 error.OutOfMemory => return error.OutOfMemory,
1080 error.LinkFailure => return error.LinkFailure,
1081 else => |e| return diags.fail("failed to link as archive: {s}", .{@errorName(e)}),
1082 };
1083 }
1084
1085 fn linkAsArchiveInner(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {
1108 const comp = base.comp;1086 const comp = base.comp;
11091087
1110 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.1088 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
...@@ -1364,6 +1342,16 @@ pub const File = struct {...@@ -1364,6 +1342,16 @@ pub const File = struct {
1364 }, llvm_object, prog_node);1342 }, llvm_object, prog_node);
1365 }1343 }
13661344
1345 pub fn cgFail(
1346 base: *File,
1347 nav_index: InternPool.Nav.Index,
1348 comptime format: []const u8,
1349 args: anytype,
1350 ) error{ CodegenFail, OutOfMemory } {
1351 @branchHint(.cold);
1352 return base.comp.zcu.?.codegenFail(nav_index, format, args);
1353 }
1354
1367 pub const C = @import("link/C.zig");1355 pub const C = @import("link/C.zig");
1368 pub const Coff = @import("link/Coff.zig");1356 pub const Coff = @import("link/Coff.zig");
1369 pub const Plan9 = @import("link/Plan9.zig");1357 pub const Plan9 = @import("link/Plan9.zig");
...@@ -1379,12 +1367,32 @@ pub const File = struct {...@@ -1379,12 +1367,32 @@ pub const File = struct {
1379/// from the rest of compilation. All tasks performed here are1367/// from the rest of compilation. All tasks performed here are
1380/// single-threaded with respect to one another.1368/// single-threaded with respect to one another.
1381pub fn flushTaskQueue(tid: usize, comp: *Compilation) void {1369pub fn flushTaskQueue(tid: usize, comp: *Compilation) void {
1370 const diags = &comp.link_diags;
1382 // As soon as check() is called, another `flushTaskQueue` call could occur,1371 // As soon as check() is called, another `flushTaskQueue` call could occur,
1383 // so the safety lock must go after the check.1372 // so the safety lock must go after the check.
1384 while (comp.link_task_queue.check()) |tasks| {1373 while (comp.link_task_queue.check()) |tasks| {
1385 comp.link_task_queue_safety.lock();1374 comp.link_task_queue_safety.lock();
1386 defer comp.link_task_queue_safety.unlock();1375 defer comp.link_task_queue_safety.unlock();
1376
1377 if (comp.remaining_prelink_tasks > 0) {
1378 comp.link_task_queue_postponed.ensureUnusedCapacity(comp.gpa, tasks.len) catch |err| switch (err) {
1379 error.OutOfMemory => return diags.setAllocFailure(),
1380 };
1381 }
1382
1387 for (tasks) |task| doTask(comp, tid, task);1383 for (tasks) |task| doTask(comp, tid, task);
1384
1385 if (comp.remaining_prelink_tasks == 0) {
1386 if (comp.bin_file) |base| if (!base.post_prelink) {
1387 base.prelink(comp.work_queue_progress_node) catch |err| switch (err) {
1388 error.OutOfMemory => diags.setAllocFailure(),
1389 error.LinkFailure => continue,
1390 };
1391 base.post_prelink = true;
1392 for (comp.link_task_queue_postponed.items) |task| doTask(comp, tid, task);
1393 comp.link_task_queue_postponed.clearRetainingCapacity();
1394 };
1395 }
1388 }1396 }
1389}1397}
13901398
...@@ -1428,6 +1436,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {...@@ -1428,6 +1436,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
1428 const diags = &comp.link_diags;1436 const diags = &comp.link_diags;
1429 switch (task) {1437 switch (task) {
1430 .load_explicitly_provided => if (comp.bin_file) |base| {1438 .load_explicitly_provided => if (comp.bin_file) |base| {
1439 comp.remaining_prelink_tasks -= 1;
1431 const prog_node = comp.work_queue_progress_node.start("Parse Linker Inputs", comp.link_inputs.len);1440 const prog_node = comp.work_queue_progress_node.start("Parse Linker Inputs", comp.link_inputs.len);
1432 defer prog_node.end();1441 defer prog_node.end();
1433 for (comp.link_inputs) |input| {1442 for (comp.link_inputs) |input| {
...@@ -1445,6 +1454,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {...@@ -1445,6 +1454,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
1445 }1454 }
1446 },1455 },
1447 .load_host_libc => if (comp.bin_file) |base| {1456 .load_host_libc => if (comp.bin_file) |base| {
1457 comp.remaining_prelink_tasks -= 1;
1448 const prog_node = comp.work_queue_progress_node.start("Linker Parse Host libc", 0);1458 const prog_node = comp.work_queue_progress_node.start("Linker Parse Host libc", 0);
1449 defer prog_node.end();1459 defer prog_node.end();
14501460
...@@ -1504,6 +1514,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {...@@ -1504,6 +1514,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
1504 }1514 }
1505 },1515 },
1506 .load_object => |path| if (comp.bin_file) |base| {1516 .load_object => |path| if (comp.bin_file) |base| {
1517 comp.remaining_prelink_tasks -= 1;
1507 const prog_node = comp.work_queue_progress_node.start("Linker Parse Object", 0);1518 const prog_node = comp.work_queue_progress_node.start("Linker Parse Object", 0);
1508 defer prog_node.end();1519 defer prog_node.end();
1509 base.openLoadObject(path) catch |err| switch (err) {1520 base.openLoadObject(path) catch |err| switch (err) {
...@@ -1512,6 +1523,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {...@@ -1512,6 +1523,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
1512 };1523 };
1513 },1524 },
1514 .load_archive => |path| if (comp.bin_file) |base| {1525 .load_archive => |path| if (comp.bin_file) |base| {
1526 comp.remaining_prelink_tasks -= 1;
1515 const prog_node = comp.work_queue_progress_node.start("Linker Parse Archive", 0);1527 const prog_node = comp.work_queue_progress_node.start("Linker Parse Archive", 0);
1516 defer prog_node.end();1528 defer prog_node.end();
1517 base.openLoadArchive(path, null) catch |err| switch (err) {1529 base.openLoadArchive(path, null) catch |err| switch (err) {
...@@ -1520,6 +1532,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {...@@ -1520,6 +1532,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
1520 };1532 };
1521 },1533 },
1522 .load_dso => |path| if (comp.bin_file) |base| {1534 .load_dso => |path| if (comp.bin_file) |base| {
1535 comp.remaining_prelink_tasks -= 1;
1523 const prog_node = comp.work_queue_progress_node.start("Linker Parse Shared Library", 0);1536 const prog_node = comp.work_queue_progress_node.start("Linker Parse Shared Library", 0);
1524 defer prog_node.end();1537 defer prog_node.end();
1525 base.openLoadDso(path, .{1538 base.openLoadDso(path, .{
...@@ -1531,6 +1544,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {...@@ -1531,6 +1544,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
1531 };1544 };
1532 },1545 },
1533 .load_input => |input| if (comp.bin_file) |base| {1546 .load_input => |input| if (comp.bin_file) |base| {
1547 comp.remaining_prelink_tasks -= 1;
1534 const prog_node = comp.work_queue_progress_node.start("Linker Parse Input", 0);1548 const prog_node = comp.work_queue_progress_node.start("Linker Parse Input", 0);
1535 defer prog_node.end();1549 defer prog_node.end();
1536 base.loadInput(input) catch |err| switch (err) {1550 base.loadInput(input) catch |err| switch (err) {
...@@ -1545,26 +1559,38 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {...@@ -1545,26 +1559,38 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
1545 };1559 };
1546 },1560 },
1547 .codegen_nav => |nav_index| {1561 .codegen_nav => |nav_index| {
1548 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));1562 if (comp.remaining_prelink_tasks == 0) {
1549 defer pt.deactivate();1563 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
1550 pt.linkerUpdateNav(nav_index) catch |err| switch (err) {1564 defer pt.deactivate();
1551 error.OutOfMemory => diags.setAllocFailure(),1565 pt.linkerUpdateNav(nav_index) catch |err| switch (err) {
1552 };1566 error.OutOfMemory => diags.setAllocFailure(),
1567 };
1568 } else {
1569 comp.link_task_queue_postponed.appendAssumeCapacity(task);
1570 }
1553 },1571 },
1554 .codegen_func => |func| {1572 .codegen_func => |func| {
1555 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));1573 if (comp.remaining_prelink_tasks == 0) {
1556 defer pt.deactivate();1574 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
1557 // This call takes ownership of `func.air`.1575 defer pt.deactivate();
1558 pt.linkerUpdateFunc(func.func, func.air) catch |err| switch (err) {1576 // This call takes ownership of `func.air`.
1559 error.OutOfMemory => diags.setAllocFailure(),1577 pt.linkerUpdateFunc(func.func, func.air) catch |err| switch (err) {
1560 };1578 error.OutOfMemory => diags.setAllocFailure(),
1579 };
1580 } else {
1581 comp.link_task_queue_postponed.appendAssumeCapacity(task);
1582 }
1561 },1583 },
1562 .codegen_type => |ty| {1584 .codegen_type => |ty| {
1563 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));1585 if (comp.remaining_prelink_tasks == 0) {
1564 defer pt.deactivate();1586 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
1565 pt.linkerUpdateContainerType(ty) catch |err| switch (err) {1587 defer pt.deactivate();
1566 error.OutOfMemory => diags.setAllocFailure(),1588 pt.linkerUpdateContainerType(ty) catch |err| switch (err) {
1567 };1589 error.OutOfMemory => diags.setAllocFailure(),
1590 };
1591 } else {
1592 comp.link_task_queue_postponed.appendAssumeCapacity(task);
1593 }
1568 },1594 },
1569 .update_line_number => |ti| {1595 .update_line_number => |ti| {
1570 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));1596 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
...@@ -1593,7 +1619,7 @@ pub fn spawnLld(...@@ -1593,7 +1619,7 @@ pub fn spawnLld(
1593 const exit_code = try lldMain(arena, argv, false);1619 const exit_code = try lldMain(arena, argv, false);
1594 if (exit_code == 0) return;1620 if (exit_code == 0) return;
1595 if (comp.clang_passthrough_mode) std.process.exit(exit_code);1621 if (comp.clang_passthrough_mode) std.process.exit(exit_code);
1596 return error.LLDReportedFailure;1622 return error.LinkFailure;
1597 }1623 }
15981624
1599 var stderr: []u8 = &.{};1625 var stderr: []u8 = &.{};
...@@ -1670,17 +1696,16 @@ pub fn spawnLld(...@@ -1670,17 +1696,16 @@ pub fn spawnLld(
1670 return error.UnableToSpawnSelf;1696 return error.UnableToSpawnSelf;
1671 };1697 };
16721698
1699 const diags = &comp.link_diags;
1673 switch (term) {1700 switch (term) {
1674 .Exited => |code| if (code != 0) {1701 .Exited => |code| if (code != 0) {
1675 if (comp.clang_passthrough_mode) std.process.exit(code);1702 if (comp.clang_passthrough_mode) std.process.exit(code);
1676 const diags = &comp.link_diags;
1677 diags.lockAndParseLldStderr(argv[1], stderr);1703 diags.lockAndParseLldStderr(argv[1], stderr);
1678 return error.LLDReportedFailure;1704 return error.LinkFailure;
1679 },1705 },
1680 else => {1706 else => {
1681 if (comp.clang_passthrough_mode) std.process.abort();1707 if (comp.clang_passthrough_mode) std.process.abort();
1682 log.err("{s} terminated with stderr:\n{s}", .{ argv[0], stderr });1708 return diags.fail("{s} terminated with stderr:\n{s}", .{ argv[0], stderr });
1683 return error.LLDCrashed;
1684 },1709 },
1685 }1710 }
16861711
...@@ -2239,7 +2264,7 @@ fn resolvePathInputLib(...@@ -2239,7 +2264,7 @@ fn resolvePathInputLib(
2239 try wip_errors.init(gpa);2264 try wip_errors.init(gpa);
2240 defer wip_errors.deinit();2265 defer wip_errors.deinit();
22412266
2242 try diags.addMessagesToBundle(&wip_errors);2267 try diags.addMessagesToBundle(&wip_errors, null);
22432268
2244 var error_bundle = try wip_errors.toOwnedBundle("");2269 var error_bundle = try wip_errors.toOwnedBundle("");
2245 defer error_bundle.deinit(gpa);2270 defer error_bundle.deinit(gpa);
src/link/C.zig+11-16
...@@ -175,21 +175,13 @@ pub fn deinit(self: *C) void {...@@ -175,21 +175,13 @@ pub fn deinit(self: *C) void {
175 self.lazy_code_buf.deinit(gpa);175 self.lazy_code_buf.deinit(gpa);
176}176}
177177
178pub fn freeDecl(self: *C, decl_index: InternPool.DeclIndex) void {
179 const gpa = self.base.comp.gpa;
180 if (self.decl_table.fetchSwapRemove(decl_index)) |kv| {
181 var decl_block = kv.value;
182 decl_block.deinit(gpa);
183 }
184}
185
186pub fn updateFunc(178pub fn updateFunc(
187 self: *C,179 self: *C,
188 pt: Zcu.PerThread,180 pt: Zcu.PerThread,
189 func_index: InternPool.Index,181 func_index: InternPool.Index,
190 air: Air,182 air: Air,
191 liveness: Liveness,183 liveness: Liveness,
192) !void {184) link.File.UpdateNavError!void {
193 const zcu = pt.zcu;185 const zcu = pt.zcu;
194 const gpa = zcu.gpa;186 const gpa = zcu.gpa;
195 const func = zcu.funcInfo(func_index);187 const func = zcu.funcInfo(func_index);
...@@ -313,7 +305,7 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void {...@@ -313,7 +305,7 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void {
313 };305 };
314}306}
315307
316pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {308pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.File.UpdateNavError!void {
317 const tracy = trace(@src());309 const tracy = trace(@src());
318 defer tracy.end();310 defer tracy.end();
319311
...@@ -390,7 +382,7 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn...@@ -390,7 +382,7 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn
390 _ = ti_id;382 _ = ti_id;
391}383}
392384
393pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {385pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
394 return self.flushModule(arena, tid, prog_node);386 return self.flushModule(arena, tid, prog_node);
395}387}
396388
...@@ -409,7 +401,7 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) {...@@ -409,7 +401,7 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) {
409 return defines;401 return defines;
410}402}
411403
412pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {404pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
413 _ = arena; // Has the same lifetime as the call to Compilation.update.405 _ = arena; // Has the same lifetime as the call to Compilation.update.
414406
415 const tracy = trace(@src());407 const tracy = trace(@src());
...@@ -419,6 +411,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -419,6 +411,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
419 defer sub_prog_node.end();411 defer sub_prog_node.end();
420412
421 const comp = self.base.comp;413 const comp = self.base.comp;
414 const diags = &comp.link_diags;
422 const gpa = comp.gpa;415 const gpa = comp.gpa;
423 const zcu = self.base.comp.zcu.?;416 const zcu = self.base.comp.zcu.?;
424 const ip = &zcu.intern_pool;417 const ip = &zcu.intern_pool;
...@@ -476,7 +469,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -476,7 +469,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
476 defer export_names.deinit(gpa);469 defer export_names.deinit(gpa);
477 try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count()));470 try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count()));
478 for (zcu.single_exports.values()) |export_index| {471 for (zcu.single_exports.values()) |export_index| {
479 export_names.putAssumeCapacity(zcu.all_exports.items[export_index].opts.name, {});472 export_names.putAssumeCapacity(export_index.ptr(zcu).opts.name, {});
480 }473 }
481 for (zcu.multi_exports.values()) |info| {474 for (zcu.multi_exports.values()) |info| {
482 try export_names.ensureUnusedCapacity(gpa, info.len);475 try export_names.ensureUnusedCapacity(gpa, info.len);
...@@ -554,8 +547,10 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -554,8 +547,10 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
554 }, self.getString(av_block.code));547 }, self.getString(av_block.code));
555548
556 const file = self.base.file.?;549 const file = self.base.file.?;
557 try file.setEndPos(f.file_size);550 file.setEndPos(f.file_size) catch |err| return diags.fail("failed to allocate file: {s}", .{@errorName(err)});
558 try file.pwritevAll(f.all_buffers.items, 0);551 file.pwritevAll(f.all_buffers.items, 0) catch |err| return diags.fail("failed to write to '{'}': {s}", .{
552 self.base.emit, @errorName(err),
553 });
559}554}
560555
561const Flush = struct {556const Flush = struct {
...@@ -845,7 +840,7 @@ pub fn updateExports(...@@ -845,7 +840,7 @@ pub fn updateExports(
845 self: *C,840 self: *C,
846 pt: Zcu.PerThread,841 pt: Zcu.PerThread,
847 exported: Zcu.Exported,842 exported: Zcu.Exported,
848 export_indices: []const u32,843 export_indices: []const Zcu.Export.Index,
849) !void {844) !void {
850 const zcu = pt.zcu;845 const zcu = pt.zcu;
851 const gpa = zcu.gpa;846 const gpa = zcu.gpa;
src/link/Coff.zig+126-85
...@@ -408,7 +408,7 @@ pub fn createEmpty(...@@ -408,7 +408,7 @@ pub fn createEmpty(
408 max_file_offset = header.pointer_to_raw_data + header.size_of_raw_data;408 max_file_offset = header.pointer_to_raw_data + header.size_of_raw_data;
409 }409 }
410 }410 }
411 try coff.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset);411 try coff.pwriteAll(&[_]u8{0}, max_file_offset);
412 }412 }
413413
414 return coff;414 return coff;
...@@ -858,7 +858,7 @@ fn writeAtom(coff: *Coff, atom_index: Atom.Index, code: []u8) !void {...@@ -858,7 +858,7 @@ fn writeAtom(coff: *Coff, atom_index: Atom.Index, code: []u8) !void {
858 }858 }
859859
860 coff.resolveRelocs(atom_index, relocs.items, code, coff.image_base);860 coff.resolveRelocs(atom_index, relocs.items, code, coff.image_base);
861 try coff.base.file.?.pwriteAll(code, file_offset);861 try coff.pwriteAll(code, file_offset);
862862
863 // Now we can mark the relocs as resolved.863 // Now we can mark the relocs as resolved.
864 while (relocs.popOrNull()) |reloc| {864 while (relocs.popOrNull()) |reloc| {
...@@ -891,7 +891,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void {...@@ -891,7 +891,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void {
891 const sect_id = coff.got_section_index.?;891 const sect_id = coff.got_section_index.?;
892892
893 if (coff.got_table_count_dirty) {893 if (coff.got_table_count_dirty) {
894 const needed_size = @as(u32, @intCast(coff.got_table.entries.items.len * coff.ptr_width.size()));894 const needed_size: u32 = @intCast(coff.got_table.entries.items.len * coff.ptr_width.size());
895 try coff.growSection(sect_id, needed_size);895 try coff.growSection(sect_id, needed_size);
896 coff.got_table_count_dirty = false;896 coff.got_table_count_dirty = false;
897 }897 }
...@@ -908,7 +908,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void {...@@ -908,7 +908,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void {
908 switch (coff.ptr_width) {908 switch (coff.ptr_width) {
909 .p32 => {909 .p32 => {
910 var buf: [4]u8 = undefined;910 var buf: [4]u8 = undefined;
911 mem.writeInt(u32, &buf, @as(u32, @intCast(entry_value + coff.image_base)), .little);911 mem.writeInt(u32, &buf, @intCast(entry_value + coff.image_base), .little);
912 try coff.base.file.?.pwriteAll(&buf, file_offset);912 try coff.base.file.?.pwriteAll(&buf, file_offset);
913 },913 },
914 .p64 => {914 .p64 => {
...@@ -1093,7 +1093,13 @@ fn freeAtom(coff: *Coff, atom_index: Atom.Index) void {...@@ -1093,7 +1093,13 @@ fn freeAtom(coff: *Coff, atom_index: Atom.Index) void {
1093 coff.getAtomPtr(atom_index).sym_index = 0;1093 coff.getAtomPtr(atom_index).sym_index = 0;
1094}1094}
10951095
1096pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {1096pub fn updateFunc(
1097 coff: *Coff,
1098 pt: Zcu.PerThread,
1099 func_index: InternPool.Index,
1100 air: Air,
1101 liveness: Liveness,
1102) link.File.UpdateNavError!void {
1097 if (build_options.skip_non_native and builtin.object_format != .coff) {1103 if (build_options.skip_non_native and builtin.object_format != .coff) {
1098 @panic("Attempted to compile for object format that was disabled by build configuration");1104 @panic("Attempted to compile for object format that was disabled by build configuration");
1099 }1105 }
...@@ -1106,34 +1112,41 @@ pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -1106,34 +1112,41 @@ pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index,
1106 const zcu = pt.zcu;1112 const zcu = pt.zcu;
1107 const gpa = zcu.gpa;1113 const gpa = zcu.gpa;
1108 const func = zcu.funcInfo(func_index);1114 const func = zcu.funcInfo(func_index);
1115 const nav_index = func.owner_nav;
11091116
1110 const atom_index = try coff.getOrCreateAtomForNav(func.owner_nav);1117 const atom_index = try coff.getOrCreateAtomForNav(nav_index);
1111 coff.freeRelocations(atom_index);1118 coff.freeRelocations(atom_index);
11121119
1113 coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?;1120 coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?;
11141121
1115 var code_buffer = std.ArrayList(u8).init(gpa);1122 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1116 defer code_buffer.deinit();1123 defer code_buffer.deinit(gpa);
11171124
1118 const res = try codegen.generateFunction(1125 codegen.generateFunction(
1119 &coff.base,1126 &coff.base,
1120 pt,1127 pt,
1121 zcu.navSrcLoc(func.owner_nav),1128 zcu.navSrcLoc(nav_index),
1122 func_index,1129 func_index,
1123 air,1130 air,
1124 liveness,1131 liveness,
1125 &code_buffer,1132 &code_buffer,
1126 .none,1133 .none,
1127 );1134 ) catch |err| switch (err) {
1128 const code = switch (res) {1135 error.CodegenFail => return error.CodegenFail,
1129 .ok => code_buffer.items,1136 error.OutOfMemory => return error.OutOfMemory,
1130 .fail => |em| {1137 error.Overflow => |e| {
1131 try zcu.failed_codegen.put(zcu.gpa, func.owner_nav, em);1138 try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
1132 return;1139 gpa,
1140 zcu.navSrcLoc(nav_index),
1141 "unable to codegen: {s}",
1142 .{@errorName(e)},
1143 ));
1144 try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index }));
1145 return error.CodegenFail;
1133 },1146 },
1134 };1147 };
11351148
1136 try coff.updateNavCode(pt, func.owner_nav, code, .FUNCTION);1149 try coff.updateNavCode(pt, nav_index, code_buffer.items, .FUNCTION);
11371150
1138 // Exports will be updated by `Zcu.processExports` after the update.1151 // Exports will be updated by `Zcu.processExports` after the update.
1139}1152}
...@@ -1154,24 +1167,21 @@ fn lowerConst(...@@ -1154,24 +1167,21 @@ fn lowerConst(
1154) !LowerConstResult {1167) !LowerConstResult {
1155 const gpa = coff.base.comp.gpa;1168 const gpa = coff.base.comp.gpa;
11561169
1157 var code_buffer = std.ArrayList(u8).init(gpa);1170 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1158 defer code_buffer.deinit();1171 defer code_buffer.deinit(gpa);
11591172
1160 const atom_index = try coff.createAtom();1173 const atom_index = try coff.createAtom();
1161 const sym = coff.getAtom(atom_index).getSymbolPtr(coff);1174 const sym = coff.getAtom(atom_index).getSymbolPtr(coff);
1162 try coff.setSymbolName(sym, name);1175 try coff.setSymbolName(sym, name);
1163 sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(sect_id + 1));1176 sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(sect_id + 1));
11641177
1165 const res = try codegen.generateSymbol(&coff.base, pt, src_loc, val, &code_buffer, .{1178 try codegen.generateSymbol(&coff.base, pt, src_loc, val, &code_buffer, .{
1166 .atom_index = coff.getAtom(atom_index).getSymbolIndex().?,1179 .atom_index = coff.getAtom(atom_index).getSymbolIndex().?,
1167 });1180 });
1168 const code = switch (res) {1181 const code = code_buffer.items;
1169 .ok => code_buffer.items,
1170 .fail => |em| return .{ .fail = em },
1171 };
11721182
1173 const atom = coff.getAtomPtr(atom_index);1183 const atom = coff.getAtomPtr(atom_index);
1174 atom.size = @as(u32, @intCast(code.len));1184 atom.size = @intCast(code.len);
1175 atom.getSymbolPtr(coff).value = try coff.allocateAtom(1185 atom.getSymbolPtr(coff).value = try coff.allocateAtom(
1176 atom_index,1186 atom_index,
1177 atom.size,1187 atom.size,
...@@ -1227,10 +1237,10 @@ pub fn updateNav(...@@ -1227,10 +1237,10 @@ pub fn updateNav(
12271237
1228 coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index);1238 coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index);
12291239
1230 var code_buffer = std.ArrayList(u8).init(gpa);1240 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1231 defer code_buffer.deinit();1241 defer code_buffer.deinit(gpa);
12321242
1233 const res = try codegen.generateSymbol(1243 try codegen.generateSymbol(
1234 &coff.base,1244 &coff.base,
1235 pt,1245 pt,
1236 zcu.navSrcLoc(nav_index),1246 zcu.navSrcLoc(nav_index),
...@@ -1238,15 +1248,8 @@ pub fn updateNav(...@@ -1238,15 +1248,8 @@ pub fn updateNav(
1238 &code_buffer,1248 &code_buffer,
1239 .{ .atom_index = atom.getSymbolIndex().? },1249 .{ .atom_index = atom.getSymbolIndex().? },
1240 );1250 );
1241 const code = switch (res) {
1242 .ok => code_buffer.items,
1243 .fail => |em| {
1244 try zcu.failed_codegen.put(gpa, nav_index, em);
1245 return;
1246 },
1247 };
12481251
1249 try coff.updateNavCode(pt, nav_index, code, .NULL);1252 try coff.updateNavCode(pt, nav_index, code_buffer.items, .NULL);
1250 }1253 }
12511254
1252 // Exports will be updated by `Zcu.processExports` after the update.1255 // Exports will be updated by `Zcu.processExports` after the update.
...@@ -1260,11 +1263,12 @@ fn updateLazySymbolAtom(...@@ -1260,11 +1263,12 @@ fn updateLazySymbolAtom(
1260 section_index: u16,1263 section_index: u16,
1261) !void {1264) !void {
1262 const zcu = pt.zcu;1265 const zcu = pt.zcu;
1263 const gpa = zcu.gpa;1266 const comp = coff.base.comp;
1267 const gpa = comp.gpa;
12641268
1265 var required_alignment: InternPool.Alignment = .none;1269 var required_alignment: InternPool.Alignment = .none;
1266 var code_buffer = std.ArrayList(u8).init(gpa);1270 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1267 defer code_buffer.deinit();1271 defer code_buffer.deinit(gpa);
12681272
1269 const name = try allocPrint(gpa, "__lazy_{s}_{}", .{1273 const name = try allocPrint(gpa, "__lazy_{s}_{}", .{
1270 @tagName(sym.kind),1274 @tagName(sym.kind),
...@@ -1276,7 +1280,7 @@ fn updateLazySymbolAtom(...@@ -1276,7 +1280,7 @@ fn updateLazySymbolAtom(
1276 const local_sym_index = atom.getSymbolIndex().?;1280 const local_sym_index = atom.getSymbolIndex().?;
12771281
1278 const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;1282 const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;
1279 const res = try codegen.generateLazySymbol(1283 try codegen.generateLazySymbol(
1280 &coff.base,1284 &coff.base,
1281 pt,1285 pt,
1282 src,1286 src,
...@@ -1286,13 +1290,7 @@ fn updateLazySymbolAtom(...@@ -1286,13 +1290,7 @@ fn updateLazySymbolAtom(
1286 .none,1290 .none,
1287 .{ .atom_index = local_sym_index },1291 .{ .atom_index = local_sym_index },
1288 );1292 );
1289 const code = switch (res) {1293 const code = code_buffer.items;
1290 .ok => code_buffer.items,
1291 .fail => |em| {
1292 log.err("{s}", .{em.msg});
1293 return error.CodegenFail;
1294 },
1295 };
12961294
1297 const code_len: u32 = @intCast(code.len);1295 const code_len: u32 = @intCast(code.len);
1298 const symbol = atom.getSymbolPtr(coff);1296 const symbol = atom.getSymbolPtr(coff);
...@@ -1387,7 +1385,7 @@ fn updateNavCode(...@@ -1387,7 +1385,7 @@ fn updateNavCode(
1387 nav_index: InternPool.Nav.Index,1385 nav_index: InternPool.Nav.Index,
1388 code: []u8,1386 code: []u8,
1389 complex_type: coff_util.ComplexType,1387 complex_type: coff_util.ComplexType,
1390) !void {1388) link.File.UpdateNavError!void {
1391 const zcu = pt.zcu;1389 const zcu = pt.zcu;
1392 const ip = &zcu.intern_pool;1390 const ip = &zcu.intern_pool;
1393 const nav = ip.getNav(nav_index);1391 const nav = ip.getNav(nav_index);
...@@ -1405,18 +1403,21 @@ fn updateNavCode(...@@ -1405,18 +1403,21 @@ fn updateNavCode(
1405 const atom = coff.getAtom(atom_index);1403 const atom = coff.getAtom(atom_index);
1406 const sym_index = atom.getSymbolIndex().?;1404 const sym_index = atom.getSymbolIndex().?;
1407 const sect_index = nav_metadata.section;1405 const sect_index = nav_metadata.section;
1408 const code_len = @as(u32, @intCast(code.len));1406 const code_len: u32 = @intCast(code.len);
14091407
1410 if (atom.size != 0) {1408 if (atom.size != 0) {
1411 const sym = atom.getSymbolPtr(coff);1409 const sym = atom.getSymbolPtr(coff);
1412 try coff.setSymbolName(sym, nav.fqn.toSlice(ip));1410 try coff.setSymbolName(sym, nav.fqn.toSlice(ip));
1413 sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(sect_index + 1));1411 sym.section_number = @enumFromInt(sect_index + 1);
1414 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };1412 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };
14151413
1416 const capacity = atom.capacity(coff);1414 const capacity = atom.capacity(coff);
1417 const need_realloc = code.len > capacity or !required_alignment.check(sym.value);1415 const need_realloc = code.len > capacity or !required_alignment.check(sym.value);
1418 if (need_realloc) {1416 if (need_realloc) {
1419 const vaddr = try coff.growAtom(atom_index, code_len, @intCast(required_alignment.toByteUnits() orelse 0));1417 const vaddr = coff.growAtom(atom_index, code_len, @intCast(required_alignment.toByteUnits() orelse 0)) catch |err| switch (err) {
1418 error.OutOfMemory => return error.OutOfMemory,
1419 else => |e| return coff.base.cgFail(nav_index, "failed to grow atom: {s}", .{@errorName(e)}),
1420 };
1420 log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), sym.value, vaddr });1421 log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), sym.value, vaddr });
1421 log.debug(" (required alignment 0x{x}", .{required_alignment});1422 log.debug(" (required alignment 0x{x}", .{required_alignment});
14221423
...@@ -1424,7 +1425,10 @@ fn updateNavCode(...@@ -1424,7 +1425,10 @@ fn updateNavCode(
1424 sym.value = vaddr;1425 sym.value = vaddr;
1425 log.debug(" (updating GOT entry)", .{});1426 log.debug(" (updating GOT entry)", .{});
1426 const got_entry_index = coff.got_table.lookup.get(.{ .sym_index = sym_index }).?;1427 const got_entry_index = coff.got_table.lookup.get(.{ .sym_index = sym_index }).?;
1427 try coff.writeOffsetTableEntry(got_entry_index);1428 coff.writeOffsetTableEntry(got_entry_index) catch |err| switch (err) {
1429 error.OutOfMemory => return error.OutOfMemory,
1430 else => |e| return coff.base.cgFail(nav_index, "failed to write offset table entry: {s}", .{@errorName(e)}),
1431 };
1428 coff.markRelocsDirtyByTarget(.{ .sym_index = sym_index });1432 coff.markRelocsDirtyByTarget(.{ .sym_index = sym_index });
1429 }1433 }
1430 } else if (code_len < atom.size) {1434 } else if (code_len < atom.size) {
...@@ -1434,26 +1438,34 @@ fn updateNavCode(...@@ -1434,26 +1438,34 @@ fn updateNavCode(
1434 } else {1438 } else {
1435 const sym = atom.getSymbolPtr(coff);1439 const sym = atom.getSymbolPtr(coff);
1436 try coff.setSymbolName(sym, nav.fqn.toSlice(ip));1440 try coff.setSymbolName(sym, nav.fqn.toSlice(ip));
1437 sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(sect_index + 1));1441 sym.section_number = @enumFromInt(sect_index + 1);
1438 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };1442 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };
14391443
1440 const vaddr = try coff.allocateAtom(atom_index, code_len, @intCast(required_alignment.toByteUnits() orelse 0));1444 const vaddr = coff.allocateAtom(atom_index, code_len, @intCast(required_alignment.toByteUnits() orelse 0)) catch |err| switch (err) {
1445 error.OutOfMemory => return error.OutOfMemory,
1446 else => |e| return coff.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(e)}),
1447 };
1441 errdefer coff.freeAtom(atom_index);1448 errdefer coff.freeAtom(atom_index);
1442 log.debug("allocated atom for {} at 0x{x}", .{ nav.fqn.fmt(ip), vaddr });1449 log.debug("allocated atom for {} at 0x{x}", .{ nav.fqn.fmt(ip), vaddr });
1443 coff.getAtomPtr(atom_index).size = code_len;1450 coff.getAtomPtr(atom_index).size = code_len;
1444 sym.value = vaddr;1451 sym.value = vaddr;
14451452
1446 try coff.addGotEntry(.{ .sym_index = sym_index });1453 coff.addGotEntry(.{ .sym_index = sym_index }) catch |err| switch (err) {
1454 error.OutOfMemory => return error.OutOfMemory,
1455 else => |e| return coff.base.cgFail(nav_index, "failed to add GOT entry: {s}", .{@errorName(e)}),
1456 };
1447 }1457 }
14481458
1449 try coff.writeAtom(atom_index, code);1459 coff.writeAtom(atom_index, code) catch |err| switch (err) {
1460 error.OutOfMemory => return error.OutOfMemory,
1461 else => |e| return coff.base.cgFail(nav_index, "failed to write atom: {s}", .{@errorName(e)}),
1462 };
1450}1463}
14511464
1452pub fn freeNav(coff: *Coff, nav_index: InternPool.NavIndex) void {1465pub fn freeNav(coff: *Coff, nav_index: InternPool.NavIndex) void {
1453 if (coff.llvm_object) |llvm_object| return llvm_object.freeNav(nav_index);1466 if (coff.llvm_object) |llvm_object| return llvm_object.freeNav(nav_index);
14541467
1455 const gpa = coff.base.comp.gpa;1468 const gpa = coff.base.comp.gpa;
1456 log.debug("freeDecl 0x{x}", .{nav_index});
14571469
1458 if (coff.decls.fetchOrderedRemove(nav_index)) |const_kv| {1470 if (coff.decls.fetchOrderedRemove(nav_index)) |const_kv| {
1459 var kv = const_kv;1471 var kv = const_kv;
...@@ -1466,7 +1478,7 @@ pub fn updateExports(...@@ -1466,7 +1478,7 @@ pub fn updateExports(
1466 coff: *Coff,1478 coff: *Coff,
1467 pt: Zcu.PerThread,1479 pt: Zcu.PerThread,
1468 exported: Zcu.Exported,1480 exported: Zcu.Exported,
1469 export_indices: []const u32,1481 export_indices: []const Zcu.Export.Index,
1470) link.File.UpdateExportsError!void {1482) link.File.UpdateExportsError!void {
1471 if (build_options.skip_non_native and builtin.object_format != .coff) {1483 if (build_options.skip_non_native and builtin.object_format != .coff) {
1472 @panic("Attempted to compile for object format that was disabled by build configuration");1484 @panic("Attempted to compile for object format that was disabled by build configuration");
...@@ -1481,7 +1493,7 @@ pub fn updateExports(...@@ -1481,7 +1493,7 @@ pub fn updateExports(
1481 // Even in the case of LLVM, we need to notice certain exported symbols in order to1493 // Even in the case of LLVM, we need to notice certain exported symbols in order to
1482 // detect the default subsystem.1494 // detect the default subsystem.
1483 for (export_indices) |export_idx| {1495 for (export_indices) |export_idx| {
1484 const exp = zcu.all_exports.items[export_idx];1496 const exp = export_idx.ptr(zcu);
1485 const exported_nav_index = switch (exp.exported) {1497 const exported_nav_index = switch (exp.exported) {
1486 .nav => |nav| nav,1498 .nav => |nav| nav,
1487 .uav => continue,1499 .uav => continue,
...@@ -1524,7 +1536,7 @@ pub fn updateExports(...@@ -1524,7 +1536,7 @@ pub fn updateExports(
1524 break :blk coff.navs.getPtr(nav).?;1536 break :blk coff.navs.getPtr(nav).?;
1525 },1537 },
1526 .uav => |uav| coff.uavs.getPtr(uav) orelse blk: {1538 .uav => |uav| coff.uavs.getPtr(uav) orelse blk: {
1527 const first_exp = zcu.all_exports.items[export_indices[0]];1539 const first_exp = export_indices[0].ptr(zcu);
1528 const res = try coff.lowerUav(pt, uav, .none, first_exp.src);1540 const res = try coff.lowerUav(pt, uav, .none, first_exp.src);
1529 switch (res) {1541 switch (res) {
1530 .mcv => {},1542 .mcv => {},
...@@ -1543,7 +1555,7 @@ pub fn updateExports(...@@ -1543,7 +1555,7 @@ pub fn updateExports(
1543 const atom = coff.getAtom(atom_index);1555 const atom = coff.getAtom(atom_index);
15441556
1545 for (export_indices) |export_idx| {1557 for (export_indices) |export_idx| {
1546 const exp = zcu.all_exports.items[export_idx];1558 const exp = export_idx.ptr(zcu);
1547 log.debug("adding new export '{}'", .{exp.opts.name.fmt(&zcu.intern_pool)});1559 log.debug("adding new export '{}'", .{exp.opts.name.fmt(&zcu.intern_pool)});
15481560
1549 if (exp.opts.section.toSlice(&zcu.intern_pool)) |section_name| {1561 if (exp.opts.section.toSlice(&zcu.intern_pool)) |section_name| {
...@@ -1671,12 +1683,17 @@ fn resolveGlobalSymbol(coff: *Coff, current: SymbolWithLoc) !void {...@@ -1671,12 +1683,17 @@ fn resolveGlobalSymbol(coff: *Coff, current: SymbolWithLoc) !void {
1671pub fn flush(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {1683pub fn flush(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
1672 const comp = coff.base.comp;1684 const comp = coff.base.comp;
1673 const use_lld = build_options.have_llvm and comp.config.use_lld;1685 const use_lld = build_options.have_llvm and comp.config.use_lld;
1686 const diags = &comp.link_diags;
1674 if (use_lld) {1687 if (use_lld) {
1675 return coff.linkWithLLD(arena, tid, prog_node);1688 return coff.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) {
1689 error.OutOfMemory => return error.OutOfMemory,
1690 error.LinkFailure => return error.LinkFailure,
1691 else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}),
1692 };
1676 }1693 }
1677 switch (comp.config.output_mode) {1694 switch (comp.config.output_mode) {
1678 .Exe, .Obj => return coff.flushModule(arena, tid, prog_node),1695 .Exe, .Obj => return coff.flushModule(arena, tid, prog_node),
1679 .Lib => return error.TODOImplementWritingLibFiles,1696 .Lib => return diags.fail("writing lib files not yet implemented for COFF", .{}),
1680 }1697 }
1681}1698}
16821699
...@@ -2207,12 +2224,16 @@ fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Director...@@ -2207,12 +2224,16 @@ fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Director
2207 return null;2224 return null;
2208}2225}
22092226
2210pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {2227pub fn flushModule(
2228 coff: *Coff,
2229 arena: Allocator,
2230 tid: Zcu.PerThread.Id,
2231 prog_node: std.Progress.Node,
2232) link.File.FlushError!void {
2211 const tracy = trace(@src());2233 const tracy = trace(@src());
2212 defer tracy.end();2234 defer tracy.end();
22132235
2214 const comp = coff.base.comp;2236 const comp = coff.base.comp;
2215 const gpa = comp.gpa;
2216 const diags = &comp.link_diags;2237 const diags = &comp.link_diags;
22172238
2218 if (coff.llvm_object) |llvm_object| {2239 if (coff.llvm_object) |llvm_object| {
...@@ -2223,8 +2244,22 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2223,8 +2244,22 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2223 const sub_prog_node = prog_node.start("COFF Flush", 0);2244 const sub_prog_node = prog_node.start("COFF Flush", 0);
2224 defer sub_prog_node.end();2245 defer sub_prog_node.end();
22252246
2247 return flushModuleInner(coff, arena, tid) catch |err| switch (err) {
2248 error.OutOfMemory => return error.OutOfMemory,
2249 error.LinkFailure => return error.LinkFailure,
2250 else => |e| return diags.fail("COFF flush failed: {s}", .{@errorName(e)}),
2251 };
2252}
2253
2254fn flushModuleInner(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id) !void {
2255 _ = arena;
2256
2257 const comp = coff.base.comp;
2258 const gpa = comp.gpa;
2259 const diags = &comp.link_diags;
2260
2226 const pt: Zcu.PerThread = .activate(2261 const pt: Zcu.PerThread = .activate(
2227 comp.zcu orelse return error.LinkingWithoutZigSourceUnimplemented,2262 comp.zcu orelse return diags.fail("linking without zig source is not yet implemented", .{}),
2228 tid,2263 tid,
2229 );2264 );
2230 defer pt.deactivate();2265 defer pt.deactivate();
...@@ -2232,24 +2267,18 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2232,24 +2267,18 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2232 if (coff.lazy_syms.getPtr(.anyerror_type)) |metadata| {2267 if (coff.lazy_syms.getPtr(.anyerror_type)) |metadata| {
2233 // Most lazy symbols can be updated on first use, but2268 // Most lazy symbols can be updated on first use, but
2234 // anyerror needs to wait for everything to be flushed.2269 // anyerror needs to wait for everything to be flushed.
2235 if (metadata.text_state != .unused) coff.updateLazySymbolAtom(2270 if (metadata.text_state != .unused) try coff.updateLazySymbolAtom(
2236 pt,2271 pt,
2237 .{ .kind = .code, .ty = .anyerror_type },2272 .{ .kind = .code, .ty = .anyerror_type },
2238 metadata.text_atom,2273 metadata.text_atom,
2239 coff.text_section_index.?,2274 coff.text_section_index.?,
2240 ) catch |err| return switch (err) {2275 );
2241 error.CodegenFail => error.FlushFailure,2276 if (metadata.rdata_state != .unused) try coff.updateLazySymbolAtom(
2242 else => |e| e,
2243 };
2244 if (metadata.rdata_state != .unused) coff.updateLazySymbolAtom(
2245 pt,2277 pt,
2246 .{ .kind = .const_data, .ty = .anyerror_type },2278 .{ .kind = .const_data, .ty = .anyerror_type },
2247 metadata.rdata_atom,2279 metadata.rdata_atom,
2248 coff.rdata_section_index.?,2280 coff.rdata_section_index.?,
2249 ) catch |err| return switch (err) {2281 );
2250 error.CodegenFail => error.FlushFailure,
2251 else => |e| e,
2252 };
2253 }2282 }
2254 for (coff.lazy_syms.values()) |*metadata| {2283 for (coff.lazy_syms.values()) |*metadata| {
2255 if (metadata.text_state != .unused) metadata.text_state = .flushed;2284 if (metadata.text_state != .unused) metadata.text_state = .flushed;
...@@ -2594,7 +2623,7 @@ fn writeBaseRelocations(coff: *Coff) !void {...@@ -2594,7 +2623,7 @@ fn writeBaseRelocations(coff: *Coff) !void {
2594 const needed_size = @as(u32, @intCast(buffer.items.len));2623 const needed_size = @as(u32, @intCast(buffer.items.len));
2595 try coff.growSection(coff.reloc_section_index.?, needed_size);2624 try coff.growSection(coff.reloc_section_index.?, needed_size);
25962625
2597 try coff.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data);2626 try coff.pwriteAll(buffer.items, header.pointer_to_raw_data);
25982627
2599 coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.BASERELOC)] = .{2628 coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.BASERELOC)] = .{
2600 .virtual_address = header.virtual_address,2629 .virtual_address = header.virtual_address,
...@@ -2727,7 +2756,7 @@ fn writeImportTables(coff: *Coff) !void {...@@ -2727,7 +2756,7 @@ fn writeImportTables(coff: *Coff) !void {
27272756
2728 assert(dll_names_offset == needed_size);2757 assert(dll_names_offset == needed_size);
27292758
2730 try coff.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data);2759 try coff.pwriteAll(buffer.items, header.pointer_to_raw_data);
27312760
2732 coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.IMPORT)] = .{2761 coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.IMPORT)] = .{
2733 .virtual_address = header.virtual_address + iat_size,2762 .virtual_address = header.virtual_address + iat_size,
...@@ -2744,17 +2773,19 @@ fn writeImportTables(coff: *Coff) !void {...@@ -2744,17 +2773,19 @@ fn writeImportTables(coff: *Coff) !void {
2744fn writeStrtab(coff: *Coff) !void {2773fn writeStrtab(coff: *Coff) !void {
2745 if (coff.strtab_offset == null) return;2774 if (coff.strtab_offset == null) return;
27462775
2776 const comp = coff.base.comp;
2777 const gpa = comp.gpa;
2778 const diags = &comp.link_diags;
2747 const allocated_size = coff.allocatedSize(coff.strtab_offset.?);2779 const allocated_size = coff.allocatedSize(coff.strtab_offset.?);
2748 const needed_size = @as(u32, @intCast(coff.strtab.buffer.items.len));2780 const needed_size: u32 = @intCast(coff.strtab.buffer.items.len);
27492781
2750 if (needed_size > allocated_size) {2782 if (needed_size > allocated_size) {
2751 coff.strtab_offset = null;2783 coff.strtab_offset = null;
2752 coff.strtab_offset = @as(u32, @intCast(coff.findFreeSpace(needed_size, @alignOf(u32))));2784 coff.strtab_offset = @intCast(coff.findFreeSpace(needed_size, @alignOf(u32)));
2753 }2785 }
27542786
2755 log.debug("writing strtab from 0x{x} to 0x{x}", .{ coff.strtab_offset.?, coff.strtab_offset.? + needed_size });2787 log.debug("writing strtab from 0x{x} to 0x{x}", .{ coff.strtab_offset.?, coff.strtab_offset.? + needed_size });
27562788
2757 const gpa = coff.base.comp.gpa;
2758 var buffer = std.ArrayList(u8).init(gpa);2789 var buffer = std.ArrayList(u8).init(gpa);
2759 defer buffer.deinit();2790 defer buffer.deinit();
2760 try buffer.ensureTotalCapacityPrecise(needed_size);2791 try buffer.ensureTotalCapacityPrecise(needed_size);
...@@ -2763,17 +2794,19 @@ fn writeStrtab(coff: *Coff) !void {...@@ -2763,17 +2794,19 @@ fn writeStrtab(coff: *Coff) !void {
2763 // we write the length of the strtab to a temporary buffer that goes to file.2794 // we write the length of the strtab to a temporary buffer that goes to file.
2764 mem.writeInt(u32, buffer.items[0..4], @as(u32, @intCast(coff.strtab.buffer.items.len)), .little);2795 mem.writeInt(u32, buffer.items[0..4], @as(u32, @intCast(coff.strtab.buffer.items.len)), .little);
27652796
2766 try coff.base.file.?.pwriteAll(buffer.items, coff.strtab_offset.?);2797 coff.pwriteAll(buffer.items, coff.strtab_offset.?) catch |err| {
2798 return diags.fail("failed to write: {s}", .{@errorName(err)});
2799 };
2767}2800}
27682801
2769fn writeSectionHeaders(coff: *Coff) !void {2802fn writeSectionHeaders(coff: *Coff) !void {
2770 const offset = coff.getSectionHeadersOffset();2803 const offset = coff.getSectionHeadersOffset();
2771 try coff.base.file.?.pwriteAll(mem.sliceAsBytes(coff.sections.items(.header)), offset);2804 try coff.pwriteAll(mem.sliceAsBytes(coff.sections.items(.header)), offset);
2772}2805}
27732806
2774fn writeDataDirectoriesHeaders(coff: *Coff) !void {2807fn writeDataDirectoriesHeaders(coff: *Coff) !void {
2775 const offset = coff.getDataDirectoryHeadersOffset();2808 const offset = coff.getDataDirectoryHeadersOffset();
2776 try coff.base.file.?.pwriteAll(mem.sliceAsBytes(&coff.data_directories), offset);2809 try coff.pwriteAll(mem.sliceAsBytes(&coff.data_directories), offset);
2777}2810}
27782811
2779fn writeHeader(coff: *Coff) !void {2812fn writeHeader(coff: *Coff) !void {
...@@ -2913,7 +2946,7 @@ fn writeHeader(coff: *Coff) !void {...@@ -2913,7 +2946,7 @@ fn writeHeader(coff: *Coff) !void {
2913 },2946 },
2914 }2947 }
29152948
2916 try coff.base.file.?.pwriteAll(buffer.items, 0);2949 try coff.pwriteAll(buffer.items, 0);
2917}2950}
29182951
2919pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {2952pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
...@@ -3710,6 +3743,14 @@ const ImportTable = struct {...@@ -3710,6 +3743,14 @@ const ImportTable = struct {
3710 const ImportIndex = u32;3743 const ImportIndex = u32;
3711};3744};
37123745
3746fn pwriteAll(coff: *Coff, bytes: []const u8, offset: u64) error{LinkFailure}!void {
3747 const comp = coff.base.comp;
3748 const diags = &comp.link_diags;
3749 coff.base.file.?.pwriteAll(bytes, offset) catch |err| {
3750 return diags.fail("failed to write: {s}", .{@errorName(err)});
3751 };
3752}
3753
3713const Coff = @This();3754const Coff = @This();
37143755
3715const std = @import("std");3756const std = @import("std");
src/link/Dwarf.zig+35-15
...@@ -21,7 +21,6 @@ debug_rnglists: DebugRngLists,...@@ -21,7 +21,6 @@ debug_rnglists: DebugRngLists,
21debug_str: StringSection,21debug_str: StringSection,
2222
23pub const UpdateError = error{23pub const UpdateError = error{
24 CodegenFail,
25 ReinterpretDeclRef,24 ReinterpretDeclRef,
26 Unimplemented,25 Unimplemented,
27 OutOfMemory,26 OutOfMemory,
...@@ -451,7 +450,6 @@ pub const Section = struct {...@@ -451,7 +450,6 @@ pub const Section = struct {
451 const zo = elf_file.zigObjectPtr().?;450 const zo = elf_file.zigObjectPtr().?;
452 const atom = zo.symbol(sec.index).atom(elf_file).?;451 const atom = zo.symbol(sec.index).atom(elf_file).?;
453 if (atom.prevAtom(elf_file)) |_| {452 if (atom.prevAtom(elf_file)) |_| {
454 // FIXME:JK trimming/shrinking has to be reworked on ZigObject/Elf level
455 atom.value += len;453 atom.value += len;
456 } else {454 } else {
457 const shdr = &elf_file.sections.items(.shdr)[atom.output_section_index];455 const shdr = &elf_file.sections.items(.shdr)[atom.output_section_index];
...@@ -600,12 +598,13 @@ const Unit = struct {...@@ -600,12 +598,13 @@ const Unit = struct {
600598
601 fn move(unit: *Unit, sec: *Section, dwarf: *Dwarf, new_off: u32) UpdateError!void {599 fn move(unit: *Unit, sec: *Section, dwarf: *Dwarf, new_off: u32) UpdateError!void {
602 if (unit.off == new_off) return;600 if (unit.off == new_off) return;
603 if (try dwarf.getFile().?.copyRangeAll(601 const n = try dwarf.getFile().?.copyRangeAll(
604 sec.off(dwarf) + unit.off,602 sec.off(dwarf) + unit.off,
605 dwarf.getFile().?,603 dwarf.getFile().?,
606 sec.off(dwarf) + new_off,604 sec.off(dwarf) + new_off,
607 unit.len,605 unit.len,
608 ) != unit.len) return error.InputOutput;606 );
607 if (n != unit.len) return error.InputOutput;
609 unit.off = new_off;608 unit.off = new_off;
610 }609 }
611610
...@@ -1891,19 +1890,16 @@ pub const WipNav = struct {...@@ -1891,19 +1890,16 @@ pub const WipNav = struct {
1891 const bytes = if (ty.hasRuntimeBits(wip_nav.pt.zcu)) ty.abiSize(wip_nav.pt.zcu) else 0;1890 const bytes = if (ty.hasRuntimeBits(wip_nav.pt.zcu)) ty.abiSize(wip_nav.pt.zcu) else 0;
1892 try uleb128(diw, bytes);1891 try uleb128(diw, bytes);
1893 if (bytes == 0) return;1892 if (bytes == 0) return;
1894 var dim = wip_nav.debug_info.toManaged(wip_nav.dwarf.gpa);1893 const old_len = wip_nav.debug_info.items.len;
1895 defer wip_nav.debug_info = dim.moveToUnmanaged();1894 try codegen.generateSymbol(
1896 switch (try codegen.generateSymbol(
1897 wip_nav.dwarf.bin_file,1895 wip_nav.dwarf.bin_file,
1898 wip_nav.pt,1896 wip_nav.pt,
1899 src_loc,1897 src_loc,
1900 val,1898 val,
1901 &dim,1899 &wip_nav.debug_info,
1902 .{ .debug_output = .{ .dwarf = wip_nav } },1900 .{ .debug_output = .{ .dwarf = wip_nav } },
1903 )) {1901 );
1904 .ok => assert(dim.items.len == wip_nav.debug_info.items.len + bytes),1902 assert(old_len + bytes == wip_nav.debug_info.items.len);
1905 .fail => unreachable,
1906 }
1907 }1903 }
19081904
1909 const AbbrevCodeForForm = struct {1905 const AbbrevCodeForForm = struct {
...@@ -2278,7 +2274,7 @@ pub fn deinit(dwarf: *Dwarf) void {...@@ -2278,7 +2274,7 @@ pub fn deinit(dwarf: *Dwarf) void {
2278 dwarf.* = undefined;2274 dwarf.* = undefined;
2279}2275}
22802276
2281fn getUnit(dwarf: *Dwarf, mod: *Module) UpdateError!Unit.Index {2277fn getUnit(dwarf: *Dwarf, mod: *Module) !Unit.Index {
2282 const mod_gop = try dwarf.mods.getOrPut(dwarf.gpa, mod);2278 const mod_gop = try dwarf.mods.getOrPut(dwarf.gpa, mod);
2283 const unit: Unit.Index = @enumFromInt(mod_gop.index);2279 const unit: Unit.Index = @enumFromInt(mod_gop.index);
2284 if (!mod_gop.found_existing) {2280 if (!mod_gop.found_existing) {
...@@ -2338,7 +2334,24 @@ fn getModInfo(dwarf: *Dwarf, unit: Unit.Index) *ModInfo {...@@ -2338,7 +2334,24 @@ fn getModInfo(dwarf: *Dwarf, unit: Unit.Index) *ModInfo {
2338 return &dwarf.mods.values()[@intFromEnum(unit)];2334 return &dwarf.mods.values()[@intFromEnum(unit)];
2339}2335}
23402336
2341pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, sym_index: u32) UpdateError!?WipNav {2337pub fn initWipNav(
2338 dwarf: *Dwarf,
2339 pt: Zcu.PerThread,
2340 nav_index: InternPool.Nav.Index,
2341 sym_index: u32,
2342) error{ OutOfMemory, CodegenFail }!?WipNav {
2343 return initWipNavInner(dwarf, pt, nav_index, sym_index) catch |err| switch (err) {
2344 error.OutOfMemory => return error.OutOfMemory,
2345 else => |e| return pt.zcu.codegenFail(nav_index, "failed to init dwarf: {s}", .{@errorName(e)}),
2346 };
2347}
2348
2349fn initWipNavInner(
2350 dwarf: *Dwarf,
2351 pt: Zcu.PerThread,
2352 nav_index: InternPool.Nav.Index,
2353 sym_index: u32,
2354) !?WipNav {
2342 const zcu = pt.zcu;2355 const zcu = pt.zcu;
2343 const ip = &zcu.intern_pool;2356 const ip = &zcu.intern_pool;
23442357
...@@ -2667,7 +2680,14 @@ pub fn finishWipNav(...@@ -2667,7 +2680,14 @@ pub fn finishWipNav(
2667 try wip_nav.updateLazy(zcu.navSrcLoc(nav_index));2680 try wip_nav.updateLazy(zcu.navSrcLoc(nav_index));
2668}2681}
26692682
2670pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateError!void {2683pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{ OutOfMemory, CodegenFail }!void {
2684 return updateComptimeNavInner(dwarf, pt, nav_index) catch |err| switch (err) {
2685 error.OutOfMemory => return error.OutOfMemory,
2686 else => |e| return pt.zcu.codegenFail(nav_index, "failed to update dwarf: {s}", .{@errorName(e)}),
2687 };
2688}
2689
2690fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
2671 const zcu = pt.zcu;2691 const zcu = pt.zcu;
2672 const ip = &zcu.intern_pool;2692 const ip = &zcu.intern_pool;
2673 const nav_src_loc = zcu.navSrcLoc(nav_index);2693 const nav_src_loc = zcu.navSrcLoc(nav_index);
src/link/Elf.zig+124-69
...@@ -795,9 +795,15 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {...@@ -795,9 +795,15 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {
795}795}
796796
797pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {797pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
798 const use_lld = build_options.have_llvm and self.base.comp.config.use_lld;798 const comp = self.base.comp;
799 const use_lld = build_options.have_llvm and comp.config.use_lld;
800 const diags = &comp.link_diags;
799 if (use_lld) {801 if (use_lld) {
800 return self.linkWithLLD(arena, tid, prog_node);802 return self.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) {
803 error.OutOfMemory => return error.OutOfMemory,
804 error.LinkFailure => return error.LinkFailure,
805 else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}),
806 };
801 }807 }
802 try self.flushModule(arena, tid, prog_node);808 try self.flushModule(arena, tid, prog_node);
803}809}
...@@ -807,7 +813,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -807,7 +813,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
807 defer tracy.end();813 defer tracy.end();
808814
809 const comp = self.base.comp;815 const comp = self.base.comp;
810 const gpa = comp.gpa;
811 const diags = &comp.link_diags;816 const diags = &comp.link_diags;
812817
813 if (self.llvm_object) |llvm_object| {818 if (self.llvm_object) |llvm_object| {
...@@ -821,6 +826,18 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -821,6 +826,18 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
821 const sub_prog_node = prog_node.start("ELF Flush", 0);826 const sub_prog_node = prog_node.start("ELF Flush", 0);
822 defer sub_prog_node.end();827 defer sub_prog_node.end();
823828
829 return flushModuleInner(self, arena, tid) catch |err| switch (err) {
830 error.OutOfMemory => return error.OutOfMemory,
831 error.LinkFailure => return error.LinkFailure,
832 else => |e| return diags.fail("ELF flush failed: {s}", .{@errorName(e)}),
833 };
834}
835
836fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
837 const comp = self.base.comp;
838 const gpa = comp.gpa;
839 const diags = &comp.link_diags;
840
824 const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{841 const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{
825 .root_dir = self.base.emit.root_dir,842 .root_dir = self.base.emit.root_dir,
826 .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname|843 .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname|
...@@ -842,12 +859,12 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -842,12 +859,12 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
842 .Exe => {},859 .Exe => {},
843 }860 }
844861
845 if (diags.hasErrors()) return error.FlushFailure;862 if (diags.hasErrors()) return error.LinkFailure;
846863
847 // If we haven't already, create a linker-generated input file comprising of864 // If we haven't already, create a linker-generated input file comprising of
848 // linker-defined synthetic symbols only such as `_DYNAMIC`, etc.865 // linker-defined synthetic symbols only such as `_DYNAMIC`, etc.
849 if (self.linker_defined_index == null) {866 if (self.linker_defined_index == null) {
850 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));867 const index: File.Index = @intCast(try self.files.addOne(gpa));
851 self.files.set(index, .{ .linker_defined = .{ .index = index } });868 self.files.set(index, .{ .linker_defined = .{ .index = index } });
852 self.linker_defined_index = index;869 self.linker_defined_index = index;
853 const object = self.linkerDefinedPtr().?;870 const object = self.linkerDefinedPtr().?;
...@@ -878,7 +895,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -878,7 +895,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
878 }895 }
879896
880 self.checkDuplicates() catch |err| switch (err) {897 self.checkDuplicates() catch |err| switch (err) {
881 error.HasDuplicates => return error.FlushFailure,898 error.HasDuplicates => return error.LinkFailure,
882 else => |e| return e,899 else => |e| return e,
883 };900 };
884901
...@@ -956,14 +973,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -956,14 +973,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
956 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,973 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
957 error.UnsupportedCpuArch => {974 error.UnsupportedCpuArch => {
958 try self.reportUnsupportedCpuArch();975 try self.reportUnsupportedCpuArch();
959 return error.FlushFailure;976 return error.LinkFailure;
960 },977 },
961 else => |e| return e,978 else => |e| return e,
962 };979 };
963 try self.base.file.?.pwriteAll(code, file_offset);980 try self.pwriteAll(code, file_offset);
964 }981 }
965982
966 if (has_reloc_errors) return error.FlushFailure;983 if (has_reloc_errors) return error.LinkFailure;
967 }984 }
968985
969 try self.writePhdrTable();986 try self.writePhdrTable();
...@@ -972,10 +989,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -972,10 +989,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
972 try self.writeMergeSections();989 try self.writeMergeSections();
973990
974 self.writeSyntheticSections() catch |err| switch (err) {991 self.writeSyntheticSections() catch |err| switch (err) {
975 error.RelocFailure => return error.FlushFailure,992 error.RelocFailure => return error.LinkFailure,
976 error.UnsupportedCpuArch => {993 error.UnsupportedCpuArch => {
977 try self.reportUnsupportedCpuArch();994 try self.reportUnsupportedCpuArch();
978 return error.FlushFailure;995 return error.LinkFailure;
979 },996 },
980 else => |e| return e,997 else => |e| return e,
981 };998 };
...@@ -989,7 +1006,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -989,7 +1006,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
989 try self.writeElfHeader();1006 try self.writeElfHeader();
990 }1007 }
9911008
992 if (diags.hasErrors()) return error.FlushFailure;1009 if (diags.hasErrors()) return error.LinkFailure;
993}1010}
9941011
995fn dumpArgvInit(self: *Elf, arena: Allocator) !void {1012fn dumpArgvInit(self: *Elf, arena: Allocator) !void {
...@@ -1389,7 +1406,7 @@ fn scanRelocs(self: *Elf) !void {...@@ -1389,7 +1406,7 @@ fn scanRelocs(self: *Elf) !void {
1389 error.RelaxFailure => unreachable,1406 error.RelaxFailure => unreachable,
1390 error.UnsupportedCpuArch => {1407 error.UnsupportedCpuArch => {
1391 try self.reportUnsupportedCpuArch();1408 try self.reportUnsupportedCpuArch();
1392 return error.FlushFailure;1409 return error.LinkFailure;
1393 },1410 },
1394 error.RelocFailure => has_reloc_errors = true,1411 error.RelocFailure => has_reloc_errors = true,
1395 else => |e| return e,1412 else => |e| return e,
...@@ -1400,7 +1417,7 @@ fn scanRelocs(self: *Elf) !void {...@@ -1400,7 +1417,7 @@ fn scanRelocs(self: *Elf) !void {
1400 error.RelaxFailure => unreachable,1417 error.RelaxFailure => unreachable,
1401 error.UnsupportedCpuArch => {1418 error.UnsupportedCpuArch => {
1402 try self.reportUnsupportedCpuArch();1419 try self.reportUnsupportedCpuArch();
1403 return error.FlushFailure;1420 return error.LinkFailure;
1404 },1421 },
1405 error.RelocFailure => has_reloc_errors = true,1422 error.RelocFailure => has_reloc_errors = true,
1406 else => |e| return e,1423 else => |e| return e,
...@@ -1409,7 +1426,7 @@ fn scanRelocs(self: *Elf) !void {...@@ -1409,7 +1426,7 @@ fn scanRelocs(self: *Elf) !void {
14091426
1410 try self.reportUndefinedSymbols(&undefs);1427 try self.reportUndefinedSymbols(&undefs);
14111428
1412 if (has_reloc_errors) return error.FlushFailure;1429 if (has_reloc_errors) return error.LinkFailure;
14131430
1414 if (self.zigObjectPtr()) |zo| {1431 if (self.zigObjectPtr()) |zo| {
1415 try zo.asFile().createSymbolIndirection(self);1432 try zo.asFile().createSymbolIndirection(self);
...@@ -2117,7 +2134,7 @@ pub fn writeShdrTable(self: *Elf) !void {...@@ -2117,7 +2134,7 @@ pub fn writeShdrTable(self: *Elf) !void {
2117 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);2134 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
2118 }2135 }
2119 }2136 }
2120 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);2137 try self.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
2121 },2138 },
2122 .p64 => {2139 .p64 => {
2123 const buf = try gpa.alloc(elf.Elf64_Shdr, self.sections.items(.shdr).len);2140 const buf = try gpa.alloc(elf.Elf64_Shdr, self.sections.items(.shdr).len);
...@@ -2130,7 +2147,7 @@ pub fn writeShdrTable(self: *Elf) !void {...@@ -2130,7 +2147,7 @@ pub fn writeShdrTable(self: *Elf) !void {
2130 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);2147 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
2131 }2148 }
2132 }2149 }
2133 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);2150 try self.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
2134 },2151 },
2135 }2152 }
2136}2153}
...@@ -2157,7 +2174,7 @@ fn writePhdrTable(self: *Elf) !void {...@@ -2157,7 +2174,7 @@ fn writePhdrTable(self: *Elf) !void {
2157 mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);2174 mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);
2158 }2175 }
2159 }2176 }
2160 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);2177 try self.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
2161 },2178 },
2162 .p64 => {2179 .p64 => {
2163 const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len);2180 const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len);
...@@ -2169,7 +2186,7 @@ fn writePhdrTable(self: *Elf) !void {...@@ -2169,7 +2186,7 @@ fn writePhdrTable(self: *Elf) !void {
2169 mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);2186 mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);
2170 }2187 }
2171 }2188 }
2172 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);2189 try self.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
2173 },2190 },
2174 }2191 }
2175}2192}
...@@ -2319,7 +2336,7 @@ pub fn writeElfHeader(self: *Elf) !void {...@@ -2319,7 +2336,7 @@ pub fn writeElfHeader(self: *Elf) !void {
23192336
2320 assert(index == e_ehsize);2337 assert(index == e_ehsize);
23212338
2322 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);2339 try self.pwriteAll(hdr_buf[0..index], 0);
2323}2340}
23242341
2325pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void {2342pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void {
...@@ -2327,7 +2344,13 @@ pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void {...@@ -2327,7 +2344,13 @@ pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void {
2327 return self.zigObjectPtr().?.freeNav(self, nav);2344 return self.zigObjectPtr().?.freeNav(self, nav);
2328}2345}
23292346
2330pub fn updateFunc(self: *Elf, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {2347pub fn updateFunc(
2348 self: *Elf,
2349 pt: Zcu.PerThread,
2350 func_index: InternPool.Index,
2351 air: Air,
2352 liveness: Liveness,
2353) link.File.UpdateNavError!void {
2331 if (build_options.skip_non_native and builtin.object_format != .elf) {2354 if (build_options.skip_non_native and builtin.object_format != .elf) {
2332 @panic("Attempted to compile for object format that was disabled by build configuration");2355 @panic("Attempted to compile for object format that was disabled by build configuration");
2333 }2356 }
...@@ -2351,19 +2374,32 @@ pub fn updateContainerType(...@@ -2351,19 +2374,32 @@ pub fn updateContainerType(
2351 self: *Elf,2374 self: *Elf,
2352 pt: Zcu.PerThread,2375 pt: Zcu.PerThread,
2353 ty: InternPool.Index,2376 ty: InternPool.Index,
2354) link.File.UpdateNavError!void {2377) link.File.UpdateContainerTypeError!void {
2355 if (build_options.skip_non_native and builtin.object_format != .elf) {2378 if (build_options.skip_non_native and builtin.object_format != .elf) {
2356 @panic("Attempted to compile for object format that was disabled by build configuration");2379 @panic("Attempted to compile for object format that was disabled by build configuration");
2357 }2380 }
2358 if (self.llvm_object) |_| return;2381 if (self.llvm_object) |_| return;
2359 return self.zigObjectPtr().?.updateContainerType(pt, ty);2382 const zcu = pt.zcu;
2383 const gpa = zcu.gpa;
2384 return self.zigObjectPtr().?.updateContainerType(pt, ty) catch |err| switch (err) {
2385 error.OutOfMemory => return error.OutOfMemory,
2386 else => |e| {
2387 try zcu.failed_types.putNoClobber(gpa, ty, try Zcu.ErrorMsg.create(
2388 gpa,
2389 zcu.typeSrcLoc(ty),
2390 "failed to update container type: {s}",
2391 .{@errorName(e)},
2392 ));
2393 return error.TypeFailureReported;
2394 },
2395 };
2360}2396}
23612397
2362pub fn updateExports(2398pub fn updateExports(
2363 self: *Elf,2399 self: *Elf,
2364 pt: Zcu.PerThread,2400 pt: Zcu.PerThread,
2365 exported: Zcu.Exported,2401 exported: Zcu.Exported,
2366 export_indices: []const u32,2402 export_indices: []const Zcu.Export.Index,
2367) link.File.UpdateExportsError!void {2403) link.File.UpdateExportsError!void {
2368 if (build_options.skip_non_native and builtin.object_format != .elf) {2404 if (build_options.skip_non_native and builtin.object_format != .elf) {
2369 @panic("Attempted to compile for object format that was disabled by build configuration");2405 @panic("Attempted to compile for object format that was disabled by build configuration");
...@@ -2441,7 +2477,7 @@ pub fn resolveMergeSections(self: *Elf) !void {...@@ -2441,7 +2477,7 @@ pub fn resolveMergeSections(self: *Elf) !void {
2441 };2477 };
2442 }2478 }
24432479
2444 if (has_errors) return error.FlushFailure;2480 if (has_errors) return error.LinkFailure;
24452481
2446 for (self.objects.items) |index| {2482 for (self.objects.items) |index| {
2447 const object = self.file(index).?.object;2483 const object = self.file(index).?.object;
...@@ -2491,8 +2527,8 @@ pub fn writeMergeSections(self: *Elf) !void {...@@ -2491,8 +2527,8 @@ pub fn writeMergeSections(self: *Elf) !void {
24912527
2492 for (self.merge_sections.items) |*msec| {2528 for (self.merge_sections.items) |*msec| {
2493 const shdr = self.sections.items(.shdr)[msec.output_section_index];2529 const shdr = self.sections.items(.shdr)[msec.output_section_index];
2494 const fileoff = math.cast(usize, msec.value + shdr.sh_offset) orelse return error.Overflow;2530 const fileoff = try self.cast(usize, msec.value + shdr.sh_offset);
2495 const size = math.cast(usize, msec.size) orelse return error.Overflow;2531 const size = try self.cast(usize, msec.size);
2496 try buffer.ensureTotalCapacity(size);2532 try buffer.ensureTotalCapacity(size);
2497 buffer.appendNTimesAssumeCapacity(0, size);2533 buffer.appendNTimesAssumeCapacity(0, size);
24982534
...@@ -2500,11 +2536,11 @@ pub fn writeMergeSections(self: *Elf) !void {...@@ -2500,11 +2536,11 @@ pub fn writeMergeSections(self: *Elf) !void {
2500 const msub = msec.mergeSubsection(msub_index);2536 const msub = msec.mergeSubsection(msub_index);
2501 assert(msub.alive);2537 assert(msub.alive);
2502 const string = msub.getString(self);2538 const string = msub.getString(self);
2503 const off = math.cast(usize, msub.value) orelse return error.Overflow;2539 const off = try self.cast(usize, msub.value);
2504 @memcpy(buffer.items[off..][0..string.len], string);2540 @memcpy(buffer.items[off..][0..string.len], string);
2505 }2541 }
25062542
2507 try self.base.file.?.pwriteAll(buffer.items, fileoff);2543 try self.pwriteAll(buffer.items, fileoff);
2508 buffer.clearRetainingCapacity();2544 buffer.clearRetainingCapacity();
2509 }2545 }
2510}2546}
...@@ -3121,9 +3157,6 @@ pub fn sortShdrs(...@@ -3121,9 +3157,6 @@ pub fn sortShdrs(
3121 fileLookup(files, ref.file, zig_object_ptr).?.atom(ref.index).?.output_section_index = atom_list.output_section_index;3157 fileLookup(files, ref.file, zig_object_ptr).?.atom(ref.index).?.output_section_index = atom_list.output_section_index;
3122 }3158 }
3123 if (shdr.sh_type == elf.SHT_RELA) {3159 if (shdr.sh_type == elf.SHT_RELA) {
3124 // FIXME:JK we should spin up .symtab potentially earlier, or set all non-dynamic RELA sections
3125 // to point at symtab
3126 // shdr.sh_link = backlinks[shdr.sh_link];
3127 shdr.sh_link = section_indexes.symtab.?;3160 shdr.sh_link = section_indexes.symtab.?;
3128 shdr.sh_info = backlinks[shdr.sh_info];3161 shdr.sh_info = backlinks[shdr.sh_info];
3129 }3162 }
...@@ -3211,7 +3244,7 @@ fn updateSectionSizes(self: *Elf) !void {...@@ -3211,7 +3244,7 @@ fn updateSectionSizes(self: *Elf) !void {
3211 atom_list.dirty = false;3244 atom_list.dirty = false;
3212 }3245 }
32133246
3214 // FIXME:JK this will hopefully not be needed once we create a link from Atom/Thunk to AtomList.3247 // This might not be needed if there was a link from Atom/Thunk to AtomList.
3215 for (self.thunks.items) |*th| {3248 for (self.thunks.items) |*th| {
3216 th.value += slice.items(.atom_list_2)[th.output_section_index].value;3249 th.value += slice.items(.atom_list_2)[th.output_section_index].value;
3217 }3250 }
...@@ -3297,7 +3330,6 @@ fn updateSectionSizes(self: *Elf) !void {...@@ -3297,7 +3330,6 @@ fn updateSectionSizes(self: *Elf) !void {
3297 self.updateShStrtabSize();3330 self.updateShStrtabSize();
3298}3331}
32993332
3300// FIXME:JK this is very much obsolete, remove!
3301pub fn updateShStrtabSize(self: *Elf) void {3333pub fn updateShStrtabSize(self: *Elf) void {
3302 if (self.section_indexes.shstrtab) |index| {3334 if (self.section_indexes.shstrtab) |index| {
3303 self.sections.items(.shdr)[index].sh_size = self.shstrtab.items.len;3335 self.sections.items(.shdr)[index].sh_size = self.shstrtab.items.len;
...@@ -3362,7 +3394,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {...@@ -3362,7 +3394,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
3362 // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op3394 // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op
3363 var err = try diags.addErrorWithNotes(1);3395 var err = try diags.addErrorWithNotes(1);
3364 try err.addMsg("fatal linker error: not enough space reserved for EHDR and PHDR table", .{});3396 try err.addMsg("fatal linker error: not enough space reserved for EHDR and PHDR table", .{});
3365 try err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space });3397 err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space });
3366 }3398 }
33673399
3368 phdr_table_load.p_filesz = needed_size + ehsize;3400 phdr_table_load.p_filesz = needed_size + ehsize;
...@@ -3658,7 +3690,7 @@ fn writeAtoms(self: *Elf) !void {...@@ -3658,7 +3690,7 @@ fn writeAtoms(self: *Elf) !void {
3658 atom_list.write(&buffer, &undefs, self) catch |err| switch (err) {3690 atom_list.write(&buffer, &undefs, self) catch |err| switch (err) {
3659 error.UnsupportedCpuArch => {3691 error.UnsupportedCpuArch => {
3660 try self.reportUnsupportedCpuArch();3692 try self.reportUnsupportedCpuArch();
3661 return error.FlushFailure;3693 return error.LinkFailure;
3662 },3694 },
3663 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,3695 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
3664 else => |e| return e,3696 else => |e| return e,
...@@ -3666,7 +3698,7 @@ fn writeAtoms(self: *Elf) !void {...@@ -3666,7 +3698,7 @@ fn writeAtoms(self: *Elf) !void {
3666 }3698 }
36673699
3668 try self.reportUndefinedSymbols(&undefs);3700 try self.reportUndefinedSymbols(&undefs);
3669 if (has_reloc_errors) return error.FlushFailure;3701 if (has_reloc_errors) return error.LinkFailure;
36703702
3671 if (self.requiresThunks()) {3703 if (self.requiresThunks()) {
3672 for (self.thunks.items) |th| {3704 for (self.thunks.items) |th| {
...@@ -3676,7 +3708,7 @@ fn writeAtoms(self: *Elf) !void {...@@ -3676,7 +3708,7 @@ fn writeAtoms(self: *Elf) !void {
3676 const offset = @as(u64, @intCast(th.value)) + shdr.sh_offset;3708 const offset = @as(u64, @intCast(th.value)) + shdr.sh_offset;
3677 try th.write(self, buffer.writer());3709 try th.write(self, buffer.writer());
3678 assert(buffer.items.len == thunk_size);3710 assert(buffer.items.len == thunk_size);
3679 try self.base.file.?.pwriteAll(buffer.items, offset);3711 try self.pwriteAll(buffer.items, offset);
3680 buffer.clearRetainingCapacity();3712 buffer.clearRetainingCapacity();
3681 }3713 }
3682 }3714 }
...@@ -3784,12 +3816,12 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -3784,12 +3816,12 @@ fn writeSyntheticSections(self: *Elf) !void {
3784 const contents = buffer[0 .. interp.len + 1];3816 const contents = buffer[0 .. interp.len + 1];
3785 const shdr = slice.items(.shdr)[shndx];3817 const shdr = slice.items(.shdr)[shndx];
3786 assert(shdr.sh_size == contents.len);3818 assert(shdr.sh_size == contents.len);
3787 try self.base.file.?.pwriteAll(contents, shdr.sh_offset);3819 try self.pwriteAll(contents, shdr.sh_offset);
3788 }3820 }
37893821
3790 if (self.section_indexes.hash) |shndx| {3822 if (self.section_indexes.hash) |shndx| {
3791 const shdr = slice.items(.shdr)[shndx];3823 const shdr = slice.items(.shdr)[shndx];
3792 try self.base.file.?.pwriteAll(self.hash.buffer.items, shdr.sh_offset);3824 try self.pwriteAll(self.hash.buffer.items, shdr.sh_offset);
3793 }3825 }
37943826
3795 if (self.section_indexes.gnu_hash) |shndx| {3827 if (self.section_indexes.gnu_hash) |shndx| {
...@@ -3797,12 +3829,12 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -3797,12 +3829,12 @@ fn writeSyntheticSections(self: *Elf) !void {
3797 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.gnu_hash.size());3829 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.gnu_hash.size());
3798 defer buffer.deinit();3830 defer buffer.deinit();
3799 try self.gnu_hash.write(self, buffer.writer());3831 try self.gnu_hash.write(self, buffer.writer());
3800 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);3832 try self.pwriteAll(buffer.items, shdr.sh_offset);
3801 }3833 }
38023834
3803 if (self.section_indexes.versym) |shndx| {3835 if (self.section_indexes.versym) |shndx| {
3804 const shdr = slice.items(.shdr)[shndx];3836 const shdr = slice.items(.shdr)[shndx];
3805 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.versym.items), shdr.sh_offset);3837 try self.pwriteAll(mem.sliceAsBytes(self.versym.items), shdr.sh_offset);
3806 }3838 }
38073839
3808 if (self.section_indexes.verneed) |shndx| {3840 if (self.section_indexes.verneed) |shndx| {
...@@ -3810,7 +3842,7 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -3810,7 +3842,7 @@ fn writeSyntheticSections(self: *Elf) !void {
3810 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.verneed.size());3842 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.verneed.size());
3811 defer buffer.deinit();3843 defer buffer.deinit();
3812 try self.verneed.write(buffer.writer());3844 try self.verneed.write(buffer.writer());
3813 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);3845 try self.pwriteAll(buffer.items, shdr.sh_offset);
3814 }3846 }
38153847
3816 if (self.section_indexes.dynamic) |shndx| {3848 if (self.section_indexes.dynamic) |shndx| {
...@@ -3818,7 +3850,7 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -3818,7 +3850,7 @@ fn writeSyntheticSections(self: *Elf) !void {
3818 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynamic.size(self));3850 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynamic.size(self));
3819 defer buffer.deinit();3851 defer buffer.deinit();
3820 try self.dynamic.write(self, buffer.writer());3852 try self.dynamic.write(self, buffer.writer());
3821 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);3853 try self.pwriteAll(buffer.items, shdr.sh_offset);
3822 }3854 }
38233855
3824 if (self.section_indexes.dynsymtab) |shndx| {3856 if (self.section_indexes.dynsymtab) |shndx| {
...@@ -3826,12 +3858,12 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -3826,12 +3858,12 @@ fn writeSyntheticSections(self: *Elf) !void {
3826 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynsym.size());3858 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynsym.size());
3827 defer buffer.deinit();3859 defer buffer.deinit();
3828 try self.dynsym.write(self, buffer.writer());3860 try self.dynsym.write(self, buffer.writer());
3829 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);3861 try self.pwriteAll(buffer.items, shdr.sh_offset);
3830 }3862 }
38313863
3832 if (self.section_indexes.dynstrtab) |shndx| {3864 if (self.section_indexes.dynstrtab) |shndx| {
3833 const shdr = slice.items(.shdr)[shndx];3865 const shdr = slice.items(.shdr)[shndx];
3834 try self.base.file.?.pwriteAll(self.dynstrtab.items, shdr.sh_offset);3866 try self.pwriteAll(self.dynstrtab.items, shdr.sh_offset);
3835 }3867 }
38363868
3837 if (self.section_indexes.eh_frame) |shndx| {3869 if (self.section_indexes.eh_frame) |shndx| {
...@@ -3841,21 +3873,21 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -3841,21 +3873,21 @@ fn writeSyntheticSections(self: *Elf) !void {
3841 break :existing_size sym.atom(self).?.size;3873 break :existing_size sym.atom(self).?.size;
3842 };3874 };
3843 const shdr = slice.items(.shdr)[shndx];3875 const shdr = slice.items(.shdr)[shndx];
3844 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;3876 const sh_size = try self.cast(usize, shdr.sh_size);
3845 var buffer = try std.ArrayList(u8).initCapacity(gpa, @intCast(sh_size - existing_size));3877 var buffer = try std.ArrayList(u8).initCapacity(gpa, @intCast(sh_size - existing_size));
3846 defer buffer.deinit();3878 defer buffer.deinit();
3847 try eh_frame.writeEhFrame(self, buffer.writer());3879 try eh_frame.writeEhFrame(self, buffer.writer());
3848 assert(buffer.items.len == sh_size - existing_size);3880 assert(buffer.items.len == sh_size - existing_size);
3849 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset + existing_size);3881 try self.pwriteAll(buffer.items, shdr.sh_offset + existing_size);
3850 }3882 }
38513883
3852 if (self.section_indexes.eh_frame_hdr) |shndx| {3884 if (self.section_indexes.eh_frame_hdr) |shndx| {
3853 const shdr = slice.items(.shdr)[shndx];3885 const shdr = slice.items(.shdr)[shndx];
3854 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;3886 const sh_size = try self.cast(usize, shdr.sh_size);
3855 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);3887 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
3856 defer buffer.deinit();3888 defer buffer.deinit();
3857 try eh_frame.writeEhFrameHdr(self, buffer.writer());3889 try eh_frame.writeEhFrameHdr(self, buffer.writer());
3858 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);3890 try self.pwriteAll(buffer.items, shdr.sh_offset);
3859 }3891 }
38603892
3861 if (self.section_indexes.got) |index| {3893 if (self.section_indexes.got) |index| {
...@@ -3863,7 +3895,7 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -3863,7 +3895,7 @@ fn writeSyntheticSections(self: *Elf) !void {
3863 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self));3895 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self));
3864 defer buffer.deinit();3896 defer buffer.deinit();
3865 try self.got.write(self, buffer.writer());3897 try self.got.write(self, buffer.writer());
3866 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);3898 try self.pwriteAll(buffer.items, shdr.sh_offset);
3867 }3899 }
38683900
3869 if (self.section_indexes.rela_dyn) |shndx| {3901 if (self.section_indexes.rela_dyn) |shndx| {
...@@ -3871,7 +3903,7 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -3871,7 +3903,7 @@ fn writeSyntheticSections(self: *Elf) !void {
3871 try self.got.addRela(self);3903 try self.got.addRela(self);
3872 try self.copy_rel.addRela(self);3904 try self.copy_rel.addRela(self);
3873 self.sortRelaDyn();3905 self.sortRelaDyn();
3874 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset);3906 try self.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset);
3875 }3907 }
38763908
3877 if (self.section_indexes.plt) |shndx| {3909 if (self.section_indexes.plt) |shndx| {
...@@ -3879,7 +3911,7 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -3879,7 +3911,7 @@ fn writeSyntheticSections(self: *Elf) !void {
3879 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size(self));3911 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size(self));
3880 defer buffer.deinit();3912 defer buffer.deinit();
3881 try self.plt.write(self, buffer.writer());3913 try self.plt.write(self, buffer.writer());
3882 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);3914 try self.pwriteAll(buffer.items, shdr.sh_offset);
3883 }3915 }
38843916
3885 if (self.section_indexes.got_plt) |shndx| {3917 if (self.section_indexes.got_plt) |shndx| {
...@@ -3887,7 +3919,7 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -3887,7 +3919,7 @@ fn writeSyntheticSections(self: *Elf) !void {
3887 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got_plt.size(self));3919 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got_plt.size(self));
3888 defer buffer.deinit();3920 defer buffer.deinit();
3889 try self.got_plt.write(self, buffer.writer());3921 try self.got_plt.write(self, buffer.writer());
3890 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);3922 try self.pwriteAll(buffer.items, shdr.sh_offset);
3891 }3923 }
38923924
3893 if (self.section_indexes.plt_got) |shndx| {3925 if (self.section_indexes.plt_got) |shndx| {
...@@ -3895,25 +3927,24 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -3895,25 +3927,24 @@ fn writeSyntheticSections(self: *Elf) !void {
3895 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt_got.size(self));3927 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt_got.size(self));
3896 defer buffer.deinit();3928 defer buffer.deinit();
3897 try self.plt_got.write(self, buffer.writer());3929 try self.plt_got.write(self, buffer.writer());
3898 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);3930 try self.pwriteAll(buffer.items, shdr.sh_offset);
3899 }3931 }
39003932
3901 if (self.section_indexes.rela_plt) |shndx| {3933 if (self.section_indexes.rela_plt) |shndx| {
3902 const shdr = slice.items(.shdr)[shndx];3934 const shdr = slice.items(.shdr)[shndx];
3903 try self.plt.addRela(self);3935 try self.plt.addRela(self);
3904 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_plt.items), shdr.sh_offset);3936 try self.pwriteAll(mem.sliceAsBytes(self.rela_plt.items), shdr.sh_offset);
3905 }3937 }
39063938
3907 try self.writeSymtab();3939 try self.writeSymtab();
3908 try self.writeShStrtab();3940 try self.writeShStrtab();
3909}3941}
39103942
3911// FIXME:JK again, why is this needed?
3912pub fn writeShStrtab(self: *Elf) !void {3943pub fn writeShStrtab(self: *Elf) !void {
3913 if (self.section_indexes.shstrtab) |index| {3944 if (self.section_indexes.shstrtab) |index| {
3914 const shdr = self.sections.items(.shdr)[index];3945 const shdr = self.sections.items(.shdr)[index];
3915 log.debug("writing .shstrtab from 0x{x} to 0x{x}", .{ shdr.sh_offset, shdr.sh_offset + shdr.sh_size });3946 log.debug("writing .shstrtab from 0x{x} to 0x{x}", .{ shdr.sh_offset, shdr.sh_offset + shdr.sh_size });
3916 try self.base.file.?.pwriteAll(self.shstrtab.items, shdr.sh_offset);3947 try self.pwriteAll(self.shstrtab.items, shdr.sh_offset);
3917 }3948 }
3918}3949}
39193950
...@@ -3928,7 +3959,7 @@ pub fn writeSymtab(self: *Elf) !void {...@@ -3928,7 +3959,7 @@ pub fn writeSymtab(self: *Elf) !void {
3928 .p32 => @sizeOf(elf.Elf32_Sym),3959 .p32 => @sizeOf(elf.Elf32_Sym),
3929 .p64 => @sizeOf(elf.Elf64_Sym),3960 .p64 => @sizeOf(elf.Elf64_Sym),
3930 };3961 };
3931 const nsyms = math.cast(usize, @divExact(symtab_shdr.sh_size, sym_size)) orelse return error.Overflow;3962 const nsyms = try self.cast(usize, @divExact(symtab_shdr.sh_size, sym_size));
39323963
3933 log.debug("writing {d} symbols in .symtab from 0x{x} to 0x{x}", .{3964 log.debug("writing {d} symbols in .symtab from 0x{x} to 0x{x}", .{
3934 nsyms,3965 nsyms,
...@@ -3941,7 +3972,7 @@ pub fn writeSymtab(self: *Elf) !void {...@@ -3941,7 +3972,7 @@ pub fn writeSymtab(self: *Elf) !void {
3941 });3972 });
39423973
3943 try self.symtab.resize(gpa, nsyms);3974 try self.symtab.resize(gpa, nsyms);
3944 const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow;3975 const needed_strtab_size = try self.cast(usize, strtab_shdr.sh_size - 1);
3945 // TODO we could resize instead and in ZigObject/Object always access as slice3976 // TODO we could resize instead and in ZigObject/Object always access as slice
3946 self.strtab.clearRetainingCapacity();3977 self.strtab.clearRetainingCapacity();
3947 self.strtab.appendAssumeCapacity(0);3978 self.strtab.appendAssumeCapacity(0);
...@@ -4010,17 +4041,17 @@ pub fn writeSymtab(self: *Elf) !void {...@@ -4010,17 +4041,17 @@ pub fn writeSymtab(self: *Elf) !void {
4010 };4041 };
4011 if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out);4042 if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out);
4012 }4043 }
4013 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset);4044 try self.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset);
4014 },4045 },
4015 .p64 => {4046 .p64 => {
4016 if (foreign_endian) {4047 if (foreign_endian) {
4017 for (self.symtab.items) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym);4048 for (self.symtab.items) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym);
4018 }4049 }
4019 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), symtab_shdr.sh_offset);4050 try self.pwriteAll(mem.sliceAsBytes(self.symtab.items), symtab_shdr.sh_offset);
4020 },4051 },
4021 }4052 }
40224053
4023 try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset);4054 try self.pwriteAll(self.strtab.items, strtab_shdr.sh_offset);
4024}4055}
40254056
4026/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.4057/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.
...@@ -4514,12 +4545,12 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {...@@ -4514,12 +4545,12 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
4514 for (refs.items[0..nrefs]) |ref| {4545 for (refs.items[0..nrefs]) |ref| {
4515 const atom_ptr = self.atom(ref).?;4546 const atom_ptr = self.atom(ref).?;
4516 const file_ptr = atom_ptr.file(self).?;4547 const file_ptr = atom_ptr.file(self).?;
4517 try err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) });4548 err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) });
4518 }4549 }
45194550
4520 if (refs.items.len > max_notes) {4551 if (refs.items.len > max_notes) {
4521 const remaining = refs.items.len - max_notes;4552 const remaining = refs.items.len - max_notes;
4522 try err.addNote("referenced {d} more times", .{remaining});4553 err.addNote("referenced {d} more times", .{remaining});
4523 }4554 }
4524 }4555 }
4525}4556}
...@@ -4536,17 +4567,17 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor...@@ -4536,17 +4567,17 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor
45364567
4537 var err = try diags.addErrorWithNotes(nnotes + 1);4568 var err = try diags.addErrorWithNotes(nnotes + 1);
4538 try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)});4569 try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)});
4539 try err.addNote("defined by {}", .{sym.file(self).?.fmtPath()});4570 err.addNote("defined by {}", .{sym.file(self).?.fmtPath()});
45404571
4541 var inote: usize = 0;4572 var inote: usize = 0;
4542 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {4573 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
4543 const file_ptr = self.file(notes.items[inote]).?;4574 const file_ptr = self.file(notes.items[inote]).?;
4544 try err.addNote("defined by {}", .{file_ptr.fmtPath()});4575 err.addNote("defined by {}", .{file_ptr.fmtPath()});
4545 }4576 }
45464577
4547 if (notes.items.len > max_notes) {4578 if (notes.items.len > max_notes) {
4548 const remaining = notes.items.len - max_notes;4579 const remaining = notes.items.len - max_notes;
4549 try err.addNote("defined {d} more times", .{remaining});4580 err.addNote("defined {d} more times", .{remaining});
4550 }4581 }
4551 }4582 }
45524583
...@@ -4570,7 +4601,7 @@ pub fn addFileError(...@@ -4570,7 +4601,7 @@ pub fn addFileError(
4570 const diags = &self.base.comp.link_diags;4601 const diags = &self.base.comp.link_diags;
4571 var err = try diags.addErrorWithNotes(1);4602 var err = try diags.addErrorWithNotes(1);
4572 try err.addMsg(format, args);4603 try err.addMsg(format, args);
4573 try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()});4604 err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()});
4574}4605}
45754606
4576pub fn failFile(4607pub fn failFile(
...@@ -5184,6 +5215,30 @@ pub fn stringTableLookup(strtab: []const u8, off: u32) [:0]const u8 {...@@ -5184,6 +5215,30 @@ pub fn stringTableLookup(strtab: []const u8, off: u32) [:0]const u8 {
5184 return slice[0..mem.indexOfScalar(u8, slice, 0).? :0];5215 return slice[0..mem.indexOfScalar(u8, slice, 0).? :0];
5185}5216}
51865217
5218pub fn pwriteAll(elf_file: *Elf, bytes: []const u8, offset: u64) error{LinkFailure}!void {
5219 const comp = elf_file.base.comp;
5220 const diags = &comp.link_diags;
5221 elf_file.base.file.?.pwriteAll(bytes, offset) catch |err| {
5222 return diags.fail("failed to write: {s}", .{@errorName(err)});
5223 };
5224}
5225
5226pub fn setEndPos(elf_file: *Elf, length: u64) error{LinkFailure}!void {
5227 const comp = elf_file.base.comp;
5228 const diags = &comp.link_diags;
5229 elf_file.base.file.?.setEndPos(length) catch |err| {
5230 return diags.fail("failed to set file end pos: {s}", .{@errorName(err)});
5231 };
5232}
5233
5234pub fn cast(elf_file: *Elf, comptime T: type, x: anytype) error{LinkFailure}!T {
5235 return std.math.cast(T, x) orelse {
5236 const comp = elf_file.base.comp;
5237 const diags = &comp.link_diags;
5238 return diags.fail("encountered {d}, overflowing {d}-bit value", .{ x, @bitSizeOf(T) });
5239 };
5240}
5241
5187const std = @import("std");5242const std = @import("std");
5188const build_options = @import("build_options");5243const build_options = @import("build_options");
5189const builtin = @import("builtin");5244const builtin = @import("builtin");
src/link/Elf/Atom.zig+12-12
...@@ -523,7 +523,7 @@ fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) Re...@@ -523,7 +523,7 @@ fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) Re
523 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),523 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
524 rel.r_offset,524 rel.r_offset,
525 });525 });
526 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });526 err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
527 return error.RelocFailure;527 return error.RelocFailure;
528}528}
529529
...@@ -539,7 +539,7 @@ fn reportTextRelocError(...@@ -539,7 +539,7 @@ fn reportTextRelocError(
539 rel.r_offset,539 rel.r_offset,
540 symbol.name(elf_file),540 symbol.name(elf_file),
541 });541 });
542 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });542 err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
543 return error.RelocFailure;543 return error.RelocFailure;
544}544}
545545
...@@ -555,8 +555,8 @@ fn reportPicError(...@@ -555,8 +555,8 @@ fn reportPicError(
555 rel.r_offset,555 rel.r_offset,
556 symbol.name(elf_file),556 symbol.name(elf_file),
557 });557 });
558 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });558 err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
559 try err.addNote("recompile with -fPIC", .{});559 err.addNote("recompile with -fPIC", .{});
560 return error.RelocFailure;560 return error.RelocFailure;
561}561}
562562
...@@ -572,8 +572,8 @@ fn reportNoPicError(...@@ -572,8 +572,8 @@ fn reportNoPicError(
572 rel.r_offset,572 rel.r_offset,
573 symbol.name(elf_file),573 symbol.name(elf_file),
574 });574 });
575 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });575 err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
576 try err.addNote("recompile with -fno-PIC", .{});576 err.addNote("recompile with -fno-PIC", .{});
577 return error.RelocFailure;577 return error.RelocFailure;
578}578}
579579
...@@ -1187,7 +1187,7 @@ const x86_64 = struct {...@@ -1187,7 +1187,7 @@ const x86_64 = struct {
1187 x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {1187 x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {
1188 var err = try diags.addErrorWithNotes(1);1188 var err = try diags.addErrorWithNotes(1);
1189 try err.addMsg("could not relax {s}", .{@tagName(r_type)});1189 try err.addMsg("could not relax {s}", .{@tagName(r_type)});
1190 try err.addNote("in {}:{s} at offset 0x{x}", .{1190 err.addNote("in {}:{s} at offset 0x{x}", .{
1191 atom.file(elf_file).?.fmtPath(),1191 atom.file(elf_file).?.fmtPath(),
1192 atom.name(elf_file),1192 atom.name(elf_file),
1193 rel.r_offset,1193 rel.r_offset,
...@@ -1332,7 +1332,7 @@ const x86_64 = struct {...@@ -1332,7 +1332,7 @@ const x86_64 = struct {
1332 relocation.fmtRelocType(rels[0].r_type(), .x86_64),1332 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
1333 relocation.fmtRelocType(rels[1].r_type(), .x86_64),1333 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
1334 });1334 });
1335 try err.addNote("in {}:{s} at offset 0x{x}", .{1335 err.addNote("in {}:{s} at offset 0x{x}", .{
1336 self.file(elf_file).?.fmtPath(),1336 self.file(elf_file).?.fmtPath(),
1337 self.name(elf_file),1337 self.name(elf_file),
1338 rels[0].r_offset,1338 rels[0].r_offset,
...@@ -1388,7 +1388,7 @@ const x86_64 = struct {...@@ -1388,7 +1388,7 @@ const x86_64 = struct {
1388 relocation.fmtRelocType(rels[0].r_type(), .x86_64),1388 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
1389 relocation.fmtRelocType(rels[1].r_type(), .x86_64),1389 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
1390 });1390 });
1391 try err.addNote("in {}:{s} at offset 0x{x}", .{1391 err.addNote("in {}:{s} at offset 0x{x}", .{
1392 self.file(elf_file).?.fmtPath(),1392 self.file(elf_file).?.fmtPath(),
1393 self.name(elf_file),1393 self.name(elf_file),
1394 rels[0].r_offset,1394 rels[0].r_offset,
...@@ -1485,7 +1485,7 @@ const x86_64 = struct {...@@ -1485,7 +1485,7 @@ const x86_64 = struct {
1485 relocation.fmtRelocType(rels[0].r_type(), .x86_64),1485 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
1486 relocation.fmtRelocType(rels[1].r_type(), .x86_64),1486 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
1487 });1487 });
1488 try err.addNote("in {}:{s} at offset 0x{x}", .{1488 err.addNote("in {}:{s} at offset 0x{x}", .{
1489 self.file(elf_file).?.fmtPath(),1489 self.file(elf_file).?.fmtPath(),
1490 self.name(elf_file),1490 self.name(elf_file),
1491 rels[0].r_offset,1491 rels[0].r_offset,
...@@ -1672,7 +1672,7 @@ const aarch64 = struct {...@@ -1672,7 +1672,7 @@ const aarch64 = struct {
1672 // TODO: relax1672 // TODO: relax
1673 var err = try diags.addErrorWithNotes(1);1673 var err = try diags.addErrorWithNotes(1);
1674 try err.addMsg("TODO: relax ADR_GOT_PAGE", .{});1674 try err.addMsg("TODO: relax ADR_GOT_PAGE", .{});
1675 try err.addNote("in {}:{s} at offset 0x{x}", .{1675 err.addNote("in {}:{s} at offset 0x{x}", .{
1676 atom.file(elf_file).?.fmtPath(),1676 atom.file(elf_file).?.fmtPath(),
1677 atom.name(elf_file),1677 atom.name(elf_file),
1678 r_offset,1678 r_offset,
...@@ -1959,7 +1959,7 @@ const riscv = struct {...@@ -1959,7 +1959,7 @@ const riscv = struct {
1959 // TODO: implement searching forward1959 // TODO: implement searching forward
1960 var err = try diags.addErrorWithNotes(1);1960 var err = try diags.addErrorWithNotes(1);
1961 try err.addMsg("TODO: find HI20 paired reloc scanning forward", .{});1961 try err.addMsg("TODO: find HI20 paired reloc scanning forward", .{});
1962 try err.addNote("in {}:{s} at offset 0x{x}", .{1962 err.addNote("in {}:{s} at offset 0x{x}", .{
1963 atom.file(elf_file).?.fmtPath(),1963 atom.file(elf_file).?.fmtPath(),
1964 atom.name(elf_file),1964 atom.name(elf_file),
1965 rel.r_offset,1965 rel.r_offset,
src/link/Elf/AtomList.zig+3-2
...@@ -58,7 +58,7 @@ pub fn allocate(list: *AtomList, elf_file: *Elf) !void {...@@ -58,7 +58,7 @@ pub fn allocate(list: *AtomList, elf_file: *Elf) !void {
58 if (expand_section) last_atom_ref.* = list.lastAtom(elf_file).ref();58 if (expand_section) last_atom_ref.* = list.lastAtom(elf_file).ref();
59 shdr.sh_addralign = @max(shdr.sh_addralign, list.alignment.toByteUnits().?);59 shdr.sh_addralign = @max(shdr.sh_addralign, list.alignment.toByteUnits().?);
6060
61 // FIXME:JK this currently ignores Thunks as valid chunks.61 // This currently ignores Thunks as valid chunks.
62 {62 {
63 var idx: usize = 0;63 var idx: usize = 0;
64 while (idx < list.atoms.keys().len) : (idx += 1) {64 while (idx < list.atoms.keys().len) : (idx += 1) {
...@@ -78,7 +78,8 @@ pub fn allocate(list: *AtomList, elf_file: *Elf) !void {...@@ -78,7 +78,8 @@ pub fn allocate(list: *AtomList, elf_file: *Elf) !void {
78 placement_atom.next_atom_ref = list.firstAtom(elf_file).ref();78 placement_atom.next_atom_ref = list.firstAtom(elf_file).ref();
79 }79 }
8080
81 // FIXME:JK if we had a link from Atom to parent AtomList we would not need to update Atom's value or osec index81 // If we had a link from Atom to parent AtomList we would not need to
82 // update Atom's value or osec index.
82 for (list.atoms.keys()) |ref| {83 for (list.atoms.keys()) |ref| {
83 const atom_ptr = elf_file.atom(ref).?;84 const atom_ptr = elf_file.atom(ref).?;
84 atom_ptr.output_section_index = list.output_section_index;85 atom_ptr.output_section_index = list.output_section_index;
src/link/Elf/Object.zig+5-5
...@@ -797,7 +797,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void {...@@ -797,7 +797,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void {
797 if (!isNull(data[end .. end + sh_entsize])) {797 if (!isNull(data[end .. end + sh_entsize])) {
798 var err = try diags.addErrorWithNotes(1);798 var err = try diags.addErrorWithNotes(1);
799 try err.addMsg("string not null terminated", .{});799 try err.addMsg("string not null terminated", .{});
800 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });800 err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
801 return error.LinkFailure;801 return error.LinkFailure;
802 }802 }
803 end += sh_entsize;803 end += sh_entsize;
...@@ -812,7 +812,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void {...@@ -812,7 +812,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void {
812 if (shdr.sh_size % sh_entsize != 0) {812 if (shdr.sh_size % sh_entsize != 0) {
813 var err = try diags.addErrorWithNotes(1);813 var err = try diags.addErrorWithNotes(1);
814 try err.addMsg("size not a multiple of sh_entsize", .{});814 try err.addMsg("size not a multiple of sh_entsize", .{});
815 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });815 err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
816 return error.LinkFailure;816 return error.LinkFailure;
817 }817 }
818818
...@@ -889,8 +889,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{...@@ -889,8 +889,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{
889 const res = imsec.findSubsection(@intCast(esym.st_value)) orelse {889 const res = imsec.findSubsection(@intCast(esym.st_value)) orelse {
890 var err = try diags.addErrorWithNotes(2);890 var err = try diags.addErrorWithNotes(2);
891 try err.addMsg("invalid symbol value: {x}", .{esym.st_value});891 try err.addMsg("invalid symbol value: {x}", .{esym.st_value});
892 try err.addNote("for symbol {s}", .{sym.name(elf_file)});892 err.addNote("for symbol {s}", .{sym.name(elf_file)});
893 try err.addNote("in {}", .{self.fmtPath()});893 err.addNote("in {}", .{self.fmtPath()});
894 return error.LinkFailure;894 return error.LinkFailure;
895 };895 };
896896
...@@ -915,7 +915,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{...@@ -915,7 +915,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{
915 const res = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {915 const res = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {
916 var err = try diags.addErrorWithNotes(1);916 var err = try diags.addErrorWithNotes(1);
917 try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset});917 try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset});
918 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });918 err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
919 return error.LinkFailure;919 return error.LinkFailure;
920 };920 };
921921
src/link/Elf/ZigObject.zig+67-65
...@@ -278,8 +278,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {...@@ -278,8 +278,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
278 .{ .kind = .code, .ty = .anyerror_type },278 .{ .kind = .code, .ty = .anyerror_type },
279 metadata.text_symbol_index,279 metadata.text_symbol_index,
280 ) catch |err| return switch (err) {280 ) catch |err| return switch (err) {
281 error.CodegenFail => error.FlushFailure,281 error.CodegenFail => error.LinkFailure,
282 else => |e| e,282 else => |e| return e,
283 };283 };
284 if (metadata.rodata_state != .unused) self.updateLazySymbol(284 if (metadata.rodata_state != .unused) self.updateLazySymbol(
285 elf_file,285 elf_file,
...@@ -287,8 +287,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {...@@ -287,8 +287,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
287 .{ .kind = .const_data, .ty = .anyerror_type },287 .{ .kind = .const_data, .ty = .anyerror_type },
288 metadata.rodata_symbol_index,288 metadata.rodata_symbol_index,
289 ) catch |err| return switch (err) {289 ) catch |err| return switch (err) {
290 error.CodegenFail => error.FlushFailure,290 error.CodegenFail => error.LinkFailure,
291 else => |e| e,291 else => |e| return e,
292 };292 };
293 }293 }
294 for (self.lazy_syms.values()) |*metadata| {294 for (self.lazy_syms.values()) |*metadata| {
...@@ -933,6 +933,7 @@ pub fn getNavVAddr(...@@ -933,6 +933,7 @@ pub fn getNavVAddr(
933 const this_sym = self.symbol(this_sym_index);933 const this_sym = self.symbol(this_sym_index);
934 const vaddr = this_sym.address(.{}, elf_file);934 const vaddr = this_sym.address(.{}, elf_file);
935 switch (reloc_info.parent) {935 switch (reloc_info.parent) {
936 .none => unreachable,
936 .atom_index => |atom_index| {937 .atom_index => |atom_index| {
937 const parent_atom = self.symbol(atom_index).atom(elf_file).?;938 const parent_atom = self.symbol(atom_index).atom(elf_file).?;
938 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);939 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
...@@ -965,6 +966,7 @@ pub fn getUavVAddr(...@@ -965,6 +966,7 @@ pub fn getUavVAddr(
965 const sym = self.symbol(sym_index);966 const sym = self.symbol(sym_index);
966 const vaddr = sym.address(.{}, elf_file);967 const vaddr = sym.address(.{}, elf_file);
967 switch (reloc_info.parent) {968 switch (reloc_info.parent) {
969 .none => unreachable,
968 .atom_index => |atom_index| {970 .atom_index => |atom_index| {
969 const parent_atom = self.symbol(atom_index).atom(elf_file).?;971 const parent_atom = self.symbol(atom_index).atom(elf_file).?;
970 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);972 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
...@@ -1261,7 +1263,7 @@ fn updateNavCode(...@@ -1261,7 +1263,7 @@ fn updateNavCode(
1261 shdr_index: u32,1263 shdr_index: u32,
1262 code: []const u8,1264 code: []const u8,
1263 stt_bits: u8,1265 stt_bits: u8,
1264) !void {1266) link.File.UpdateNavError!void {
1265 const zcu = pt.zcu;1267 const zcu = pt.zcu;
1266 const gpa = zcu.gpa;1268 const gpa = zcu.gpa;
1267 const ip = &zcu.intern_pool;1269 const ip = &zcu.intern_pool;
...@@ -1298,7 +1300,9 @@ fn updateNavCode(...@@ -1298,7 +1300,9 @@ fn updateNavCode(
1298 const capacity = atom_ptr.capacity(elf_file);1300 const capacity = atom_ptr.capacity(elf_file);
1299 const need_realloc = code.len > capacity or !required_alignment.check(@intCast(atom_ptr.value));1301 const need_realloc = code.len > capacity or !required_alignment.check(@intCast(atom_ptr.value));
1300 if (need_realloc) {1302 if (need_realloc) {
1301 try self.allocateAtom(atom_ptr, true, elf_file);1303 self.allocateAtom(atom_ptr, true, elf_file) catch |err|
1304 return elf_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)});
1305
1302 log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom_ptr.value });1306 log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom_ptr.value });
1303 if (old_vaddr != atom_ptr.value) {1307 if (old_vaddr != atom_ptr.value) {
1304 sym.value = 0;1308 sym.value = 0;
...@@ -1308,7 +1312,9 @@ fn updateNavCode(...@@ -1308,7 +1312,9 @@ fn updateNavCode(
1308 // TODO shrink section size1312 // TODO shrink section size
1309 }1313 }
1310 } else {1314 } else {
1311 try self.allocateAtom(atom_ptr, true, elf_file);1315 self.allocateAtom(atom_ptr, true, elf_file) catch |err|
1316 return elf_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)});
1317
1312 errdefer self.freeNavMetadata(elf_file, sym_index);1318 errdefer self.freeNavMetadata(elf_file, sym_index);
1313 sym.value = 0;1319 sym.value = 0;
1314 esym.st_value = 0;1320 esym.st_value = 0;
...@@ -1333,14 +1339,15 @@ fn updateNavCode(...@@ -1333,14 +1339,15 @@ fn updateNavCode(
1333 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),1339 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
1334 }1340 }
1335 },1341 },
1336 else => return error.HotSwapUnavailableOnHostOperatingSystem,1342 else => return elf_file.base.cgFail(nav_index, "ELF hot swap unavailable on host operating system '{s}'", .{@tagName(builtin.os.tag)}),
1337 }1343 }
1338 }1344 }
13391345
1340 const shdr = elf_file.sections.items(.shdr)[shdr_index];1346 const shdr = elf_file.sections.items(.shdr)[shdr_index];
1341 if (shdr.sh_type != elf.SHT_NOBITS) {1347 if (shdr.sh_type != elf.SHT_NOBITS) {
1342 const file_offset = atom_ptr.offset(elf_file);1348 const file_offset = atom_ptr.offset(elf_file);
1343 try elf_file.base.file.?.pwriteAll(code, file_offset);1349 elf_file.base.file.?.pwriteAll(code, file_offset) catch |err|
1350 return elf_file.base.cgFail(nav_index, "failed to write to output file: {s}", .{@errorName(err)});
1344 log.debug("writing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), file_offset, file_offset + code.len });1351 log.debug("writing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), file_offset, file_offset + code.len });
1345 }1352 }
1346}1353}
...@@ -1353,7 +1360,7 @@ fn updateTlv(...@@ -1353,7 +1360,7 @@ fn updateTlv(
1353 sym_index: Symbol.Index,1360 sym_index: Symbol.Index,
1354 shndx: u32,1361 shndx: u32,
1355 code: []const u8,1362 code: []const u8,
1356) !void {1363) link.File.UpdateNavError!void {
1357 const zcu = pt.zcu;1364 const zcu = pt.zcu;
1358 const ip = &zcu.intern_pool;1365 const ip = &zcu.intern_pool;
1359 const gpa = zcu.gpa;1366 const gpa = zcu.gpa;
...@@ -1383,7 +1390,8 @@ fn updateTlv(...@@ -1383,7 +1390,8 @@ fn updateTlv(
1383 const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index);1390 const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index);
1384 assert(!gop.found_existing); // TODO incremental updates1391 assert(!gop.found_existing); // TODO incremental updates
13851392
1386 try self.allocateAtom(atom_ptr, true, elf_file);1393 self.allocateAtom(atom_ptr, true, elf_file) catch |err|
1394 return elf_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)});
1387 sym.value = 0;1395 sym.value = 0;
1388 esym.st_value = 0;1396 esym.st_value = 0;
13891397
...@@ -1392,7 +1400,8 @@ fn updateTlv(...@@ -1392,7 +1400,8 @@ fn updateTlv(
1392 const shdr = elf_file.sections.items(.shdr)[shndx];1400 const shdr = elf_file.sections.items(.shdr)[shndx];
1393 if (shdr.sh_type != elf.SHT_NOBITS) {1401 if (shdr.sh_type != elf.SHT_NOBITS) {
1394 const file_offset = atom_ptr.offset(elf_file);1402 const file_offset = atom_ptr.offset(elf_file);
1395 try elf_file.base.file.?.pwriteAll(code, file_offset);1403 elf_file.base.file.?.pwriteAll(code, file_offset) catch |err|
1404 return elf_file.base.cgFail(nav_index, "failed to write to output file: {s}", .{@errorName(err)});
1396 log.debug("writing TLV {s} from 0x{x} to 0x{x}", .{1405 log.debug("writing TLV {s} from 0x{x} to 0x{x}", .{
1397 atom_ptr.name(elf_file),1406 atom_ptr.name(elf_file),
1398 file_offset,1407 file_offset,
...@@ -1408,7 +1417,7 @@ pub fn updateFunc(...@@ -1408,7 +1417,7 @@ pub fn updateFunc(
1408 func_index: InternPool.Index,1417 func_index: InternPool.Index,
1409 air: Air,1418 air: Air,
1410 liveness: Liveness,1419 liveness: Liveness,
1411) !void {1420) link.File.UpdateNavError!void {
1412 const tracy = trace(@src());1421 const tracy = trace(@src());
1413 defer tracy.end();1422 defer tracy.end();
14141423
...@@ -1422,13 +1431,13 @@ pub fn updateFunc(...@@ -1422,13 +1431,13 @@ pub fn updateFunc(
1422 const sym_index = try self.getOrCreateMetadataForNav(zcu, func.owner_nav);1431 const sym_index = try self.getOrCreateMetadataForNav(zcu, func.owner_nav);
1423 self.atom(self.symbol(sym_index).ref.index).?.freeRelocs(self);1432 self.atom(self.symbol(sym_index).ref.index).?.freeRelocs(self);
14241433
1425 var code_buffer = std.ArrayList(u8).init(gpa);1434 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1426 defer code_buffer.deinit();1435 defer code_buffer.deinit(gpa);
14271436
1428 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;1437 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;
1429 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();1438 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
14301439
1431 const res = try codegen.generateFunction(1440 try codegen.generateFunction(
1432 &elf_file.base,1441 &elf_file.base,
1433 pt,1442 pt,
1434 zcu.navSrcLoc(func.owner_nav),1443 zcu.navSrcLoc(func.owner_nav),
...@@ -1438,14 +1447,7 @@ pub fn updateFunc(...@@ -1438,14 +1447,7 @@ pub fn updateFunc(
1438 &code_buffer,1447 &code_buffer,
1439 if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none,1448 if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none,
1440 );1449 );
14411450 const code = code_buffer.items;
1442 const code = switch (res) {
1443 .ok => code_buffer.items,
1444 .fail => |em| {
1445 try zcu.failed_codegen.put(gpa, func.owner_nav, em);
1446 return;
1447 },
1448 };
14491451
1450 const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, sym_index, code);1452 const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, sym_index, code);
1451 log.debug("setting shdr({x},{s}) for {}", .{1453 log.debug("setting shdr({x},{s}) for {}", .{
...@@ -1463,7 +1465,8 @@ pub fn updateFunc(...@@ -1463,7 +1465,8 @@ pub fn updateFunc(
1463 break :blk .{ atom_ptr.value, atom_ptr.alignment };1465 break :blk .{ atom_ptr.value, atom_ptr.alignment };
1464 };1466 };
14651467
1466 if (debug_wip_nav) |*wip_nav| try self.dwarf.?.finishWipNavFunc(pt, func.owner_nav, code.len, wip_nav);1468 if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNavFunc(pt, func.owner_nav, code.len, wip_nav) catch |err|
1469 return elf_file.base.cgFail(func.owner_nav, "failed to finish dwarf function: {s}", .{@errorName(err)});
14671470
1468 // Exports will be updated by `Zcu.processExports` after the update.1471 // Exports will be updated by `Zcu.processExports` after the update.
14691472
...@@ -1511,7 +1514,8 @@ pub fn updateFunc(...@@ -1511,7 +1514,8 @@ pub fn updateFunc(
1511 target_sym.flags.has_trampoline = true;1514 target_sym.flags.has_trampoline = true;
1512 }1515 }
1513 const target_sym = self.symbol(sym_index);1516 const target_sym = self.symbol(sym_index);
1514 try writeTrampoline(self.symbol(target_sym.extra(elf_file).trampoline).*, target_sym.*, elf_file);1517 writeTrampoline(self.symbol(target_sym.extra(elf_file).trampoline).*, target_sym.*, elf_file) catch |err|
1518 return elf_file.base.cgFail(func.owner_nav, "failed to write trampoline: {s}", .{@errorName(err)});
1515 }1519 }
1516}1520}
15171521
...@@ -1547,7 +1551,11 @@ pub fn updateNav(...@@ -1547,7 +1551,11 @@ pub fn updateNav(
1547 if (self.dwarf) |*dwarf| dwarf: {1551 if (self.dwarf) |*dwarf| dwarf: {
1548 var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf;1552 var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf;
1549 defer debug_wip_nav.deinit();1553 defer debug_wip_nav.deinit();
1550 try dwarf.finishWipNav(pt, nav_index, &debug_wip_nav);1554 dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) {
1555 error.OutOfMemory => return error.OutOfMemory,
1556 error.Overflow => return error.Overflow,
1557 else => |e| return elf_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
1558 };
1551 }1559 }
1552 return;1560 return;
1553 },1561 },
...@@ -1558,13 +1566,13 @@ pub fn updateNav(...@@ -1558,13 +1566,13 @@ pub fn updateNav(
1558 const sym_index = try self.getOrCreateMetadataForNav(zcu, nav_index);1566 const sym_index = try self.getOrCreateMetadataForNav(zcu, nav_index);
1559 self.symbol(sym_index).atom(elf_file).?.freeRelocs(self);1567 self.symbol(sym_index).atom(elf_file).?.freeRelocs(self);
15601568
1561 var code_buffer = std.ArrayList(u8).init(zcu.gpa);1569 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1562 defer code_buffer.deinit();1570 defer code_buffer.deinit(zcu.gpa);
15631571
1564 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;1572 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;
1565 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();1573 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
15661574
1567 const res = try codegen.generateSymbol(1575 try codegen.generateSymbol(
1568 &elf_file.base,1576 &elf_file.base,
1569 pt,1577 pt,
1570 zcu.navSrcLoc(nav_index),1578 zcu.navSrcLoc(nav_index),
...@@ -1572,14 +1580,7 @@ pub fn updateNav(...@@ -1572,14 +1580,7 @@ pub fn updateNav(
1572 &code_buffer,1580 &code_buffer,
1573 .{ .atom_index = sym_index },1581 .{ .atom_index = sym_index },
1574 );1582 );
15751583 const code = code_buffer.items;
1576 const code = switch (res) {
1577 .ok => code_buffer.items,
1578 .fail => |em| {
1579 try zcu.failed_codegen.put(zcu.gpa, nav_index, em);
1580 return;
1581 },
1582 };
15831584
1584 const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, sym_index, code);1585 const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, sym_index, code);
1585 log.debug("setting shdr({x},{s}) for {}", .{1586 log.debug("setting shdr({x},{s}) for {}", .{
...@@ -1592,7 +1593,11 @@ pub fn updateNav(...@@ -1592,7 +1593,11 @@ pub fn updateNav(
1592 else1593 else
1593 try self.updateNavCode(elf_file, pt, nav_index, sym_index, shndx, code, elf.STT_OBJECT);1594 try self.updateNavCode(elf_file, pt, nav_index, sym_index, shndx, code, elf.STT_OBJECT);
15941595
1595 if (debug_wip_nav) |*wip_nav| try self.dwarf.?.finishWipNav(pt, nav_index, wip_nav);1596 if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNav(pt, nav_index, wip_nav) catch |err| switch (err) {
1597 error.OutOfMemory => return error.OutOfMemory,
1598 error.Overflow => return error.Overflow,
1599 else => |e| return elf_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
1600 };
1596 } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index);1601 } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index);
15971602
1598 // Exports will be updated by `Zcu.processExports` after the update.1603 // Exports will be updated by `Zcu.processExports` after the update.
...@@ -1602,7 +1607,7 @@ pub fn updateContainerType(...@@ -1602,7 +1607,7 @@ pub fn updateContainerType(
1602 self: *ZigObject,1607 self: *ZigObject,
1603 pt: Zcu.PerThread,1608 pt: Zcu.PerThread,
1604 ty: InternPool.Index,1609 ty: InternPool.Index,
1605) link.File.UpdateNavError!void {1610) !void {
1606 const tracy = trace(@src());1611 const tracy = trace(@src());
1607 defer tracy.end();1612 defer tracy.end();
16081613
...@@ -1620,8 +1625,8 @@ fn updateLazySymbol(...@@ -1620,8 +1625,8 @@ fn updateLazySymbol(
1620 const gpa = zcu.gpa;1625 const gpa = zcu.gpa;
16211626
1622 var required_alignment: InternPool.Alignment = .none;1627 var required_alignment: InternPool.Alignment = .none;
1623 var code_buffer = std.ArrayList(u8).init(gpa);1628 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1624 defer code_buffer.deinit();1629 defer code_buffer.deinit(gpa);
16251630
1626 const name_str_index = blk: {1631 const name_str_index = blk: {
1627 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{1632 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{
...@@ -1633,7 +1638,7 @@ fn updateLazySymbol(...@@ -1633,7 +1638,7 @@ fn updateLazySymbol(
1633 };1638 };
16341639
1635 const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;1640 const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;
1636 const res = try codegen.generateLazySymbol(1641 try codegen.generateLazySymbol(
1637 &elf_file.base,1642 &elf_file.base,
1638 pt,1643 pt,
1639 src,1644 src,
...@@ -1643,13 +1648,7 @@ fn updateLazySymbol(...@@ -1643,13 +1648,7 @@ fn updateLazySymbol(
1643 .none,1648 .none,
1644 .{ .atom_index = symbol_index },1649 .{ .atom_index = symbol_index },
1645 );1650 );
1646 const code = switch (res) {1651 const code = code_buffer.items;
1647 .ok => code_buffer.items,
1648 .fail => |em| {
1649 log.err("{s}", .{em.msg});
1650 return error.CodegenFail;
1651 },
1652 };
16531652
1654 const output_section_index = switch (sym.kind) {1653 const output_section_index = switch (sym.kind) {
1655 .code => if (self.text_index) |sym_index|1654 .code => if (self.text_index) |sym_index|
...@@ -1696,7 +1695,7 @@ fn updateLazySymbol(...@@ -1696,7 +1695,7 @@ fn updateLazySymbol(
1696 local_sym.value = 0;1695 local_sym.value = 0;
1697 local_esym.st_value = 0;1696 local_esym.st_value = 0;
16981697
1699 try elf_file.base.file.?.pwriteAll(code, atom_ptr.offset(elf_file));1698 try elf_file.pwriteAll(code, atom_ptr.offset(elf_file));
1700}1699}
17011700
1702const LowerConstResult = union(enum) {1701const LowerConstResult = union(enum) {
...@@ -1716,13 +1715,13 @@ fn lowerConst(...@@ -1716,13 +1715,13 @@ fn lowerConst(
1716) !LowerConstResult {1715) !LowerConstResult {
1717 const gpa = pt.zcu.gpa;1716 const gpa = pt.zcu.gpa;
17181717
1719 var code_buffer = std.ArrayList(u8).init(gpa);1718 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1720 defer code_buffer.deinit();1719 defer code_buffer.deinit(gpa);
17211720
1722 const name_off = try self.addString(gpa, name);1721 const name_off = try self.addString(gpa, name);
1723 const sym_index = try self.newSymbolWithAtom(gpa, name_off);1722 const sym_index = try self.newSymbolWithAtom(gpa, name_off);
17241723
1725 const res = try codegen.generateSymbol(1724 try codegen.generateSymbol(
1726 &elf_file.base,1725 &elf_file.base,
1727 pt,1726 pt,
1728 src_loc,1727 src_loc,
...@@ -1730,10 +1729,7 @@ fn lowerConst(...@@ -1730,10 +1729,7 @@ fn lowerConst(
1730 &code_buffer,1729 &code_buffer,
1731 .{ .atom_index = sym_index },1730 .{ .atom_index = sym_index },
1732 );1731 );
1733 const code = switch (res) {1732 const code = code_buffer.items;
1734 .ok => code_buffer.items,
1735 .fail => |em| return .{ .fail = em },
1736 };
17371733
1738 const local_sym = self.symbol(sym_index);1734 const local_sym = self.symbol(sym_index);
1739 const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index];1735 const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index];
...@@ -1748,7 +1744,7 @@ fn lowerConst(...@@ -1748,7 +1744,7 @@ fn lowerConst(
1748 try self.allocateAtom(atom_ptr, true, elf_file);1744 try self.allocateAtom(atom_ptr, true, elf_file);
1749 errdefer self.freeNavMetadata(elf_file, sym_index);1745 errdefer self.freeNavMetadata(elf_file, sym_index);
17501746
1751 try elf_file.base.file.?.pwriteAll(code, atom_ptr.offset(elf_file));1747 try elf_file.pwriteAll(code, atom_ptr.offset(elf_file));
17521748
1753 return .{ .ok = sym_index };1749 return .{ .ok = sym_index };
1754}1750}
...@@ -1758,7 +1754,7 @@ pub fn updateExports(...@@ -1758,7 +1754,7 @@ pub fn updateExports(
1758 elf_file: *Elf,1754 elf_file: *Elf,
1759 pt: Zcu.PerThread,1755 pt: Zcu.PerThread,
1760 exported: Zcu.Exported,1756 exported: Zcu.Exported,
1761 export_indices: []const u32,1757 export_indices: []const Zcu.Export.Index,
1762) link.File.UpdateExportsError!void {1758) link.File.UpdateExportsError!void {
1763 const tracy = trace(@src());1759 const tracy = trace(@src());
1764 defer tracy.end();1760 defer tracy.end();
...@@ -1771,7 +1767,7 @@ pub fn updateExports(...@@ -1771,7 +1767,7 @@ pub fn updateExports(
1771 break :blk self.navs.getPtr(nav).?;1767 break :blk self.navs.getPtr(nav).?;
1772 },1768 },
1773 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {1769 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1774 const first_exp = zcu.all_exports.items[export_indices[0]];1770 const first_exp = export_indices[0].ptr(zcu);
1775 const res = try self.lowerUav(elf_file, pt, uav, .none, first_exp.src);1771 const res = try self.lowerUav(elf_file, pt, uav, .none, first_exp.src);
1776 switch (res) {1772 switch (res) {
1777 .mcv => {},1773 .mcv => {},
...@@ -1792,7 +1788,7 @@ pub fn updateExports(...@@ -1792,7 +1788,7 @@ pub fn updateExports(
1792 const esym_shndx = self.symtab.items(.shndx)[esym_index];1788 const esym_shndx = self.symtab.items(.shndx)[esym_index];
17931789
1794 for (export_indices) |export_idx| {1790 for (export_indices) |export_idx| {
1795 const exp = zcu.all_exports.items[export_idx];1791 const exp = export_idx.ptr(zcu);
1796 if (exp.opts.section.unwrap()) |section_name| {1792 if (exp.opts.section.unwrap()) |section_name| {
1797 if (!section_name.eqlSlice(".text", &zcu.intern_pool)) {1793 if (!section_name.eqlSlice(".text", &zcu.intern_pool)) {
1798 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);1794 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
...@@ -1849,7 +1845,13 @@ pub fn updateExports(...@@ -1849,7 +1845,13 @@ pub fn updateExports(
18491845
1850pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {1846pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
1851 if (self.dwarf) |*dwarf| {1847 if (self.dwarf) |*dwarf| {
1852 try dwarf.updateLineNumber(pt.zcu, ti_id);1848 const comp = dwarf.bin_file.comp;
1849 const diags = &comp.link_diags;
1850 dwarf.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) {
1851 error.Overflow => return error.Overflow,
1852 error.OutOfMemory => return error.OutOfMemory,
1853 else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}),
1854 };
1853 }1855 }
1854}1856}
18551857
...@@ -1935,8 +1937,8 @@ pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, e...@@ -1935,8 +1937,8 @@ pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, e
1935 const shdr = &slice.items(.shdr)[atom_ptr.output_section_index];1937 const shdr = &slice.items(.shdr)[atom_ptr.output_section_index];
1936 const last_atom_ref = &slice.items(.last_atom)[atom_ptr.output_section_index];1938 const last_atom_ref = &slice.items(.last_atom)[atom_ptr.output_section_index];
19371939
1938 // FIXME:JK this only works if this atom is the only atom in the output section1940 // This only works if this atom is the only atom in the output section. In
1939 // In every other case, we need to redo the prev/next links1941 // every other case, we need to redo the prev/next links.
1940 if (last_atom_ref.eql(atom_ptr.ref())) last_atom_ref.* = .{};1942 if (last_atom_ref.eql(atom_ptr.ref())) last_atom_ref.* = .{};
19411943
1942 const alloc_res = try elf_file.allocateChunk(.{1944 const alloc_res = try elf_file.allocateChunk(.{
src/link/Elf/eh_frame.zig+1-1
...@@ -611,7 +611,7 @@ fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void {...@@ -611,7 +611,7 @@ fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void {
611 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),611 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
612 rel.r_offset,612 rel.r_offset,
613 });613 });
614 try err.addNote("in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()});614 err.addNote("in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()});
615 return error.RelocFailure;615 return error.RelocFailure;
616}616}
617617
src/link/Elf/relocatable.zig+6-6
...@@ -1,8 +1,8 @@...@@ -1,8 +1,8 @@
1pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void {1pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) !void {
2 const gpa = comp.gpa;2 const gpa = comp.gpa;
3 const diags = &comp.link_diags;3 const diags = &comp.link_diags;
44
5 if (diags.hasErrors()) return error.FlushFailure;5 if (diags.hasErrors()) return error.LinkFailure;
66
7 // First, we flush relocatable object file generated with our backends.7 // First, we flush relocatable object file generated with our backends.
8 if (elf_file.zigObjectPtr()) |zig_object| {8 if (elf_file.zigObjectPtr()) |zig_object| {
...@@ -127,13 +127,13 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!v...@@ -127,13 +127,13 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!v
127 try elf_file.base.file.?.setEndPos(total_size);127 try elf_file.base.file.?.setEndPos(total_size);
128 try elf_file.base.file.?.pwriteAll(buffer.items, 0);128 try elf_file.base.file.?.pwriteAll(buffer.items, 0);
129129
130 if (diags.hasErrors()) return error.FlushFailure;130 if (diags.hasErrors()) return error.LinkFailure;
131}131}
132132
133pub fn flushObject(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void {133pub fn flushObject(elf_file: *Elf, comp: *Compilation) !void {
134 const diags = &comp.link_diags;134 const diags = &comp.link_diags;
135135
136 if (diags.hasErrors()) return error.FlushFailure;136 if (diags.hasErrors()) return error.LinkFailure;
137137
138 // Now, we are ready to resolve the symbols across all input files.138 // Now, we are ready to resolve the symbols across all input files.
139 // We will first resolve the files in the ZigObject, next in the parsed139 // We will first resolve the files in the ZigObject, next in the parsed
...@@ -179,7 +179,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void...@@ -179,7 +179,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void
179 try elf_file.writeShdrTable();179 try elf_file.writeShdrTable();
180 try elf_file.writeElfHeader();180 try elf_file.writeElfHeader();
181181
182 if (diags.hasErrors()) return error.FlushFailure;182 if (diags.hasErrors()) return error.LinkFailure;
183}183}
184184
185fn claimUnresolved(elf_file: *Elf) void {185fn claimUnresolved(elf_file: *Elf) void {
src/link/MachO.zig+125-56
...@@ -434,7 +434,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -434,7 +434,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
434 // libc/libSystem dep434 // libc/libSystem dep
435 self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) {435 self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) {
436 error.MissingLibSystem => {}, // already reported436 error.MissingLibSystem => {}, // already reported
437 else => |e| return e, // TODO: convert into an error437 else => |e| return diags.fail("failed to resolve libSystem: {s}", .{@errorName(e)}),
438 };438 };
439439
440 for (comp.link_inputs) |link_input| switch (link_input) {440 for (comp.link_inputs) |link_input| switch (link_input) {
...@@ -481,7 +481,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -481,7 +481,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
481 }481 }
482 };482 };
483483
484 if (diags.hasErrors()) return error.FlushFailure;484 if (diags.hasErrors()) return error.LinkFailure;
485485
486 {486 {
487 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));487 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
...@@ -494,14 +494,17 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -494,14 +494,17 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
494494
495 try self.resolveSymbols();495 try self.resolveSymbols();
496 try self.convertTentativeDefsAndResolveSpecialSymbols();496 try self.convertTentativeDefsAndResolveSpecialSymbols();
497 try self.dedupLiterals();497 self.dedupLiterals() catch |err| switch (err) {
498 error.LinkFailure => return error.LinkFailure,
499 else => |e| return diags.fail("failed to deduplicate literals: {s}", .{@errorName(e)}),
500 };
498501
499 if (self.base.gc_sections) {502 if (self.base.gc_sections) {
500 try dead_strip.gcAtoms(self);503 try dead_strip.gcAtoms(self);
501 }504 }
502505
503 self.checkDuplicates() catch |err| switch (err) {506 self.checkDuplicates() catch |err| switch (err) {
504 error.HasDuplicates => return error.FlushFailure,507 error.HasDuplicates => return error.LinkFailure,
505 else => |e| return diags.fail("failed to check for duplicate symbol definitions: {s}", .{@errorName(e)}),508 else => |e| return diags.fail("failed to check for duplicate symbol definitions: {s}", .{@errorName(e)}),
506 };509 };
507510
...@@ -516,7 +519,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -516,7 +519,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
516 self.claimUnresolved();519 self.claimUnresolved();
517520
518 self.scanRelocs() catch |err| switch (err) {521 self.scanRelocs() catch |err| switch (err) {
519 error.HasUndefinedSymbols => return error.FlushFailure,522 error.HasUndefinedSymbols => return error.LinkFailure,
520 else => |e| return diags.fail("failed to scan relocations: {s}", .{@errorName(e)}),523 else => |e| return diags.fail("failed to scan relocations: {s}", .{@errorName(e)}),
521 };524 };
522525
...@@ -529,7 +532,10 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -529,7 +532,10 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
529 try self.generateUnwindInfo();532 try self.generateUnwindInfo();
530533
531 try self.initSegments();534 try self.initSegments();
532 try self.allocateSections();535 self.allocateSections() catch |err| switch (err) {
536 error.LinkFailure => return error.LinkFailure,
537 else => |e| return diags.fail("failed to allocate sections: {s}", .{@errorName(e)}),
538 };
533 self.allocateSegments();539 self.allocateSegments();
534 self.allocateSyntheticSymbols();540 self.allocateSyntheticSymbols();
535541
...@@ -543,7 +549,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -543,7 +549,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
543549
544 if (self.getZigObject()) |zo| {550 if (self.getZigObject()) |zo| {
545 zo.resolveRelocs(self) catch |err| switch (err) {551 zo.resolveRelocs(self) catch |err| switch (err) {
546 error.ResolveFailed => return error.FlushFailure,552 error.ResolveFailed => return error.LinkFailure,
547 else => |e| return e,553 else => |e| return e,
548 };554 };
549 }555 }
...@@ -551,7 +557,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -551,7 +557,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
551557
552 try self.writeSectionsToFile();558 try self.writeSectionsToFile();
553 try self.allocateLinkeditSegment();559 try self.allocateLinkeditSegment();
554 try self.writeLinkeditSectionsToFile();560 self.writeLinkeditSectionsToFile() catch |err| switch (err) {
561 error.OutOfMemory => return error.OutOfMemory,
562 error.LinkFailure => return error.LinkFailure,
563 else => |e| return diags.fail("failed to write linkedit sections to file: {s}", .{@errorName(e)}),
564 };
555565
556 var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: {566 var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: {
557 // Preallocate space for the code signature.567 // Preallocate space for the code signature.
...@@ -561,7 +571,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -561,7 +571,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
561 // where the code signature goes into.571 // where the code signature goes into.
562 var codesig = CodeSignature.init(self.getPageSize());572 var codesig = CodeSignature.init(self.getPageSize());
563 codesig.code_directory.ident = fs.path.basename(self.base.emit.sub_path);573 codesig.code_directory.ident = fs.path.basename(self.base.emit.sub_path);
564 if (self.entitlements) |path| try codesig.addEntitlements(gpa, path);574 if (self.entitlements) |path| codesig.addEntitlements(gpa, path) catch |err|
575 return diags.fail("failed to add entitlements from {s}: {s}", .{ path, @errorName(err) });
565 try self.writeCodeSignaturePadding(&codesig);576 try self.writeCodeSignaturePadding(&codesig);
566 break :blk codesig;577 break :blk codesig;
567 } else null;578 } else null;
...@@ -573,15 +584,34 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -573,15 +584,34 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
573 self.getPageSize(),584 self.getPageSize(),
574 );585 );
575586
576 const ncmds, const sizeofcmds, const uuid_cmd_offset = try self.writeLoadCommands();587 const ncmds, const sizeofcmds, const uuid_cmd_offset = self.writeLoadCommands() catch |err| switch (err) {
588 error.NoSpaceLeft => unreachable,
589 error.OutOfMemory => return error.OutOfMemory,
590 error.LinkFailure => return error.LinkFailure,
591 };
577 try self.writeHeader(ncmds, sizeofcmds);592 try self.writeHeader(ncmds, sizeofcmds);
578 try self.writeUuid(uuid_cmd_offset, self.requiresCodeSig());593 self.writeUuid(uuid_cmd_offset, self.requiresCodeSig()) catch |err| switch (err) {
579 if (self.getDebugSymbols()) |dsym| try dsym.flushModule(self);594 error.OutOfMemory => return error.OutOfMemory,
595 error.LinkFailure => return error.LinkFailure,
596 else => |e| return diags.fail("failed to calculate and write uuid: {s}", .{@errorName(e)}),
597 };
598 if (self.getDebugSymbols()) |dsym| dsym.flushModule(self) catch |err| switch (err) {
599 error.OutOfMemory => return error.OutOfMemory,
600 else => |e| return diags.fail("failed to get debug symbols: {s}", .{@errorName(e)}),
601 };
580602
603 // Code signing always comes last.
581 if (codesig) |*csig| {604 if (codesig) |*csig| {
582 try self.writeCodeSignature(csig); // code signing always comes last605 self.writeCodeSignature(csig) catch |err| switch (err) {
606 error.OutOfMemory => return error.OutOfMemory,
607 error.LinkFailure => return error.LinkFailure,
608 else => |e| return diags.fail("failed to write code signature: {s}", .{@errorName(e)}),
609 };
583 const emit = self.base.emit;610 const emit = self.base.emit;
584 try invalidateKernelCache(emit.root_dir.handle, emit.sub_path);611 invalidateKernelCache(emit.root_dir.handle, emit.sub_path) catch |err| switch (err) {
612 error.OutOfMemory => return error.OutOfMemory,
613 else => |e| return diags.fail("failed to invalidate kernel cache: {s}", .{@errorName(e)}),
614 };
585 }615 }
586}616}
587617
...@@ -1545,21 +1575,21 @@ fn reportUndefs(self: *MachO) !void {...@@ -1545,21 +1575,21 @@ fn reportUndefs(self: *MachO) !void {
1545 try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});1575 try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});
15461576
1547 switch (notes) {1577 switch (notes) {
1548 .force_undefined => try err.addNote("referenced with linker flag -u", .{}),1578 .force_undefined => err.addNote("referenced with linker flag -u", .{}),
1549 .entry => try err.addNote("referenced with linker flag -e", .{}),1579 .entry => err.addNote("referenced with linker flag -e", .{}),
1550 .dyld_stub_binder, .objc_msgsend => try err.addNote("referenced implicitly", .{}),1580 .dyld_stub_binder, .objc_msgsend => err.addNote("referenced implicitly", .{}),
1551 .refs => |refs| {1581 .refs => |refs| {
1552 var inote: usize = 0;1582 var inote: usize = 0;
1553 while (inote < @min(refs.items.len, max_notes)) : (inote += 1) {1583 while (inote < @min(refs.items.len, max_notes)) : (inote += 1) {
1554 const ref = refs.items[inote];1584 const ref = refs.items[inote];
1555 const file = self.getFile(ref.file).?;1585 const file = self.getFile(ref.file).?;
1556 const atom = ref.getAtom(self).?;1586 const atom = ref.getAtom(self).?;
1557 try err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });1587 err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
1558 }1588 }
15591589
1560 if (refs.items.len > max_notes) {1590 if (refs.items.len > max_notes) {
1561 const remaining = refs.items.len - max_notes;1591 const remaining = refs.items.len - max_notes;
1562 try err.addNote("referenced {d} more times", .{remaining});1592 err.addNote("referenced {d} more times", .{remaining});
1563 }1593 }
1564 },1594 },
1565 }1595 }
...@@ -2171,7 +2201,7 @@ fn allocateSections(self: *MachO) !void {...@@ -2171,7 +2201,7 @@ fn allocateSections(self: *MachO) !void {
2171 fileoff = mem.alignForward(u32, fileoff, page_size);2201 fileoff = mem.alignForward(u32, fileoff, page_size);
2172 }2202 }
21732203
2174 const alignment = try math.powi(u32, 2, header.@"align");2204 const alignment = try self.alignPow(header.@"align");
21752205
2176 vmaddr = mem.alignForward(u64, vmaddr, alignment);2206 vmaddr = mem.alignForward(u64, vmaddr, alignment);
2177 header.addr = vmaddr;2207 header.addr = vmaddr;
...@@ -2327,7 +2357,7 @@ fn allocateLinkeditSegment(self: *MachO) !void {...@@ -2327,7 +2357,7 @@ fn allocateLinkeditSegment(self: *MachO) !void {
2327 seg.vmaddr = mem.alignForward(u64, vmaddr, page_size);2357 seg.vmaddr = mem.alignForward(u64, vmaddr, page_size);
2328 seg.fileoff = mem.alignForward(u64, fileoff, page_size);2358 seg.fileoff = mem.alignForward(u64, fileoff, page_size);
23292359
2330 var off = math.cast(u32, seg.fileoff) orelse return error.Overflow;2360 var off = try self.cast(u32, seg.fileoff);
2331 // DYLD_INFO_ONLY2361 // DYLD_INFO_ONLY
2332 {2362 {
2333 const cmd = &self.dyld_info_cmd;2363 const cmd = &self.dyld_info_cmd;
...@@ -2392,7 +2422,7 @@ fn resizeSections(self: *MachO) !void {...@@ -2392,7 +2422,7 @@ fn resizeSections(self: *MachO) !void {
2392 if (header.isZerofill()) continue;2422 if (header.isZerofill()) continue;
2393 if (self.isZigSection(@intCast(n_sect))) continue; // TODO this is horrible2423 if (self.isZigSection(@intCast(n_sect))) continue; // TODO this is horrible
2394 const cpu_arch = self.getTarget().cpu.arch;2424 const cpu_arch = self.getTarget().cpu.arch;
2395 const size = math.cast(usize, header.size) orelse return error.Overflow;2425 const size = try self.cast(usize, header.size);
2396 try out.resize(self.base.comp.gpa, size);2426 try out.resize(self.base.comp.gpa, size);
2397 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;2427 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
2398 @memset(out.items, padding_byte);2428 @memset(out.items, padding_byte);
...@@ -2489,7 +2519,7 @@ fn writeThunkWorker(self: *MachO, thunk: Thunk) void {...@@ -2489,7 +2519,7 @@ fn writeThunkWorker(self: *MachO, thunk: Thunk) void {
24892519
2490 const doWork = struct {2520 const doWork = struct {
2491 fn doWork(th: Thunk, buffer: []u8, macho_file: *MachO) !void {2521 fn doWork(th: Thunk, buffer: []u8, macho_file: *MachO) !void {
2492 const off = math.cast(usize, th.value) orelse return error.Overflow;2522 const off = try macho_file.cast(usize, th.value);
2493 const size = th.size();2523 const size = th.size();
2494 var stream = std.io.fixedBufferStream(buffer[off..][0..size]);2524 var stream = std.io.fixedBufferStream(buffer[off..][0..size]);
2495 try th.write(macho_file, stream.writer());2525 try th.write(macho_file, stream.writer());
...@@ -2601,7 +2631,7 @@ fn writeSectionsToFile(self: *MachO) !void {...@@ -2601,7 +2631,7 @@ fn writeSectionsToFile(self: *MachO) !void {
26012631
2602 const slice = self.sections.slice();2632 const slice = self.sections.slice();
2603 for (slice.items(.header), slice.items(.out)) |header, out| {2633 for (slice.items(.header), slice.items(.out)) |header, out| {
2604 try self.base.file.?.pwriteAll(out.items, header.offset);2634 try self.pwriteAll(out.items, header.offset);
2605 }2635 }
2606}2636}
26072637
...@@ -2644,7 +2674,7 @@ fn writeDyldInfo(self: *MachO) !void {...@@ -2644,7 +2674,7 @@ fn writeDyldInfo(self: *MachO) !void {
2644 try self.lazy_bind_section.write(writer);2674 try self.lazy_bind_section.write(writer);
2645 try stream.seekTo(cmd.export_off - base_off);2675 try stream.seekTo(cmd.export_off - base_off);
2646 try self.export_trie.write(writer);2676 try self.export_trie.write(writer);
2647 try self.base.file.?.pwriteAll(buffer, cmd.rebase_off);2677 try self.pwriteAll(buffer, cmd.rebase_off);
2648}2678}
26492679
2650pub fn writeDataInCode(self: *MachO) !void {2680pub fn writeDataInCode(self: *MachO) !void {
...@@ -2655,7 +2685,7 @@ pub fn writeDataInCode(self: *MachO) !void {...@@ -2655,7 +2685,7 @@ pub fn writeDataInCode(self: *MachO) !void {
2655 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.data_in_code.size());2685 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.data_in_code.size());
2656 defer buffer.deinit();2686 defer buffer.deinit();
2657 try self.data_in_code.write(self, buffer.writer());2687 try self.data_in_code.write(self, buffer.writer());
2658 try self.base.file.?.pwriteAll(buffer.items, cmd.dataoff);2688 try self.pwriteAll(buffer.items, cmd.dataoff);
2659}2689}
26602690
2661fn writeIndsymtab(self: *MachO) !void {2691fn writeIndsymtab(self: *MachO) !void {
...@@ -2667,15 +2697,15 @@ fn writeIndsymtab(self: *MachO) !void {...@@ -2667,15 +2697,15 @@ fn writeIndsymtab(self: *MachO) !void {
2667 var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size);2697 var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size);
2668 defer buffer.deinit();2698 defer buffer.deinit();
2669 try self.indsymtab.write(self, buffer.writer());2699 try self.indsymtab.write(self, buffer.writer());
2670 try self.base.file.?.pwriteAll(buffer.items, cmd.indirectsymoff);2700 try self.pwriteAll(buffer.items, cmd.indirectsymoff);
2671}2701}
26722702
2673pub fn writeSymtabToFile(self: *MachO) !void {2703pub fn writeSymtabToFile(self: *MachO) !void {
2674 const tracy = trace(@src());2704 const tracy = trace(@src());
2675 defer tracy.end();2705 defer tracy.end();
2676 const cmd = self.symtab_cmd;2706 const cmd = self.symtab_cmd;
2677 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);2707 try self.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
2678 try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff);2708 try self.pwriteAll(self.strtab.items, cmd.stroff);
2679}2709}
26802710
2681fn writeUnwindInfo(self: *MachO) !void {2711fn writeUnwindInfo(self: *MachO) !void {
...@@ -2686,20 +2716,20 @@ fn writeUnwindInfo(self: *MachO) !void {...@@ -2686,20 +2716,20 @@ fn writeUnwindInfo(self: *MachO) !void {
26862716
2687 if (self.eh_frame_sect_index) |index| {2717 if (self.eh_frame_sect_index) |index| {
2688 const header = self.sections.items(.header)[index];2718 const header = self.sections.items(.header)[index];
2689 const size = math.cast(usize, header.size) orelse return error.Overflow;2719 const size = try self.cast(usize, header.size);
2690 const buffer = try gpa.alloc(u8, size);2720 const buffer = try gpa.alloc(u8, size);
2691 defer gpa.free(buffer);2721 defer gpa.free(buffer);
2692 eh_frame.write(self, buffer);2722 eh_frame.write(self, buffer);
2693 try self.base.file.?.pwriteAll(buffer, header.offset);2723 try self.pwriteAll(buffer, header.offset);
2694 }2724 }
26952725
2696 if (self.unwind_info_sect_index) |index| {2726 if (self.unwind_info_sect_index) |index| {
2697 const header = self.sections.items(.header)[index];2727 const header = self.sections.items(.header)[index];
2698 const size = math.cast(usize, header.size) orelse return error.Overflow;2728 const size = try self.cast(usize, header.size);
2699 const buffer = try gpa.alloc(u8, size);2729 const buffer = try gpa.alloc(u8, size);
2700 defer gpa.free(buffer);2730 defer gpa.free(buffer);
2701 try self.unwind_info.write(self, buffer);2731 try self.unwind_info.write(self, buffer);
2702 try self.base.file.?.pwriteAll(buffer, header.offset);2732 try self.pwriteAll(buffer, header.offset);
2703 }2733 }
2704}2734}
27052735
...@@ -2890,7 +2920,7 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {...@@ -2890,7 +2920,7 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
28902920
2891 assert(stream.pos == needed_size);2921 assert(stream.pos == needed_size);
28922922
2893 try self.base.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64));2923 try self.pwriteAll(buffer, @sizeOf(macho.mach_header_64));
28942924
2895 return .{ ncmds, buffer.len, uuid_cmd_offset };2925 return .{ ncmds, buffer.len, uuid_cmd_offset };
2896}2926}
...@@ -2944,7 +2974,7 @@ fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void {...@@ -2944,7 +2974,7 @@ fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void {
29442974
2945 log.debug("writing Mach-O header {}", .{header});2975 log.debug("writing Mach-O header {}", .{header});
29462976
2947 try self.base.file.?.pwriteAll(mem.asBytes(&header), 0);2977 try self.pwriteAll(mem.asBytes(&header), 0);
2948}2978}
29492979
2950fn writeUuid(self: *MachO, uuid_cmd_offset: u64, has_codesig: bool) !void {2980fn writeUuid(self: *MachO, uuid_cmd_offset: u64, has_codesig: bool) !void {
...@@ -2954,7 +2984,7 @@ fn writeUuid(self: *MachO, uuid_cmd_offset: u64, has_codesig: bool) !void {...@@ -2954,7 +2984,7 @@ fn writeUuid(self: *MachO, uuid_cmd_offset: u64, has_codesig: bool) !void {
2954 } else self.codesig_cmd.dataoff;2984 } else self.codesig_cmd.dataoff;
2955 try calcUuid(self.base.comp, self.base.file.?, file_size, &self.uuid_cmd.uuid);2985 try calcUuid(self.base.comp, self.base.file.?, file_size, &self.uuid_cmd.uuid);
2956 const offset = uuid_cmd_offset + @sizeOf(macho.load_command);2986 const offset = uuid_cmd_offset + @sizeOf(macho.load_command);
2957 try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset);2987 try self.pwriteAll(&self.uuid_cmd.uuid, offset);
2958}2988}
29592989
2960pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {2990pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
...@@ -2968,7 +2998,7 @@ pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {...@@ -2968,7 +2998,7 @@ pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
2968 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });2998 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2969 // Pad out the space. We need to do this to calculate valid hashes for everything in the file2999 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
2970 // except for code signature data.3000 // except for code signature data.
2971 try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);3001 try self.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
29723002
2973 self.codesig_cmd.dataoff = @as(u32, @intCast(offset));3003 self.codesig_cmd.dataoff = @as(u32, @intCast(offset));
2974 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));3004 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));
...@@ -2995,10 +3025,16 @@ pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {...@@ -2995,10 +3025,16 @@ pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {
2995 offset + buffer.items.len,3025 offset + buffer.items.len,
2996 });3026 });
29973027
2998 try self.base.file.?.pwriteAll(buffer.items, offset);3028 try self.pwriteAll(buffer.items, offset);
2999}3029}
30003030
3001pub fn updateFunc(self: *MachO, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {3031pub fn updateFunc(
3032 self: *MachO,
3033 pt: Zcu.PerThread,
3034 func_index: InternPool.Index,
3035 air: Air,
3036 liveness: Liveness,
3037) link.File.UpdateNavError!void {
3002 if (build_options.skip_non_native and builtin.object_format != .macho) {3038 if (build_options.skip_non_native and builtin.object_format != .macho) {
3003 @panic("Attempted to compile for object format that was disabled by build configuration");3039 @panic("Attempted to compile for object format that was disabled by build configuration");
3004 }3040 }
...@@ -3006,7 +3042,7 @@ pub fn updateFunc(self: *MachO, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -3006,7 +3042,7 @@ pub fn updateFunc(self: *MachO, pt: Zcu.PerThread, func_index: InternPool.Index,
3006 return self.getZigObject().?.updateFunc(self, pt, func_index, air, liveness);3042 return self.getZigObject().?.updateFunc(self, pt, func_index, air, liveness);
3007}3043}
30083044
3009pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {3045pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void {
3010 if (build_options.skip_non_native and builtin.object_format != .macho) {3046 if (build_options.skip_non_native and builtin.object_format != .macho) {
3011 @panic("Attempted to compile for object format that was disabled by build configuration");3047 @panic("Attempted to compile for object format that was disabled by build configuration");
3012 }3048 }
...@@ -3023,7 +3059,7 @@ pub fn updateExports(...@@ -3023,7 +3059,7 @@ pub fn updateExports(
3023 self: *MachO,3059 self: *MachO,
3024 pt: Zcu.PerThread,3060 pt: Zcu.PerThread,
3025 exported: Zcu.Exported,3061 exported: Zcu.Exported,
3026 export_indices: []const u32,3062 export_indices: []const Zcu.Export.Index,
3027) link.File.UpdateExportsError!void {3063) link.File.UpdateExportsError!void {
3028 if (build_options.skip_non_native and builtin.object_format != .macho) {3064 if (build_options.skip_non_native and builtin.object_format != .macho) {
3029 @panic("Attempted to compile for object format that was disabled by build configuration");3065 @panic("Attempted to compile for object format that was disabled by build configuration");
...@@ -3199,7 +3235,7 @@ fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64...@@ -3199,7 +3235,7 @@ fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64
3199 const gpa = self.base.comp.gpa;3235 const gpa = self.base.comp.gpa;
3200 try self.copyRangeAll(old_offset, new_offset, size);3236 try self.copyRangeAll(old_offset, new_offset, size);
3201 const size_u = math.cast(usize, size) orelse return error.Overflow;3237 const size_u = math.cast(usize, size) orelse return error.Overflow;
3202 const zeroes = try gpa.alloc(u8, size_u);3238 const zeroes = try gpa.alloc(u8, size_u); // TODO no need to allocate here.
3203 defer gpa.free(zeroes);3239 defer gpa.free(zeroes);
3204 @memset(zeroes, 0);3240 @memset(zeroes, 0);
3205 try self.base.file.?.pwriteAll(zeroes, old_offset);3241 try self.base.file.?.pwriteAll(zeroes, old_offset);
...@@ -3306,10 +3342,9 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {...@@ -3306,10 +3342,9 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
3306 const allocSect = struct {3342 const allocSect = struct {
3307 fn allocSect(macho_file: *MachO, sect_id: u8, size: u64) !void {3343 fn allocSect(macho_file: *MachO, sect_id: u8, size: u64) !void {
3308 const sect = &macho_file.sections.items(.header)[sect_id];3344 const sect = &macho_file.sections.items(.header)[sect_id];
3309 const alignment = try math.powi(u32, 2, sect.@"align");3345 const alignment = try macho_file.alignPow(sect.@"align");
3310 if (!sect.isZerofill()) {3346 if (!sect.isZerofill()) {
3311 sect.offset = math.cast(u32, try macho_file.findFreeSpace(size, alignment)) orelse3347 sect.offset = try macho_file.cast(u32, try macho_file.findFreeSpace(size, alignment));
3312 return error.Overflow;
3313 }3348 }
3314 sect.addr = macho_file.findFreeSpaceVirtual(size, alignment);3349 sect.addr = macho_file.findFreeSpaceVirtual(size, alignment);
3315 sect.size = size;3350 sect.size = size;
...@@ -3441,8 +3476,8 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo...@@ -3441,8 +3476,8 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo
3441 seg_id,3476 seg_id,
3442 seg.segName(),3477 seg.segName(),
3443 });3478 });
3444 try err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{});3479 err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{});
3445 try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});3480 err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
3446 }3481 }
34473482
3448 seg.vmsize = needed_size;3483 seg.vmsize = needed_size;
...@@ -3744,7 +3779,7 @@ pub fn reportParseError2(...@@ -3744,7 +3779,7 @@ pub fn reportParseError2(
3744 const diags = &self.base.comp.link_diags;3779 const diags = &self.base.comp.link_diags;
3745 var err = try diags.addErrorWithNotes(1);3780 var err = try diags.addErrorWithNotes(1);
3746 try err.addMsg(format, args);3781 try err.addMsg(format, args);
3747 try err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()});3782 err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()});
3748}3783}
37493784
3750fn reportMissingDependencyError(3785fn reportMissingDependencyError(
...@@ -3758,10 +3793,10 @@ fn reportMissingDependencyError(...@@ -3758,10 +3793,10 @@ fn reportMissingDependencyError(
3758 const diags = &self.base.comp.link_diags;3793 const diags = &self.base.comp.link_diags;
3759 var err = try diags.addErrorWithNotes(2 + checked_paths.len);3794 var err = try diags.addErrorWithNotes(2 + checked_paths.len);
3760 try err.addMsg(format, args);3795 try err.addMsg(format, args);
3761 try err.addNote("while resolving {s}", .{path});3796 err.addNote("while resolving {s}", .{path});
3762 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});3797 err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3763 for (checked_paths) |p| {3798 for (checked_paths) |p| {
3764 try err.addNote("tried {s}", .{p});3799 err.addNote("tried {s}", .{p});
3765 }3800 }
3766}3801}
37673802
...@@ -3775,8 +3810,8 @@ fn reportDependencyError(...@@ -3775,8 +3810,8 @@ fn reportDependencyError(
3775 const diags = &self.base.comp.link_diags;3810 const diags = &self.base.comp.link_diags;
3776 var err = try diags.addErrorWithNotes(2);3811 var err = try diags.addErrorWithNotes(2);
3777 try err.addMsg(format, args);3812 try err.addMsg(format, args);
3778 try err.addNote("while parsing {s}", .{path});3813 err.addNote("while parsing {s}", .{path});
3779 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});3814 err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3780}3815}
37813816
3782fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {3817fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
...@@ -3806,17 +3841,17 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {...@@ -3806,17 +3841,17 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
38063841
3807 var err = try diags.addErrorWithNotes(nnotes + 1);3842 var err = try diags.addErrorWithNotes(nnotes + 1);
3808 try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)});3843 try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)});
3809 try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()});3844 err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()});
38103845
3811 var inote: usize = 0;3846 var inote: usize = 0;
3812 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {3847 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
3813 const file = self.getFile(notes.items[inote]).?;3848 const file = self.getFile(notes.items[inote]).?;
3814 try err.addNote("defined by {}", .{file.fmtPath()});3849 err.addNote("defined by {}", .{file.fmtPath()});
3815 }3850 }
38163851
3817 if (notes.items.len > max_notes) {3852 if (notes.items.len > max_notes) {
3818 const remaining = notes.items.len - max_notes;3853 const remaining = notes.items.len - max_notes;
3819 try err.addNote("defined {d} more times", .{remaining});3854 err.addNote("defined {d} more times", .{remaining});
3820 }3855 }
3821 }3856 }
3822 return error.HasDuplicates;3857 return error.HasDuplicates;
...@@ -5310,6 +5345,40 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {...@@ -5310,6 +5345,40 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
5310 return true;5345 return true;
5311}5346}
53125347
5348pub fn pwriteAll(macho_file: *MachO, bytes: []const u8, offset: u64) error{LinkFailure}!void {
5349 const comp = macho_file.base.comp;
5350 const diags = &comp.link_diags;
5351 macho_file.base.file.?.pwriteAll(bytes, offset) catch |err| {
5352 return diags.fail("failed to write: {s}", .{@errorName(err)});
5353 };
5354}
5355
5356pub fn setEndPos(macho_file: *MachO, length: u64) error{LinkFailure}!void {
5357 const comp = macho_file.base.comp;
5358 const diags = &comp.link_diags;
5359 macho_file.base.file.?.setEndPos(length) catch |err| {
5360 return diags.fail("failed to set file end pos: {s}", .{@errorName(err)});
5361 };
5362}
5363
5364pub fn cast(macho_file: *MachO, comptime T: type, x: anytype) error{LinkFailure}!T {
5365 return std.math.cast(T, x) orelse {
5366 const comp = macho_file.base.comp;
5367 const diags = &comp.link_diags;
5368 return diags.fail("encountered {d}, overflowing {d}-bit value", .{ x, @bitSizeOf(T) });
5369 };
5370}
5371
5372pub fn alignPow(macho_file: *MachO, x: u32) error{LinkFailure}!u32 {
5373 const result, const ov = @shlWithOverflow(@as(u32, 1), try cast(macho_file, u5, x));
5374 if (ov != 0) {
5375 const comp = macho_file.base.comp;
5376 const diags = &comp.link_diags;
5377 return diags.fail("alignment overflow", .{});
5378 }
5379 return result;
5380}
5381
5313/// Branch instruction has 26 bits immediate but is 4 byte aligned.5382/// Branch instruction has 26 bits immediate but is 4 byte aligned.
5314const jump_bits = @bitSizeOf(i28);5383const jump_bits = @bitSizeOf(i28);
5315const max_distance = (1 << (jump_bits - 1));5384const max_distance = (1 << (jump_bits - 1));
src/link/MachO/Atom.zig+7-7
...@@ -909,8 +909,8 @@ const x86_64 = struct {...@@ -909,8 +909,8 @@ const x86_64 = struct {
909 rel.offset,909 rel.offset,
910 rel.fmtPretty(.x86_64),910 rel.fmtPretty(.x86_64),
911 });911 });
912 try err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)});912 err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)});
913 try err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()});913 err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()});
914 return error.RelaxFailUnexpectedInstruction;914 return error.RelaxFailUnexpectedInstruction;
915 },915 },
916 }916 }
...@@ -971,7 +971,7 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {...@@ -971,7 +971,7 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {
971 }971 }
972}972}
973973
974pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) !void {974pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) error{ LinkFailure, OutOfMemory }!void {
975 const tracy = trace(@src());975 const tracy = trace(@src());
976 defer tracy.end();976 defer tracy.end();
977977
...@@ -983,15 +983,15 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r...@@ -983,15 +983,15 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r
983 var i: usize = 0;983 var i: usize = 0;
984 for (relocs) |rel| {984 for (relocs) |rel| {
985 defer i += 1;985 defer i += 1;
986 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;986 const rel_offset = try macho_file.cast(usize, rel.offset - self.off);
987 const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow;987 const r_address: i32 = try macho_file.cast(i32, self.value + rel_offset);
988 assert(r_address >= 0);988 assert(r_address >= 0);
989 const r_symbolnum = r_symbolnum: {989 const r_symbolnum = r_symbolnum: {
990 const r_symbolnum: u32 = switch (rel.tag) {990 const r_symbolnum: u32 = switch (rel.tag) {
991 .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1,991 .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1,
992 .@"extern" => rel.getTargetSymbol(self, macho_file).getOutputSymtabIndex(macho_file).?,992 .@"extern" => rel.getTargetSymbol(self, macho_file).getOutputSymtabIndex(macho_file).?,
993 };993 };
994 break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow;994 break :r_symbolnum try macho_file.cast(u24, r_symbolnum);
995 };995 };
996 const r_extern = rel.tag == .@"extern";996 const r_extern = rel.tag == .@"extern";
997 var addend = rel.addend + rel.getRelocAddend(cpu_arch);997 var addend = rel.addend + rel.getRelocAddend(cpu_arch);
...@@ -1027,7 +1027,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r...@@ -1027,7 +1027,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r
1027 } else if (addend > 0) {1027 } else if (addend > 0) {
1028 buffer[i] = .{1028 buffer[i] = .{
1029 .r_address = r_address,1029 .r_address = r_address,
1030 .r_symbolnum = @bitCast(math.cast(i24, addend) orelse return error.Overflow),1030 .r_symbolnum = @bitCast(try macho_file.cast(i24, addend)),
1031 .r_pcrel = 0,1031 .r_pcrel = 0,
1032 .r_length = 2,1032 .r_length = 2,
1033 .r_extern = 0,1033 .r_extern = 0,
src/link/MachO/InternalObject.zig+9-7
...@@ -414,10 +414,11 @@ pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file...@@ -414,10 +414,11 @@ pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file
414 const rel = relocs[0];414 const rel = relocs[0];
415 assert(rel.tag == .@"extern");415 assert(rel.tag == .@"extern");
416 const target = rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?;416 const target = rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?;
417 const target_size = std.math.cast(usize, target.size) orelse return error.Overflow;417 const target_size = try macho_file.cast(usize, target.size);
418 try buffer.ensureUnusedCapacity(target_size);418 try buffer.ensureUnusedCapacity(target_size);
419 buffer.resize(target_size) catch unreachable;419 buffer.resize(target_size) catch unreachable;
420 @memcpy(buffer.items, try self.getSectionData(target.n_sect));420 const section_data = try self.getSectionData(target.n_sect, macho_file);
421 @memcpy(buffer.items, section_data);
421 const res = try lp.insert(gpa, header.type(), buffer.items);422 const res = try lp.insert(gpa, header.type(), buffer.items);
422 buffer.clearRetainingCapacity();423 buffer.clearRetainingCapacity();
423 if (!res.found_existing) {424 if (!res.found_existing) {
...@@ -607,10 +608,11 @@ pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void {...@@ -607,10 +608,11 @@ pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void {
607 if (!atom.isAlive()) continue;608 if (!atom.isAlive()) continue;
608 const sect = atom.getInputSection(macho_file);609 const sect = atom.getInputSection(macho_file);
609 if (sect.isZerofill()) continue;610 if (sect.isZerofill()) continue;
610 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;611 const off = try macho_file.cast(usize, atom.value);
611 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;612 const size = try macho_file.cast(usize, atom.size);
612 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items[off..][0..size];613 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items[off..][0..size];
613 @memcpy(buffer, try self.getSectionData(atom.n_sect));614 const section_data = try self.getSectionData(atom.n_sect, macho_file);
615 @memcpy(buffer, section_data);
614 try atom.resolveRelocs(macho_file, buffer);616 try atom.resolveRelocs(macho_file, buffer);
615 }617 }
616}618}
...@@ -644,13 +646,13 @@ fn addSection(self: *InternalObject, allocator: Allocator, segname: []const u8,...@@ -644,13 +646,13 @@ fn addSection(self: *InternalObject, allocator: Allocator, segname: []const u8,
644 return n_sect;646 return n_sect;
645}647}
646648
647fn getSectionData(self: *const InternalObject, index: u32) error{Overflow}![]const u8 {649fn getSectionData(self: *const InternalObject, index: u32, macho_file: *MachO) error{LinkFailure}![]const u8 {
648 const slice = self.sections.slice();650 const slice = self.sections.slice();
649 assert(index < slice.items(.header).len);651 assert(index < slice.items(.header).len);
650 const sect = slice.items(.header)[index];652 const sect = slice.items(.header)[index];
651 const extra = slice.items(.extra)[index];653 const extra = slice.items(.extra)[index];
652 if (extra.is_objc_methname) {654 if (extra.is_objc_methname) {
653 const size = std.math.cast(usize, sect.size) orelse return error.Overflow;655 const size = try macho_file.cast(usize, sect.size);
654 return self.objc_methnames.items[sect.offset..][0..size];656 return self.objc_methnames.items[sect.offset..][0..size];
655 } else if (extra.is_objc_selref)657 } else if (extra.is_objc_selref)
656 return &self.objc_selrefs658 return &self.objc_selrefs
src/link/MachO/Object.zig+32-34
...@@ -582,7 +582,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO)...@@ -582,7 +582,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO)
582 );582 );
583 return error.MalformedObject;583 return error.MalformedObject;
584 }584 }
585 const num_ptrs = math.cast(usize, @divExact(sect.size, rec_size)) orelse return error.Overflow;585 const num_ptrs = try macho_file.cast(usize, @divExact(sect.size, rec_size));
586586
587 for (0..num_ptrs) |i| {587 for (0..num_ptrs) |i| {
588 const pos: u32 = @as(u32, @intCast(i)) * rec_size;588 const pos: u32 = @as(u32, @intCast(i)) * rec_size;
...@@ -650,8 +650,8 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO...@@ -650,8 +650,8 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
650650
651 for (subs.items) |sub| {651 for (subs.items) |sub| {
652 const atom = self.getAtom(sub.atom).?;652 const atom = self.getAtom(sub.atom).?;
653 const atom_off = math.cast(usize, atom.off) orelse return error.Overflow;653 const atom_off = try macho_file.cast(usize, atom.off);
654 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;654 const atom_size = try macho_file.cast(usize, atom.size);
655 const atom_data = data[atom_off..][0..atom_size];655 const atom_data = data[atom_off..][0..atom_size];
656 const res = try lp.insert(gpa, header.type(), atom_data);656 const res = try lp.insert(gpa, header.type(), atom_data);
657 if (!res.found_existing) {657 if (!res.found_existing) {
...@@ -674,8 +674,8 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO...@@ -674,8 +674,8 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
674 .local => rel.getTargetAtom(atom.*, macho_file),674 .local => rel.getTargetAtom(atom.*, macho_file),
675 .@"extern" => rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?,675 .@"extern" => rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?,
676 };676 };
677 const addend = math.cast(u32, rel.addend) orelse return error.Overflow;677 const addend = try macho_file.cast(u32, rel.addend);
678 const target_size = math.cast(usize, target.size) orelse return error.Overflow;678 const target_size = try macho_file.cast(usize, target.size);
679 try buffer.ensureUnusedCapacity(target_size);679 try buffer.ensureUnusedCapacity(target_size);
680 buffer.resize(target_size) catch unreachable;680 buffer.resize(target_size) catch unreachable;
681 const gop = try sections_data.getOrPut(target.n_sect);681 const gop = try sections_data.getOrPut(target.n_sect);
...@@ -683,7 +683,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO...@@ -683,7 +683,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
683 gop.value_ptr.* = try self.readSectionData(gpa, file, @intCast(target.n_sect));683 gop.value_ptr.* = try self.readSectionData(gpa, file, @intCast(target.n_sect));
684 }684 }
685 const data = gop.value_ptr.*;685 const data = gop.value_ptr.*;
686 const target_off = math.cast(usize, target.off) orelse return error.Overflow;686 const target_off = try macho_file.cast(usize, target.off);
687 @memcpy(buffer.items, data[target_off..][0..target_size]);687 @memcpy(buffer.items, data[target_off..][0..target_size]);
688 const res = try lp.insert(gpa, header.type(), buffer.items[addend..]);688 const res = try lp.insert(gpa, header.type(), buffer.items[addend..]);
689 buffer.clearRetainingCapacity();689 buffer.clearRetainingCapacity();
...@@ -1033,7 +1033,7 @@ fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fi...@@ -1033,7 +1033,7 @@ fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fi
1033 const sect = slice.items(.header)[sect_id];1033 const sect = slice.items(.header)[sect_id];
1034 const relocs = slice.items(.relocs)[sect_id];1034 const relocs = slice.items(.relocs)[sect_id];
10351035
1036 const size = math.cast(usize, sect.size) orelse return error.Overflow;1036 const size = try macho_file.cast(usize, sect.size);
1037 try self.eh_frame_data.resize(allocator, size);1037 try self.eh_frame_data.resize(allocator, size);
1038 const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset);1038 const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset);
1039 if (amt != self.eh_frame_data.items.len) return error.InputOutput;1039 if (amt != self.eh_frame_data.items.len) return error.InputOutput;
...@@ -1696,7 +1696,7 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void {...@@ -1696,7 +1696,7 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void {
16961696
1697pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {1697pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {
1698 // Header1698 // Header
1699 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;1699 const size = try macho_file.cast(usize, self.output_ar_state.size);
1700 const basename = std.fs.path.basename(self.path.sub_path);1700 const basename = std.fs.path.basename(self.path.sub_path);
1701 try Archive.writeHeader(basename, size, ar_format, writer);1701 try Archive.writeHeader(basename, size, ar_format, writer);
1702 // Data1702 // Data
...@@ -1826,7 +1826,7 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {...@@ -1826,7 +1826,7 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {
18261826
1827 for (headers, 0..) |header, n_sect| {1827 for (headers, 0..) |header, n_sect| {
1828 if (header.isZerofill()) continue;1828 if (header.isZerofill()) continue;
1829 const size = math.cast(usize, header.size) orelse return error.Overflow;1829 const size = try macho_file.cast(usize, header.size);
1830 const data = try gpa.alloc(u8, size);1830 const data = try gpa.alloc(u8, size);
1831 const amt = try file.preadAll(data, header.offset + self.offset);1831 const amt = try file.preadAll(data, header.offset + self.offset);
1832 if (amt != data.len) return error.InputOutput;1832 if (amt != data.len) return error.InputOutput;
...@@ -1837,9 +1837,9 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {...@@ -1837,9 +1837,9 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {
1837 if (!atom.isAlive()) continue;1837 if (!atom.isAlive()) continue;
1838 const sect = atom.getInputSection(macho_file);1838 const sect = atom.getInputSection(macho_file);
1839 if (sect.isZerofill()) continue;1839 if (sect.isZerofill()) continue;
1840 const value = math.cast(usize, atom.value) orelse return error.Overflow;1840 const value = try macho_file.cast(usize, atom.value);
1841 const off = math.cast(usize, atom.off) orelse return error.Overflow;1841 const off = try macho_file.cast(usize, atom.off);
1842 const size = math.cast(usize, atom.size) orelse return error.Overflow;1842 const size = try macho_file.cast(usize, atom.size);
1843 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;1843 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
1844 const data = sections_data[atom.n_sect];1844 const data = sections_data[atom.n_sect];
1845 @memcpy(buffer[value..][0..size], data[off..][0..size]);1845 @memcpy(buffer[value..][0..size], data[off..][0..size]);
...@@ -1865,7 +1865,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {...@@ -1865,7 +1865,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
18651865
1866 for (headers, 0..) |header, n_sect| {1866 for (headers, 0..) |header, n_sect| {
1867 if (header.isZerofill()) continue;1867 if (header.isZerofill()) continue;
1868 const size = math.cast(usize, header.size) orelse return error.Overflow;1868 const size = try macho_file.cast(usize, header.size);
1869 const data = try gpa.alloc(u8, size);1869 const data = try gpa.alloc(u8, size);
1870 const amt = try file.preadAll(data, header.offset + self.offset);1870 const amt = try file.preadAll(data, header.offset + self.offset);
1871 if (amt != data.len) return error.InputOutput;1871 if (amt != data.len) return error.InputOutput;
...@@ -1876,9 +1876,9 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {...@@ -1876,9 +1876,9 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
1876 if (!atom.isAlive()) continue;1876 if (!atom.isAlive()) continue;
1877 const sect = atom.getInputSection(macho_file);1877 const sect = atom.getInputSection(macho_file);
1878 if (sect.isZerofill()) continue;1878 if (sect.isZerofill()) continue;
1879 const value = math.cast(usize, atom.value) orelse return error.Overflow;1879 const value = try macho_file.cast(usize, atom.value);
1880 const off = math.cast(usize, atom.off) orelse return error.Overflow;1880 const off = try macho_file.cast(usize, atom.off);
1881 const size = math.cast(usize, atom.size) orelse return error.Overflow;1881 const size = try macho_file.cast(usize, atom.size);
1882 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;1882 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
1883 const data = sections_data[atom.n_sect];1883 const data = sections_data[atom.n_sect];
1884 @memcpy(buffer[value..][0..size], data[off..][0..size]);1884 @memcpy(buffer[value..][0..size], data[off..][0..size]);
...@@ -1909,29 +1909,27 @@ pub fn calcCompactUnwindSizeRelocatable(self: *Object, macho_file: *MachO) void...@@ -1909,29 +1909,27 @@ pub fn calcCompactUnwindSizeRelocatable(self: *Object, macho_file: *MachO) void
1909 }1909 }
1910}1910}
19111911
1912fn addReloc(offset: u32, arch: std.Target.Cpu.Arch) !macho.relocation_info {
1913 return .{
1914 .r_address = std.math.cast(i32, offset) orelse return error.Overflow,
1915 .r_symbolnum = 0,
1916 .r_pcrel = 0,
1917 .r_length = 3,
1918 .r_extern = 0,
1919 .r_type = switch (arch) {
1920 .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1921 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1922 else => unreachable,
1923 },
1924 };
1925}
1926
1912pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {1927pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
1913 const tracy = trace(@src());1928 const tracy = trace(@src());
1914 defer tracy.end();1929 defer tracy.end();
19151930
1916 const cpu_arch = macho_file.getTarget().cpu.arch;1931 const cpu_arch = macho_file.getTarget().cpu.arch;
19171932
1918 const addReloc = struct {
1919 fn addReloc(offset: u32, arch: std.Target.Cpu.Arch) !macho.relocation_info {
1920 return .{
1921 .r_address = math.cast(i32, offset) orelse return error.Overflow,
1922 .r_symbolnum = 0,
1923 .r_pcrel = 0,
1924 .r_length = 3,
1925 .r_extern = 0,
1926 .r_type = switch (arch) {
1927 .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1928 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1929 else => unreachable,
1930 },
1931 };
1932 }
1933 }.addReloc;
1934
1935 const nsect = macho_file.unwind_info_sect_index.?;1933 const nsect = macho_file.unwind_info_sect_index.?;
1936 const buffer = macho_file.sections.items(.out)[nsect].items;1934 const buffer = macho_file.sections.items(.out)[nsect].items;
1937 const relocs = macho_file.sections.items(.relocs)[nsect].items;1935 const relocs = macho_file.sections.items(.relocs)[nsect].items;
...@@ -1967,7 +1965,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {...@@ -1967,7 +1965,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
19671965
1968 // Personality function1966 // Personality function
1969 if (rec.getPersonality(macho_file)) |sym| {1967 if (rec.getPersonality(macho_file)) |sym| {
1970 const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;1968 const r_symbolnum = try macho_file.cast(u24, sym.getOutputSymtabIndex(macho_file).?);
1971 var reloc = try addReloc(offset + 16, cpu_arch);1969 var reloc = try addReloc(offset + 16, cpu_arch);
1972 reloc.r_symbolnum = r_symbolnum;1970 reloc.r_symbolnum = r_symbolnum;
1973 reloc.r_extern = 1;1971 reloc.r_extern = 1;
src/link/MachO/ZigObject.zig+81-72
...@@ -290,12 +290,15 @@ pub fn dedupLiterals(self: *ZigObject, lp: MachO.LiteralPool, macho_file: *MachO...@@ -290,12 +290,15 @@ pub fn dedupLiterals(self: *ZigObject, lp: MachO.LiteralPool, macho_file: *MachO
290/// We need this so that we can write to an archive.290/// We need this so that we can write to an archive.
291/// TODO implement writing ZigObject data directly to a buffer instead.291/// TODO implement writing ZigObject data directly to a buffer instead.
292pub fn readFileContents(self: *ZigObject, macho_file: *MachO) !void {292pub fn readFileContents(self: *ZigObject, macho_file: *MachO) !void {
293 const diags = &macho_file.base.comp.link_diags;
293 // Size of the output object file is always the offset + size of the strtab294 // Size of the output object file is always the offset + size of the strtab
294 const size = macho_file.symtab_cmd.stroff + macho_file.symtab_cmd.strsize;295 const size = macho_file.symtab_cmd.stroff + macho_file.symtab_cmd.strsize;
295 const gpa = macho_file.base.comp.gpa;296 const gpa = macho_file.base.comp.gpa;
296 try self.data.resize(gpa, size);297 try self.data.resize(gpa, size);
297 const amt = try macho_file.base.file.?.preadAll(self.data.items, 0);298 const amt = macho_file.base.file.?.preadAll(self.data.items, 0) catch |err|
298 if (amt != size) return error.InputOutput;299 return diags.fail("failed to read output file: {s}", .{@errorName(err)});
300 if (amt != size)
301 return diags.fail("unexpected EOF reading from output file", .{});
299}302}
300303
301pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {304pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {
...@@ -376,7 +379,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {...@@ -376,7 +379,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
376 if (atom.getRelocs(macho_file).len == 0) continue;379 if (atom.getRelocs(macho_file).len == 0) continue;
377 // TODO: we will resolve and write ZigObject's TLS data twice:380 // TODO: we will resolve and write ZigObject's TLS data twice:
378 // once here, and once in writeAtoms381 // once here, and once in writeAtoms
379 const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow;382 const atom_size = try macho_file.cast(usize, atom.size);
380 const code = try gpa.alloc(u8, atom_size);383 const code = try gpa.alloc(u8, atom_size);
381 defer gpa.free(code);384 defer gpa.free(code);
382 self.getAtomData(macho_file, atom.*, code) catch |err| {385 self.getAtomData(macho_file, atom.*, code) catch |err| {
...@@ -400,7 +403,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {...@@ -400,7 +403,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
400 has_error = true;403 has_error = true;
401 continue;404 continue;
402 };405 };
403 try macho_file.base.file.?.pwriteAll(code, file_offset);406 try macho_file.pwriteAll(code, file_offset);
404 }407 }
405408
406 if (has_error) return error.ResolveFailed;409 if (has_error) return error.ResolveFailed;
...@@ -419,7 +422,7 @@ pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {...@@ -419,7 +422,7 @@ pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {
419 }422 }
420}423}
421424
422pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {425pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) error{ LinkFailure, OutOfMemory }!void {
423 const gpa = macho_file.base.comp.gpa;426 const gpa = macho_file.base.comp.gpa;
424 const diags = &macho_file.base.comp.link_diags;427 const diags = &macho_file.base.comp.link_diags;
425428
...@@ -432,14 +435,14 @@ pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {...@@ -432,14 +435,14 @@ pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {
432 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;435 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
433 if (atom.getRelocs(macho_file).len == 0) continue;436 if (atom.getRelocs(macho_file).len == 0) continue;
434 const extra = atom.getExtra(macho_file);437 const extra = atom.getExtra(macho_file);
435 const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow;438 const atom_size = try macho_file.cast(usize, atom.size);
436 const code = try gpa.alloc(u8, atom_size);439 const code = try gpa.alloc(u8, atom_size);
437 defer gpa.free(code);440 defer gpa.free(code);
438 self.getAtomData(macho_file, atom.*, code) catch |err|441 self.getAtomData(macho_file, atom.*, code) catch |err|
439 return diags.fail("failed to fetch code for '{s}': {s}", .{ atom.getName(macho_file), @errorName(err) });442 return diags.fail("failed to fetch code for '{s}': {s}", .{ atom.getName(macho_file), @errorName(err) });
440 const file_offset = header.offset + atom.value;443 const file_offset = header.offset + atom.value;
441 try atom.writeRelocs(macho_file, code, relocs[extra.rel_out_index..][0..extra.rel_out_count]);444 try atom.writeRelocs(macho_file, code, relocs[extra.rel_out_index..][0..extra.rel_out_count]);
442 try macho_file.base.file.?.pwriteAll(code, file_offset);445 try macho_file.pwriteAll(code, file_offset);
443 }446 }
444}447}
445448
...@@ -457,8 +460,8 @@ pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void {...@@ -457,8 +460,8 @@ pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void {
457 if (sect.isZerofill()) continue;460 if (sect.isZerofill()) continue;
458 if (macho_file.isZigSection(atom.out_n_sect)) continue;461 if (macho_file.isZigSection(atom.out_n_sect)) continue;
459 if (atom.getRelocs(macho_file).len == 0) continue;462 if (atom.getRelocs(macho_file).len == 0) continue;
460 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;463 const off = try macho_file.cast(usize, atom.value);
461 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;464 const size = try macho_file.cast(usize, atom.size);
462 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;465 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
463 try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);466 try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
464 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;467 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
...@@ -480,8 +483,8 @@ pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void {...@@ -480,8 +483,8 @@ pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void {
480 const sect = atom.getInputSection(macho_file);483 const sect = atom.getInputSection(macho_file);
481 if (sect.isZerofill()) continue;484 if (sect.isZerofill()) continue;
482 if (macho_file.isZigSection(atom.out_n_sect)) continue;485 if (macho_file.isZigSection(atom.out_n_sect)) continue;
483 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;486 const off = try macho_file.cast(usize, atom.value);
484 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;487 const size = try macho_file.cast(usize, atom.size);
485 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;488 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
486 try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);489 try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
487 try atom.resolveRelocs(macho_file, buffer[off..][0..size]);490 try atom.resolveRelocs(macho_file, buffer[off..][0..size]);
...@@ -546,7 +549,9 @@ pub fn getInputSection(self: ZigObject, atom: Atom, macho_file: *MachO) macho.se...@@ -546,7 +549,9 @@ pub fn getInputSection(self: ZigObject, atom: Atom, macho_file: *MachO) macho.se
546 return sect;549 return sect;
547}550}
548551
549pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) !void {552pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.File.FlushError!void {
553 const diags = &macho_file.base.comp.link_diags;
554
550 // Handle any lazy symbols that were emitted by incremental compilation.555 // Handle any lazy symbols that were emitted by incremental compilation.
551 if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| {556 if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| {
552 const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid);557 const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid);
...@@ -559,18 +564,20 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id)...@@ -559,18 +564,20 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id)
559 pt,564 pt,
560 .{ .kind = .code, .ty = .anyerror_type },565 .{ .kind = .code, .ty = .anyerror_type },
561 metadata.text_symbol_index,566 metadata.text_symbol_index,
562 ) catch |err| return switch (err) {567 ) catch |err| switch (err) {
563 error.CodegenFail => error.FlushFailure,568 error.OutOfMemory => return error.OutOfMemory,
564 else => |e| e,569 error.LinkFailure => return error.LinkFailure,
570 else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}),
565 };571 };
566 if (metadata.const_state != .unused) self.updateLazySymbol(572 if (metadata.const_state != .unused) self.updateLazySymbol(
567 macho_file,573 macho_file,
568 pt,574 pt,
569 .{ .kind = .const_data, .ty = .anyerror_type },575 .{ .kind = .const_data, .ty = .anyerror_type },
570 metadata.const_symbol_index,576 metadata.const_symbol_index,
571 ) catch |err| return switch (err) {577 ) catch |err| switch (err) {
572 error.CodegenFail => error.FlushFailure,578 error.OutOfMemory => return error.OutOfMemory,
573 else => |e| e,579 error.LinkFailure => return error.LinkFailure,
580 else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}),
574 };581 };
575 }582 }
576 for (self.lazy_syms.values()) |*metadata| {583 for (self.lazy_syms.values()) |*metadata| {
...@@ -581,7 +588,10 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id)...@@ -581,7 +588,10 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id)
581 if (self.dwarf) |*dwarf| {588 if (self.dwarf) |*dwarf| {
582 const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid);589 const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid);
583 defer pt.deactivate();590 defer pt.deactivate();
584 try dwarf.flushModule(pt);591 dwarf.flushModule(pt) catch |err| switch (err) {
592 error.OutOfMemory => return error.OutOfMemory,
593 else => |e| return diags.fail("failed to flush dwarf module: {s}", .{@errorName(e)}),
594 };
585595
586 self.debug_abbrev_dirty = false;596 self.debug_abbrev_dirty = false;
587 self.debug_aranges_dirty = false;597 self.debug_aranges_dirty = false;
...@@ -616,6 +626,7 @@ pub fn getNavVAddr(...@@ -616,6 +626,7 @@ pub fn getNavVAddr(
616 const sym = self.symbols.items[sym_index];626 const sym = self.symbols.items[sym_index];
617 const vaddr = sym.getAddress(.{}, macho_file);627 const vaddr = sym.getAddress(.{}, macho_file);
618 switch (reloc_info.parent) {628 switch (reloc_info.parent) {
629 .none => unreachable,
619 .atom_index => |atom_index| {630 .atom_index => |atom_index| {
620 const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?;631 const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?;
621 try parent_atom.addReloc(macho_file, .{632 try parent_atom.addReloc(macho_file, .{
...@@ -655,6 +666,7 @@ pub fn getUavVAddr(...@@ -655,6 +666,7 @@ pub fn getUavVAddr(
655 const sym = self.symbols.items[sym_index];666 const sym = self.symbols.items[sym_index];
656 const vaddr = sym.getAddress(.{}, macho_file);667 const vaddr = sym.getAddress(.{}, macho_file);
657 switch (reloc_info.parent) {668 switch (reloc_info.parent) {
669 .none => unreachable,
658 .atom_index => |atom_index| {670 .atom_index => |atom_index| {
659 const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?;671 const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?;
660 try parent_atom.addReloc(macho_file, .{672 try parent_atom.addReloc(macho_file, .{
...@@ -766,7 +778,7 @@ pub fn updateFunc(...@@ -766,7 +778,7 @@ pub fn updateFunc(
766 func_index: InternPool.Index,778 func_index: InternPool.Index,
767 air: Air,779 air: Air,
768 liveness: Liveness,780 liveness: Liveness,
769) !void {781) link.File.UpdateNavError!void {
770 const tracy = trace(@src());782 const tracy = trace(@src());
771 defer tracy.end();783 defer tracy.end();
772784
...@@ -777,13 +789,13 @@ pub fn updateFunc(...@@ -777,13 +789,13 @@ pub fn updateFunc(
777 const sym_index = try self.getOrCreateMetadataForNav(macho_file, func.owner_nav);789 const sym_index = try self.getOrCreateMetadataForNav(macho_file, func.owner_nav);
778 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);790 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
779791
780 var code_buffer = std.ArrayList(u8).init(gpa);792 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
781 defer code_buffer.deinit();793 defer code_buffer.deinit(gpa);
782794
783 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;795 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;
784 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();796 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
785797
786 const res = try codegen.generateFunction(798 try codegen.generateFunction(
787 &macho_file.base,799 &macho_file.base,
788 pt,800 pt,
789 zcu.navSrcLoc(func.owner_nav),801 zcu.navSrcLoc(func.owner_nav),
...@@ -793,14 +805,7 @@ pub fn updateFunc(...@@ -793,14 +805,7 @@ pub fn updateFunc(
793 &code_buffer,805 &code_buffer,
794 if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none,806 if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none,
795 );807 );
796808 const code = code_buffer.items;
797 const code = switch (res) {
798 .ok => code_buffer.items,
799 .fail => |em| {
800 try zcu.failed_codegen.put(gpa, func.owner_nav, em);
801 return;
802 },
803 };
804809
805 const sect_index = try self.getNavOutputSection(macho_file, zcu, func.owner_nav, code);810 const sect_index = try self.getNavOutputSection(macho_file, zcu, func.owner_nav, code);
806 const old_rva, const old_alignment = blk: {811 const old_rva, const old_alignment = blk: {
...@@ -813,7 +818,8 @@ pub fn updateFunc(...@@ -813,7 +818,8 @@ pub fn updateFunc(
813 break :blk .{ atom.value, atom.alignment };818 break :blk .{ atom.value, atom.alignment };
814 };819 };
815820
816 if (debug_wip_nav) |*wip_nav| try self.dwarf.?.finishWipNavFunc(pt, func.owner_nav, code.len, wip_nav);821 if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNavFunc(pt, func.owner_nav, code.len, wip_nav) catch |err|
822 return macho_file.base.cgFail(func.owner_nav, "falied to finish dwarf function: {s}", .{@errorName(err)});
817823
818 // Exports will be updated by `Zcu.processExports` after the update.824 // Exports will be updated by `Zcu.processExports` after the update.
819 if (old_rva != new_rva and old_rva > 0) {825 if (old_rva != new_rva and old_rva > 0) {
...@@ -850,7 +856,8 @@ pub fn updateFunc(...@@ -850,7 +856,8 @@ pub fn updateFunc(
850 }856 }
851 const target_sym = self.symbols.items[sym_index];857 const target_sym = self.symbols.items[sym_index];
852 const source_sym = self.symbols.items[target_sym.getExtra(macho_file).trampoline];858 const source_sym = self.symbols.items[target_sym.getExtra(macho_file).trampoline];
853 try writeTrampoline(source_sym, target_sym, macho_file);859 writeTrampoline(source_sym, target_sym, macho_file) catch |err|
860 return macho_file.base.cgFail(func.owner_nav, "failed to write trampoline: {s}", .{@errorName(err)});
854 }861 }
855}862}
856863
...@@ -883,7 +890,11 @@ pub fn updateNav(...@@ -883,7 +890,11 @@ pub fn updateNav(
883 if (self.dwarf) |*dwarf| dwarf: {890 if (self.dwarf) |*dwarf| dwarf: {
884 var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf;891 var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf;
885 defer debug_wip_nav.deinit();892 defer debug_wip_nav.deinit();
886 try dwarf.finishWipNav(pt, nav_index, &debug_wip_nav);893 dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) {
894 error.OutOfMemory => return error.OutOfMemory,
895 error.Overflow => return error.Overflow,
896 else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
897 };
887 }898 }
888 return;899 return;
889 },900 },
...@@ -894,13 +905,13 @@ pub fn updateNav(...@@ -894,13 +905,13 @@ pub fn updateNav(
894 const sym_index = try self.getOrCreateMetadataForNav(macho_file, nav_index);905 const sym_index = try self.getOrCreateMetadataForNav(macho_file, nav_index);
895 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);906 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
896907
897 var code_buffer = std.ArrayList(u8).init(zcu.gpa);908 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
898 defer code_buffer.deinit();909 defer code_buffer.deinit(zcu.gpa);
899910
900 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;911 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;
901 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();912 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
902913
903 const res = try codegen.generateSymbol(914 try codegen.generateSymbol(
904 &macho_file.base,915 &macho_file.base,
905 pt,916 pt,
906 zcu.navSrcLoc(nav_index),917 zcu.navSrcLoc(nav_index),
...@@ -908,21 +919,19 @@ pub fn updateNav(...@@ -908,21 +919,19 @@ pub fn updateNav(
908 &code_buffer,919 &code_buffer,
909 .{ .atom_index = sym_index },920 .{ .atom_index = sym_index },
910 );921 );
922 const code = code_buffer.items;
911923
912 const code = switch (res) {
913 .ok => code_buffer.items,
914 .fail => |em| {
915 try zcu.failed_codegen.put(zcu.gpa, nav_index, em);
916 return;
917 },
918 };
919 const sect_index = try self.getNavOutputSection(macho_file, zcu, nav_index, code);924 const sect_index = try self.getNavOutputSection(macho_file, zcu, nav_index, code);
920 if (isThreadlocal(macho_file, nav_index))925 if (isThreadlocal(macho_file, nav_index))
921 try self.updateTlv(macho_file, pt, nav_index, sym_index, sect_index, code)926 try self.updateTlv(macho_file, pt, nav_index, sym_index, sect_index, code)
922 else927 else
923 try self.updateNavCode(macho_file, pt, nav_index, sym_index, sect_index, code);928 try self.updateNavCode(macho_file, pt, nav_index, sym_index, sect_index, code);
924929
925 if (debug_wip_nav) |*wip_nav| try self.dwarf.?.finishWipNav(pt, nav_index, wip_nav);930 if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNav(pt, nav_index, wip_nav) catch |err| switch (err) {
931 error.OutOfMemory => return error.OutOfMemory,
932 error.Overflow => return error.Overflow,
933 else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
934 };
926 } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index);935 } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index);
927936
928 // Exports will be updated by `Zcu.processExports` after the update.937 // Exports will be updated by `Zcu.processExports` after the update.
...@@ -936,7 +945,7 @@ fn updateNavCode(...@@ -936,7 +945,7 @@ fn updateNavCode(
936 sym_index: Symbol.Index,945 sym_index: Symbol.Index,
937 sect_index: u8,946 sect_index: u8,
938 code: []const u8,947 code: []const u8,
939) !void {948) link.File.UpdateNavError!void {
940 const zcu = pt.zcu;949 const zcu = pt.zcu;
941 const gpa = zcu.gpa;950 const gpa = zcu.gpa;
942 const ip = &zcu.intern_pool;951 const ip = &zcu.intern_pool;
...@@ -978,7 +987,8 @@ fn updateNavCode(...@@ -978,7 +987,8 @@ fn updateNavCode(
978 const need_realloc = code.len > capacity or !required_alignment.check(atom.value);987 const need_realloc = code.len > capacity or !required_alignment.check(atom.value);
979988
980 if (need_realloc) {989 if (need_realloc) {
981 try atom.grow(macho_file);990 atom.grow(macho_file) catch |err|
991 return macho_file.base.cgFail(nav_index, "failed to grow atom: {s}", .{@errorName(err)});
982 log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom.value });992 log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom.value });
983 if (old_vaddr != atom.value) {993 if (old_vaddr != atom.value) {
984 sym.value = 0;994 sym.value = 0;
...@@ -991,7 +1001,8 @@ fn updateNavCode(...@@ -991,7 +1001,8 @@ fn updateNavCode(
991 sect.size = needed_size;1001 sect.size = needed_size;
992 }1002 }
993 } else {1003 } else {
994 try atom.allocate(macho_file);1004 atom.allocate(macho_file) catch |err|
1005 return macho_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)});
995 errdefer self.freeNavMetadata(macho_file, sym_index);1006 errdefer self.freeNavMetadata(macho_file, sym_index);
9961007
997 sym.value = 0;1008 sym.value = 0;
...@@ -1000,7 +1011,8 @@ fn updateNavCode(...@@ -1000,7 +1011,8 @@ fn updateNavCode(
10001011
1001 if (!sect.isZerofill()) {1012 if (!sect.isZerofill()) {
1002 const file_offset = sect.offset + atom.value;1013 const file_offset = sect.offset + atom.value;
1003 try macho_file.base.file.?.pwriteAll(code, file_offset);1014 macho_file.base.file.?.pwriteAll(code, file_offset) catch |err|
1015 return macho_file.base.cgFail(nav_index, "failed to write output file: {s}", .{@errorName(err)});
1004 }1016 }
1005}1017}
10061018
...@@ -1198,13 +1210,13 @@ fn lowerConst(...@@ -1198,13 +1210,13 @@ fn lowerConst(
1198) !LowerConstResult {1210) !LowerConstResult {
1199 const gpa = macho_file.base.comp.gpa;1211 const gpa = macho_file.base.comp.gpa;
12001212
1201 var code_buffer = std.ArrayList(u8).init(gpa);1213 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1202 defer code_buffer.deinit();1214 defer code_buffer.deinit(gpa);
12031215
1204 const name_str = try self.addString(gpa, name);1216 const name_str = try self.addString(gpa, name);
1205 const sym_index = try self.newSymbolWithAtom(gpa, name_str, macho_file);1217 const sym_index = try self.newSymbolWithAtom(gpa, name_str, macho_file);
12061218
1207 const res = try codegen.generateSymbol(1219 try codegen.generateSymbol(
1208 &macho_file.base,1220 &macho_file.base,
1209 pt,1221 pt,
1210 src_loc,1222 src_loc,
...@@ -1212,10 +1224,7 @@ fn lowerConst(...@@ -1212,10 +1224,7 @@ fn lowerConst(
1212 &code_buffer,1224 &code_buffer,
1213 .{ .atom_index = sym_index },1225 .{ .atom_index = sym_index },
1214 );1226 );
1215 const code = switch (res) {1227 const code = code_buffer.items;
1216 .ok => code_buffer.items,
1217 .fail => |em| return .{ .fail = em },
1218 };
12191228
1220 const sym = &self.symbols.items[sym_index];1229 const sym = &self.symbols.items[sym_index];
1221 sym.out_n_sect = output_section_index;1230 sym.out_n_sect = output_section_index;
...@@ -1236,7 +1245,7 @@ fn lowerConst(...@@ -1236,7 +1245,7 @@ fn lowerConst(
12361245
1237 const sect = macho_file.sections.items(.header)[output_section_index];1246 const sect = macho_file.sections.items(.header)[output_section_index];
1238 const file_offset = sect.offset + atom.value;1247 const file_offset = sect.offset + atom.value;
1239 try macho_file.base.file.?.pwriteAll(code, file_offset);1248 try macho_file.pwriteAll(code, file_offset);
12401249
1241 return .{ .ok = sym_index };1250 return .{ .ok = sym_index };
1242}1251}
...@@ -1246,7 +1255,7 @@ pub fn updateExports(...@@ -1246,7 +1255,7 @@ pub fn updateExports(
1246 macho_file: *MachO,1255 macho_file: *MachO,
1247 pt: Zcu.PerThread,1256 pt: Zcu.PerThread,
1248 exported: Zcu.Exported,1257 exported: Zcu.Exported,
1249 export_indices: []const u32,1258 export_indices: []const Zcu.Export.Index,
1250) link.File.UpdateExportsError!void {1259) link.File.UpdateExportsError!void {
1251 const tracy = trace(@src());1260 const tracy = trace(@src());
1252 defer tracy.end();1261 defer tracy.end();
...@@ -1259,7 +1268,7 @@ pub fn updateExports(...@@ -1259,7 +1268,7 @@ pub fn updateExports(
1259 break :blk self.navs.getPtr(nav).?;1268 break :blk self.navs.getPtr(nav).?;
1260 },1269 },
1261 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {1270 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1262 const first_exp = zcu.all_exports.items[export_indices[0]];1271 const first_exp = export_indices[0].ptr(zcu);
1263 const res = try self.lowerUav(macho_file, pt, uav, .none, first_exp.src);1272 const res = try self.lowerUav(macho_file, pt, uav, .none, first_exp.src);
1264 switch (res) {1273 switch (res) {
1265 .mcv => {},1274 .mcv => {},
...@@ -1279,7 +1288,7 @@ pub fn updateExports(...@@ -1279,7 +1288,7 @@ pub fn updateExports(
1279 const nlist = self.symtab.items(.nlist)[nlist_idx];1288 const nlist = self.symtab.items(.nlist)[nlist_idx];
12801289
1281 for (export_indices) |export_idx| {1290 for (export_indices) |export_idx| {
1282 const exp = zcu.all_exports.items[export_idx];1291 const exp = export_idx.ptr(zcu);
1283 if (exp.opts.section.unwrap()) |section_name| {1292 if (exp.opts.section.unwrap()) |section_name| {
1284 if (!section_name.eqlSlice("__text", &zcu.intern_pool)) {1293 if (!section_name.eqlSlice("__text", &zcu.intern_pool)) {
1285 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);1294 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
...@@ -1352,8 +1361,8 @@ fn updateLazySymbol(...@@ -1352,8 +1361,8 @@ fn updateLazySymbol(
1352 const gpa = zcu.gpa;1361 const gpa = zcu.gpa;
13531362
1354 var required_alignment: Atom.Alignment = .none;1363 var required_alignment: Atom.Alignment = .none;
1355 var code_buffer = std.ArrayList(u8).init(gpa);1364 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1356 defer code_buffer.deinit();1365 defer code_buffer.deinit(gpa);
13571366
1358 const name_str = blk: {1367 const name_str = blk: {
1359 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{1368 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{
...@@ -1365,7 +1374,7 @@ fn updateLazySymbol(...@@ -1365,7 +1374,7 @@ fn updateLazySymbol(
1365 };1374 };
13661375
1367 const src = Type.fromInterned(lazy_sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;1376 const src = Type.fromInterned(lazy_sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;
1368 const res = try codegen.generateLazySymbol(1377 try codegen.generateLazySymbol(
1369 &macho_file.base,1378 &macho_file.base,
1370 pt,1379 pt,
1371 src,1380 src,
...@@ -1375,13 +1384,7 @@ fn updateLazySymbol(...@@ -1375,13 +1384,7 @@ fn updateLazySymbol(
1375 .none,1384 .none,
1376 .{ .atom_index = symbol_index },1385 .{ .atom_index = symbol_index },
1377 );1386 );
1378 const code = switch (res) {1387 const code = code_buffer.items;
1379 .ok => code_buffer.items,
1380 .fail => |em| {
1381 log.err("{s}", .{em.msg});
1382 return error.CodegenFail;
1383 },
1384 };
13851388
1386 const output_section_index = switch (lazy_sym.kind) {1389 const output_section_index = switch (lazy_sym.kind) {
1387 .code => macho_file.zig_text_sect_index.?,1390 .code => macho_file.zig_text_sect_index.?,
...@@ -1412,12 +1415,18 @@ fn updateLazySymbol(...@@ -1412,12 +1415,18 @@ fn updateLazySymbol(
14121415
1413 const sect = macho_file.sections.items(.header)[output_section_index];1416 const sect = macho_file.sections.items(.header)[output_section_index];
1414 const file_offset = sect.offset + atom.value;1417 const file_offset = sect.offset + atom.value;
1415 try macho_file.base.file.?.pwriteAll(code, file_offset);1418 try macho_file.pwriteAll(code, file_offset);
1416}1419}
14171420
1418pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {1421pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
1419 if (self.dwarf) |*dwarf| {1422 if (self.dwarf) |*dwarf| {
1420 try dwarf.updateLineNumber(pt.zcu, ti_id);1423 const comp = dwarf.bin_file.comp;
1424 const diags = &comp.link_diags;
1425 dwarf.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) {
1426 error.Overflow => return error.Overflow,
1427 error.OutOfMemory => return error.OutOfMemory,
1428 else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}),
1429 };
1421 }1430 }
1422}1431}
14231432
src/link/MachO/relocatable.zig+68-42
...@@ -18,13 +18,15 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -18,13 +18,15 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
18 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all18 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all
19 // debug info segments/sections (this is apparently by design by Apple), we copy19 // debug info segments/sections (this is apparently by design by Apple), we copy
20 // the *only* input file over.20 // the *only* input file over.
21 // TODO: in the future, when we implement `dsymutil` alternative directly in the Zig
22 // compiler, investigate if we can get rid of this `if` prong here.
23 const path = positionals.items[0].path().?;21 const path = positionals.items[0].path().?;
24 const in_file = try path.root_dir.handle.openFile(path.sub_path, .{});22 const in_file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err|
25 const stat = try in_file.stat();23 return diags.fail("failed to open {}: {s}", .{ path, @errorName(err) });
26 const amt = try in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size);24 const stat = in_file.stat() catch |err|
27 if (amt != stat.size) return error.InputOutput; // TODO: report an actual user error25 return diags.fail("failed to stat {}: {s}", .{ path, @errorName(err) });
26 const amt = in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size) catch |err|
27 return diags.fail("failed to copy range of file {}: {s}", .{ path, @errorName(err) });
28 if (amt != stat.size)
29 return diags.fail("unexpected short write in copy range of file {}", .{path});
28 return;30 return;
29 }31 }
3032
...@@ -33,14 +35,18 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -33,14 +35,18 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
33 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});35 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
34 }36 }
3537
36 if (diags.hasErrors()) return error.FlushFailure;38 if (diags.hasErrors()) return error.LinkFailure;
3739
38 try macho_file.parseInputFiles();40 try macho_file.parseInputFiles();
3941
40 if (diags.hasErrors()) return error.FlushFailure;42 if (diags.hasErrors()) return error.LinkFailure;
4143
42 try macho_file.resolveSymbols();44 try macho_file.resolveSymbols();
43 try macho_file.dedupLiterals();45 macho_file.dedupLiterals() catch |err| switch (err) {
46 error.OutOfMemory => return error.OutOfMemory,
47 error.LinkFailure => return error.LinkFailure,
48 else => |e| return diags.fail("failed to update ar size: {s}", .{@errorName(e)}),
49 };
44 markExports(macho_file);50 markExports(macho_file);
45 claimUnresolved(macho_file);51 claimUnresolved(macho_file);
46 try initOutputSections(macho_file);52 try initOutputSections(macho_file);
...@@ -49,7 +55,10 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -49,7 +55,10 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
49 try calcSectionSizes(macho_file);55 try calcSectionSizes(macho_file);
5056
51 try createSegment(macho_file);57 try createSegment(macho_file);
52 try allocateSections(macho_file);58 allocateSections(macho_file) catch |err| switch (err) {
59 error.LinkFailure => return error.LinkFailure,
60 else => |e| return diags.fail("failed to allocate sections: {s}", .{@errorName(e)}),
61 };
53 allocateSegment(macho_file);62 allocateSegment(macho_file);
5463
55 if (build_options.enable_logging) {64 if (build_options.enable_logging) {
...@@ -93,11 +102,11 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -93,11 +102,11 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
93 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});102 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
94 }103 }
95104
96 if (diags.hasErrors()) return error.FlushFailure;105 if (diags.hasErrors()) return error.LinkFailure;
97106
98 try parseInputFilesAr(macho_file);107 try parseInputFilesAr(macho_file);
99108
100 if (diags.hasErrors()) return error.FlushFailure;109 if (diags.hasErrors()) return error.LinkFailure;
101110
102 // First, we flush relocatable object file generated with our backends.111 // First, we flush relocatable object file generated with our backends.
103 if (macho_file.getZigObject()) |zo| {112 if (macho_file.getZigObject()) |zo| {
...@@ -108,7 +117,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -108,7 +117,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
108 try macho_file.addAtomsToSections();117 try macho_file.addAtomsToSections();
109 try calcSectionSizes(macho_file);118 try calcSectionSizes(macho_file);
110 try createSegment(macho_file);119 try createSegment(macho_file);
111 try allocateSections(macho_file);120 allocateSections(macho_file) catch |err|
121 return diags.fail("failed to allocate sections: {s}", .{@errorName(err)});
112 allocateSegment(macho_file);122 allocateSegment(macho_file);
113123
114 if (build_options.enable_logging) {124 if (build_options.enable_logging) {
...@@ -126,8 +136,6 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -126,8 +136,6 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
126 const ncmds, const sizeofcmds = try writeLoadCommands(macho_file);136 const ncmds, const sizeofcmds = try writeLoadCommands(macho_file);
127 try writeHeader(macho_file, ncmds, sizeofcmds);137 try writeHeader(macho_file, ncmds, sizeofcmds);
128138
129 // TODO we can avoid reading in the file contents we just wrote if we give the linker
130 // ability to write directly to a buffer.
131 try zo.readFileContents(macho_file);139 try zo.readFileContents(macho_file);
132 }140 }
133141
...@@ -152,7 +160,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -152,7 +160,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
152160
153 // Update sizes of contributing objects161 // Update sizes of contributing objects
154 for (files.items) |index| {162 for (files.items) |index| {
155 try macho_file.getFile(index).?.updateArSize(macho_file);163 macho_file.getFile(index).?.updateArSize(macho_file) catch |err|
164 return diags.fail("failed to update ar size: {s}", .{@errorName(err)});
156 }165 }
157166
158 // Update file offsets of contributing objects167 // Update file offsets of contributing objects
...@@ -171,7 +180,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -171,7 +180,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
171 state.file_off = pos;180 state.file_off = pos;
172 pos += @sizeOf(Archive.ar_hdr);181 pos += @sizeOf(Archive.ar_hdr);
173 pos += mem.alignForward(usize, zo.basename.len + 1, ptr_width);182 pos += mem.alignForward(usize, zo.basename.len + 1, ptr_width);
174 pos += math.cast(usize, state.size) orelse return error.Overflow;183 pos += try macho_file.cast(usize, state.size);
175 },184 },
176 .object => |o| {185 .object => |o| {
177 const state = &o.output_ar_state;186 const state = &o.output_ar_state;
...@@ -179,7 +188,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -179,7 +188,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
179 state.file_off = pos;188 state.file_off = pos;
180 pos += @sizeOf(Archive.ar_hdr);189 pos += @sizeOf(Archive.ar_hdr);
181 pos += mem.alignForward(usize, o.path.basename().len + 1, ptr_width);190 pos += mem.alignForward(usize, o.path.basename().len + 1, ptr_width);
182 pos += math.cast(usize, state.size) orelse return error.Overflow;191 pos += try macho_file.cast(usize, state.size);
183 },192 },
184 else => unreachable,193 else => unreachable,
185 }194 }
...@@ -201,7 +210,10 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -201,7 +210,10 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
201 try writer.writeAll(Archive.ARMAG);210 try writer.writeAll(Archive.ARMAG);
202211
203 // Write symtab212 // Write symtab
204 try ar_symtab.write(format, macho_file, writer);213 ar_symtab.write(format, macho_file, writer) catch |err| switch (err) {
214 error.OutOfMemory => return error.OutOfMemory,
215 else => |e| return diags.fail("failed to write archive symbol table: {s}", .{@errorName(e)}),
216 };
205217
206 // Write object files218 // Write object files
207 for (files.items) |index| {219 for (files.items) |index| {
...@@ -210,15 +222,16 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -210,15 +222,16 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
210 if (padding > 0) {222 if (padding > 0) {
211 try writer.writeByteNTimes(0, padding);223 try writer.writeByteNTimes(0, padding);
212 }224 }
213 try macho_file.getFile(index).?.writeAr(format, macho_file, writer);225 macho_file.getFile(index).?.writeAr(format, macho_file, writer) catch |err|
226 return diags.fail("failed to write archive: {s}", .{@errorName(err)});
214 }227 }
215228
216 assert(buffer.items.len == total_size);229 assert(buffer.items.len == total_size);
217230
218 try macho_file.base.file.?.setEndPos(total_size);231 try macho_file.setEndPos(total_size);
219 try macho_file.base.file.?.pwriteAll(buffer.items, 0);232 try macho_file.pwriteAll(buffer.items, 0);
220233
221 if (diags.hasErrors()) return error.FlushFailure;234 if (diags.hasErrors()) return error.LinkFailure;
222}235}
223236
224fn parseInputFilesAr(macho_file: *MachO) !void {237fn parseInputFilesAr(macho_file: *MachO) !void {
...@@ -452,11 +465,10 @@ fn allocateSections(macho_file: *MachO) !void {...@@ -452,11 +465,10 @@ fn allocateSections(macho_file: *MachO) !void {
452 for (slice.items(.header)) |*header| {465 for (slice.items(.header)) |*header| {
453 const needed_size = header.size;466 const needed_size = header.size;
454 header.size = 0;467 header.size = 0;
455 const alignment = try math.powi(u32, 2, header.@"align");468 const alignment = try macho_file.alignPow(header.@"align");
456 if (!header.isZerofill()) {469 if (!header.isZerofill()) {
457 if (needed_size > macho_file.allocatedSize(header.offset)) {470 if (needed_size > macho_file.allocatedSize(header.offset)) {
458 header.offset = math.cast(u32, try macho_file.findFreeSpace(needed_size, alignment)) orelse471 header.offset = try macho_file.cast(u32, try macho_file.findFreeSpace(needed_size, alignment));
459 return error.Overflow;
460 }472 }
461 }473 }
462 if (needed_size > macho_file.allocatedSizeVirtual(header.addr)) {474 if (needed_size > macho_file.allocatedSizeVirtual(header.addr)) {
...@@ -572,7 +584,7 @@ fn sortRelocs(macho_file: *MachO) void {...@@ -572,7 +584,7 @@ fn sortRelocs(macho_file: *MachO) void {
572 }584 }
573}585}
574586
575fn writeSections(macho_file: *MachO) !void {587fn writeSections(macho_file: *MachO) link.File.FlushError!void {
576 const tracy = trace(@src());588 const tracy = trace(@src());
577 defer tracy.end();589 defer tracy.end();
578590
...@@ -583,7 +595,7 @@ fn writeSections(macho_file: *MachO) !void {...@@ -583,7 +595,7 @@ fn writeSections(macho_file: *MachO) !void {
583 for (slice.items(.header), slice.items(.out), slice.items(.relocs), 0..) |header, *out, *relocs, n_sect| {595 for (slice.items(.header), slice.items(.out), slice.items(.relocs), 0..) |header, *out, *relocs, n_sect| {
584 if (header.isZerofill()) continue;596 if (header.isZerofill()) continue;
585 if (!macho_file.isZigSection(@intCast(n_sect))) { // TODO this is wrong; what about debug sections?597 if (!macho_file.isZigSection(@intCast(n_sect))) { // TODO this is wrong; what about debug sections?
586 const size = math.cast(usize, header.size) orelse return error.Overflow;598 const size = try macho_file.cast(usize, header.size);
587 try out.resize(gpa, size);599 try out.resize(gpa, size);
588 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;600 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
589 @memset(out.items, padding_byte);601 @memset(out.items, padding_byte);
...@@ -662,16 +674,16 @@ fn writeSectionsToFile(macho_file: *MachO) !void {...@@ -662,16 +674,16 @@ fn writeSectionsToFile(macho_file: *MachO) !void {
662674
663 const slice = macho_file.sections.slice();675 const slice = macho_file.sections.slice();
664 for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, out, relocs| {676 for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, out, relocs| {
665 try macho_file.base.file.?.pwriteAll(out.items, header.offset);677 try macho_file.pwriteAll(out.items, header.offset);
666 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);678 try macho_file.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
667 }679 }
668680
669 try macho_file.writeDataInCode();681 try macho_file.writeDataInCode();
670 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff);682 try macho_file.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff);
671 try macho_file.base.file.?.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff);683 try macho_file.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff);
672}684}
673685
674fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {686fn writeLoadCommands(macho_file: *MachO) error{ LinkFailure, OutOfMemory }!struct { usize, usize } {
675 const gpa = macho_file.base.comp.gpa;687 const gpa = macho_file.base.comp.gpa;
676 const needed_size = load_commands.calcLoadCommandsSizeObject(macho_file);688 const needed_size = load_commands.calcLoadCommandsSizeObject(macho_file);
677 const buffer = try gpa.alloc(u8, needed_size);689 const buffer = try gpa.alloc(u8, needed_size);
...@@ -686,31 +698,45 @@ fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {...@@ -686,31 +698,45 @@ fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {
686 {698 {
687 assert(macho_file.segments.items.len == 1);699 assert(macho_file.segments.items.len == 1);
688 const seg = macho_file.segments.items[0];700 const seg = macho_file.segments.items[0];
689 try writer.writeStruct(seg);701 writer.writeStruct(seg) catch |err| switch (err) {
702 error.NoSpaceLeft => unreachable,
703 };
690 for (macho_file.sections.items(.header)) |header| {704 for (macho_file.sections.items(.header)) |header| {
691 try writer.writeStruct(header);705 writer.writeStruct(header) catch |err| switch (err) {
706 error.NoSpaceLeft => unreachable,
707 };
692 }708 }
693 ncmds += 1;709 ncmds += 1;
694 }710 }
695711
696 try writer.writeStruct(macho_file.data_in_code_cmd);712 writer.writeStruct(macho_file.data_in_code_cmd) catch |err| switch (err) {
713 error.NoSpaceLeft => unreachable,
714 };
697 ncmds += 1;715 ncmds += 1;
698 try writer.writeStruct(macho_file.symtab_cmd);716 writer.writeStruct(macho_file.symtab_cmd) catch |err| switch (err) {
717 error.NoSpaceLeft => unreachable,
718 };
699 ncmds += 1;719 ncmds += 1;
700 try writer.writeStruct(macho_file.dysymtab_cmd);720 writer.writeStruct(macho_file.dysymtab_cmd) catch |err| switch (err) {
721 error.NoSpaceLeft => unreachable,
722 };
701 ncmds += 1;723 ncmds += 1;
702724
703 if (macho_file.platform.isBuildVersionCompatible()) {725 if (macho_file.platform.isBuildVersionCompatible()) {
704 try load_commands.writeBuildVersionLC(macho_file.platform, macho_file.sdk_version, writer);726 load_commands.writeBuildVersionLC(macho_file.platform, macho_file.sdk_version, writer) catch |err| switch (err) {
727 error.NoSpaceLeft => unreachable,
728 };
705 ncmds += 1;729 ncmds += 1;
706 } else {730 } else {
707 try load_commands.writeVersionMinLC(macho_file.platform, macho_file.sdk_version, writer);731 load_commands.writeVersionMinLC(macho_file.platform, macho_file.sdk_version, writer) catch |err| switch (err) {
732 error.NoSpaceLeft => unreachable,
733 };
708 ncmds += 1;734 ncmds += 1;
709 }735 }
710736
711 assert(stream.pos == needed_size);737 assert(stream.pos == needed_size);
712738
713 try macho_file.base.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64));739 try macho_file.pwriteAll(buffer, @sizeOf(macho.mach_header_64));
714740
715 return .{ ncmds, buffer.len };741 return .{ ncmds, buffer.len };
716}742}
...@@ -742,7 +768,7 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {...@@ -742,7 +768,7 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {
742 header.ncmds = @intCast(ncmds);768 header.ncmds = @intCast(ncmds);
743 header.sizeofcmds = @intCast(sizeofcmds);769 header.sizeofcmds = @intCast(sizeofcmds);
744770
745 try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0);771 try macho_file.pwriteAll(mem.asBytes(&header), 0);
746}772}
747773
748const std = @import("std");774const std = @import("std");
src/link/NvPtx.zig+9-7
...@@ -82,11 +82,17 @@ pub fn deinit(self: *NvPtx) void {...@@ -82,11 +82,17 @@ pub fn deinit(self: *NvPtx) void {
82 self.llvm_object.deinit();82 self.llvm_object.deinit();
83}83}
8484
85pub fn updateFunc(self: *NvPtx, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {85pub fn updateFunc(
86 self: *NvPtx,
87 pt: Zcu.PerThread,
88 func_index: InternPool.Index,
89 air: Air,
90 liveness: Liveness,
91) link.File.UpdateNavError!void {
86 try self.llvm_object.updateFunc(pt, func_index, air, liveness);92 try self.llvm_object.updateFunc(pt, func_index, air, liveness);
87}93}
8894
89pub fn updateNav(self: *NvPtx, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {95pub fn updateNav(self: *NvPtx, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void {
90 return self.llvm_object.updateNav(pt, nav);96 return self.llvm_object.updateNav(pt, nav);
91}97}
9298
...@@ -94,7 +100,7 @@ pub fn updateExports(...@@ -94,7 +100,7 @@ pub fn updateExports(
94 self: *NvPtx,100 self: *NvPtx,
95 pt: Zcu.PerThread,101 pt: Zcu.PerThread,
96 exported: Zcu.Exported,102 exported: Zcu.Exported,
97 export_indices: []const u32,103 export_indices: []const Zcu.Export.Index,
98) !void {104) !void {
99 if (build_options.skip_non_native and builtin.object_format != .nvptx)105 if (build_options.skip_non_native and builtin.object_format != .nvptx)
100 @panic("Attempted to compile for object format that was disabled by build configuration");106 @panic("Attempted to compile for object format that was disabled by build configuration");
...@@ -102,10 +108,6 @@ pub fn updateExports(...@@ -102,10 +108,6 @@ pub fn updateExports(
102 return self.llvm_object.updateExports(pt, exported, export_indices);108 return self.llvm_object.updateExports(pt, exported, export_indices);
103}109}
104110
105pub fn freeDecl(self: *NvPtx, decl_index: InternPool.DeclIndex) void {
106 return self.llvm_object.freeDecl(decl_index);
107}
108
109pub fn flush(self: *NvPtx, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {111pub fn flush(self: *NvPtx, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
110 return self.flushModule(arena, tid, prog_node);112 return self.flushModule(arena, tid, prog_node);
111}113}
src/link/Plan9.zig+76-110
...@@ -60,7 +60,7 @@ fn_nav_table: std.AutoArrayHashMapUnmanaged(...@@ -60,7 +60,7 @@ fn_nav_table: std.AutoArrayHashMapUnmanaged(
60data_nav_table: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u8) = .empty,60data_nav_table: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u8) = .empty,
61/// When `updateExports` is called, we store the export indices here, to be used61/// When `updateExports` is called, we store the export indices here, to be used
62/// during flush.62/// during flush.
63nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u32) = .empty,63nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []Zcu.Export.Index) = .empty,
6464
65lazy_syms: LazySymbolTable = .{},65lazy_syms: LazySymbolTable = .{},
6666
...@@ -345,6 +345,7 @@ fn putFn(self: *Plan9, nav_index: InternPool.Nav.Index, out: FnNavOutput) !void...@@ -345,6 +345,7 @@ fn putFn(self: *Plan9, nav_index: InternPool.Nav.Index, out: FnNavOutput) !void
345 try a.writer().writeInt(u16, 1, .big);345 try a.writer().writeInt(u16, 1, .big);
346346
347 // getting the full file path347 // getting the full file path
348 // TODO don't call getcwd here, that is inappropriate
348 var buf: [std.fs.max_path_bytes]u8 = undefined;349 var buf: [std.fs.max_path_bytes]u8 = undefined;
349 const full_path = try std.fs.path.join(arena, &.{350 const full_path = try std.fs.path.join(arena, &.{
350 file.mod.root.root_dir.path orelse try std.posix.getcwd(&buf),351 file.mod.root.root_dir.path orelse try std.posix.getcwd(&buf),
...@@ -385,7 +386,13 @@ fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !voi...@@ -385,7 +386,13 @@ fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !voi
385 }386 }
386}387}
387388
388pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {389pub fn updateFunc(
390 self: *Plan9,
391 pt: Zcu.PerThread,
392 func_index: InternPool.Index,
393 air: Air,
394 liveness: Liveness,
395) link.File.UpdateNavError!void {
389 if (build_options.skip_non_native and builtin.object_format != .plan9) {396 if (build_options.skip_non_native and builtin.object_format != .plan9) {
390 @panic("Attempted to compile for object format that was disabled by build configuration");397 @panic("Attempted to compile for object format that was disabled by build configuration");
391 }398 }
...@@ -397,8 +404,8 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -397,8 +404,8 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,
397404
398 const atom_idx = try self.seeNav(pt, func.owner_nav);405 const atom_idx = try self.seeNav(pt, func.owner_nav);
399406
400 var code_buffer = std.ArrayList(u8).init(gpa);407 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
401 defer code_buffer.deinit();408 defer code_buffer.deinit(gpa);
402 var dbg_info_output: DebugInfoOutput = .{409 var dbg_info_output: DebugInfoOutput = .{
403 .dbg_line = std.ArrayList(u8).init(gpa),410 .dbg_line = std.ArrayList(u8).init(gpa),
404 .start_line = null,411 .start_line = null,
...@@ -409,7 +416,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -409,7 +416,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,
409 };416 };
410 defer dbg_info_output.dbg_line.deinit();417 defer dbg_info_output.dbg_line.deinit();
411418
412 const res = try codegen.generateFunction(419 try codegen.generateFunction(
413 &self.base,420 &self.base,
414 pt,421 pt,
415 zcu.navSrcLoc(func.owner_nav),422 zcu.navSrcLoc(func.owner_nav),
...@@ -419,10 +426,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -419,10 +426,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,
419 &code_buffer,426 &code_buffer,
420 .{ .plan9 = &dbg_info_output },427 .{ .plan9 = &dbg_info_output },
421 );428 );
422 const code = switch (res) {429 const code = try code_buffer.toOwnedSlice(gpa);
423 .ok => try code_buffer.toOwnedSlice(),
424 .fail => |em| return zcu.failed_codegen.put(gpa, func.owner_nav, em),
425 };
426 self.getAtomPtr(atom_idx).code = .{430 self.getAtomPtr(atom_idx).code = .{
427 .code_ptr = null,431 .code_ptr = null,
428 .other = .{ .nav_index = func.owner_nav },432 .other = .{ .nav_index = func.owner_nav },
...@@ -433,11 +437,13 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -433,11 +437,13 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,
433 .start_line = dbg_info_output.start_line.?,437 .start_line = dbg_info_output.start_line.?,
434 .end_line = dbg_info_output.end_line,438 .end_line = dbg_info_output.end_line,
435 };439 };
436 try self.putFn(func.owner_nav, out);440 // The awkward error handling here is due to putFn calling `std.posix.getcwd` which it should not do.
441 self.putFn(func.owner_nav, out) catch |err|
442 return zcu.codegenFail(func.owner_nav, "failed to put fn: {s}", .{@errorName(err)});
437 return self.updateFinish(pt, func.owner_nav);443 return self.updateFinish(pt, func.owner_nav);
438}444}
439445
440pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {446pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.File.UpdateNavError!void {
441 const zcu = pt.zcu;447 const zcu = pt.zcu;
442 const gpa = zcu.gpa;448 const gpa = zcu.gpa;
443 const ip = &zcu.intern_pool;449 const ip = &zcu.intern_pool;
...@@ -456,10 +462,10 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde...@@ -456,10 +462,10 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
456 if (nav_init.typeOf(zcu).hasRuntimeBits(zcu)) {462 if (nav_init.typeOf(zcu).hasRuntimeBits(zcu)) {
457 const atom_idx = try self.seeNav(pt, nav_index);463 const atom_idx = try self.seeNav(pt, nav_index);
458464
459 var code_buffer = std.ArrayList(u8).init(gpa);465 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
460 defer code_buffer.deinit();466 defer code_buffer.deinit(gpa);
461 // TODO we need the symbol index for symbol in the table of locals for the containing atom467 // TODO we need the symbol index for symbol in the table of locals for the containing atom
462 const res = try codegen.generateSymbol(468 try codegen.generateSymbol(
463 &self.base,469 &self.base,
464 pt,470 pt,
465 zcu.navSrcLoc(nav_index),471 zcu.navSrcLoc(nav_index),
...@@ -467,10 +473,7 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde...@@ -467,10 +473,7 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
467 &code_buffer,473 &code_buffer,
468 .{ .atom_index = @intCast(atom_idx) },474 .{ .atom_index = @intCast(atom_idx) },
469 );475 );
470 const code = switch (res) {476 const code = code_buffer.items;
471 .ok => code_buffer.items,
472 .fail => |em| return zcu.failed_codegen.put(gpa, nav_index, em),
473 };
474 try self.data_nav_table.ensureUnusedCapacity(gpa, 1);477 try self.data_nav_table.ensureUnusedCapacity(gpa, 1);
475 const duped_code = try gpa.dupe(u8, code);478 const duped_code = try gpa.dupe(u8, code);
476 self.getAtomPtr(self.navs.get(nav_index).?.index).code = .{ .code_ptr = null, .other = .{ .nav_index = nav_index } };479 self.getAtomPtr(self.navs.get(nav_index).?.index).code = .{ .code_ptr = null, .other = .{ .nav_index = nav_index } };
...@@ -529,16 +532,21 @@ fn allocateGotIndex(self: *Plan9) usize {...@@ -529,16 +532,21 @@ fn allocateGotIndex(self: *Plan9) usize {
529 }532 }
530}533}
531534
532pub fn flush(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {535pub fn flush(
536 self: *Plan9,
537 arena: Allocator,
538 tid: Zcu.PerThread.Id,
539 prog_node: std.Progress.Node,
540) link.File.FlushError!void {
533 const comp = self.base.comp;541 const comp = self.base.comp;
542 const diags = &comp.link_diags;
534 const use_lld = build_options.have_llvm and comp.config.use_lld;543 const use_lld = build_options.have_llvm and comp.config.use_lld;
535 assert(!use_lld);544 assert(!use_lld);
536545
537 switch (link.File.effectiveOutputMode(use_lld, comp.config.output_mode)) {546 switch (link.File.effectiveOutputMode(use_lld, comp.config.output_mode)) {
538 .Exe => {},547 .Exe => {},
539 // plan9 object files are totally different548 .Obj => return diags.fail("writing plan9 object files unimplemented", .{}),
540 .Obj => return error.TODOImplementPlan9Objs,549 .Lib => return diags.fail("writing plan9 lib files unimplemented", .{}),
541 .Lib => return error.TODOImplementWritingLibFiles,
542 }550 }
543 return self.flushModule(arena, tid, prog_node);551 return self.flushModule(arena, tid, prog_node);
544}552}
...@@ -583,7 +591,13 @@ fn atomCount(self: *Plan9) usize {...@@ -583,7 +591,13 @@ fn atomCount(self: *Plan9) usize {
583 return data_nav_count + fn_nav_count + lazy_atom_count + extern_atom_count + uav_atom_count;591 return data_nav_count + fn_nav_count + lazy_atom_count + extern_atom_count + uav_atom_count;
584}592}
585593
586pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {594pub fn flushModule(
595 self: *Plan9,
596 arena: Allocator,
597 /// TODO: stop using this
598 tid: Zcu.PerThread.Id,
599 prog_node: std.Progress.Node,
600) link.File.FlushError!void {
587 if (build_options.skip_non_native and builtin.object_format != .plan9) {601 if (build_options.skip_non_native and builtin.object_format != .plan9) {
588 @panic("Attempted to compile for object format that was disabled by build configuration");602 @panic("Attempted to compile for object format that was disabled by build configuration");
589 }603 }
...@@ -594,6 +608,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -594,6 +608,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
594 _ = arena; // Has the same lifetime as the call to Compilation.update.608 _ = arena; // Has the same lifetime as the call to Compilation.update.
595609
596 const comp = self.base.comp;610 const comp = self.base.comp;
611 const diags = &comp.link_diags;
597 const gpa = comp.gpa;612 const gpa = comp.gpa;
598 const target = comp.root_mod.resolved_target.result;613 const target = comp.root_mod.resolved_target.result;
599614
...@@ -605,7 +620,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -605,7 +620,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
605 defer assert(self.hdr.entry != 0x0);620 defer assert(self.hdr.entry != 0x0);
606621
607 const pt: Zcu.PerThread = .activate(622 const pt: Zcu.PerThread = .activate(
608 self.base.comp.zcu orelse return error.LinkingWithoutZigSourceUnimplemented,623 self.base.comp.zcu orelse return diags.fail("linking without zig source unimplemented", .{}),
609 tid,624 tid,
610 );625 );
611 defer pt.deactivate();626 defer pt.deactivate();
...@@ -614,22 +629,16 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -614,22 +629,16 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
614 if (self.lazy_syms.getPtr(.none)) |metadata| {629 if (self.lazy_syms.getPtr(.none)) |metadata| {
615 // Most lazy symbols can be updated on first use, but630 // Most lazy symbols can be updated on first use, but
616 // anyerror needs to wait for everything to be flushed.631 // anyerror needs to wait for everything to be flushed.
617 if (metadata.text_state != .unused) self.updateLazySymbolAtom(632 if (metadata.text_state != .unused) try self.updateLazySymbolAtom(
618 pt,633 pt,
619 .{ .kind = .code, .ty = .anyerror_type },634 .{ .kind = .code, .ty = .anyerror_type },
620 metadata.text_atom,635 metadata.text_atom,
621 ) catch |err| return switch (err) {636 );
622 error.CodegenFail => error.FlushFailure,637 if (metadata.rodata_state != .unused) try self.updateLazySymbolAtom(
623 else => |e| e,
624 };
625 if (metadata.rodata_state != .unused) self.updateLazySymbolAtom(
626 pt,638 pt,
627 .{ .kind = .const_data, .ty = .anyerror_type },639 .{ .kind = .const_data, .ty = .anyerror_type },
628 metadata.rodata_atom,640 metadata.rodata_atom,
629 ) catch |err| return switch (err) {641 );
630 error.CodegenFail => error.FlushFailure,
631 else => |e| e,
632 };
633 }642 }
634 for (self.lazy_syms.values()) |*metadata| {643 for (self.lazy_syms.values()) |*metadata| {
635 if (metadata.text_state != .unused) metadata.text_state = .flushed;644 if (metadata.text_state != .unused) metadata.text_state = .flushed;
...@@ -902,30 +911,29 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -902,30 +911,29 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
902 }911 }
903 }912 }
904 }913 }
905 // write it all!914 file.pwritevAll(iovecs, 0) catch |err| return diags.fail("failed to write file: {s}", .{@errorName(err)});
906 try file.pwritevAll(iovecs, 0);
907}915}
908fn addNavExports(916fn addNavExports(
909 self: *Plan9,917 self: *Plan9,
910 mod: *Zcu,918 zcu: *Zcu,
911 nav_index: InternPool.Nav.Index,919 nav_index: InternPool.Nav.Index,
912 export_indices: []const u32,920 export_indices: []const Zcu.Export.Index,
913) !void {921) !void {
914 const gpa = self.base.comp.gpa;922 const gpa = self.base.comp.gpa;
915 const metadata = self.navs.getPtr(nav_index).?;923 const metadata = self.navs.getPtr(nav_index).?;
916 const atom = self.getAtom(metadata.index);924 const atom = self.getAtom(metadata.index);
917925
918 for (export_indices) |export_idx| {926 for (export_indices) |export_idx| {
919 const exp = mod.all_exports.items[export_idx];927 const exp = export_idx.ptr(zcu);
920 const exp_name = exp.opts.name.toSlice(&mod.intern_pool);928 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
921 // plan9 does not support custom sections929 // plan9 does not support custom sections
922 if (exp.opts.section.unwrap()) |section_name| {930 if (exp.opts.section.unwrap()) |section_name| {
923 if (!section_name.eqlSlice(".text", &mod.intern_pool) and931 if (!section_name.eqlSlice(".text", &zcu.intern_pool) and
924 !section_name.eqlSlice(".data", &mod.intern_pool))932 !section_name.eqlSlice(".data", &zcu.intern_pool))
925 {933 {
926 try mod.failed_exports.put(mod.gpa, export_idx, try Zcu.ErrorMsg.create(934 try zcu.failed_exports.put(zcu.gpa, export_idx, try Zcu.ErrorMsg.create(
927 gpa,935 gpa,
928 mod.navSrcLoc(nav_index),936 zcu.navSrcLoc(nav_index),
929 "plan9 does not support extra sections",937 "plan9 does not support extra sections",
930 .{},938 .{},
931 ));939 ));
...@@ -947,50 +955,6 @@ fn addNavExports(...@@ -947,50 +955,6 @@ fn addNavExports(
947 }955 }
948}956}
949957
950pub fn freeDecl(self: *Plan9, decl_index: InternPool.DeclIndex) void {
951 const gpa = self.base.comp.gpa;
952 // TODO audit the lifetimes of decls table entries. It's possible to get
953 // freeDecl without any updateDecl in between.
954 const zcu = self.base.comp.zcu.?;
955 const decl = zcu.declPtr(decl_index);
956 const is_fn = decl.val.isFuncBody(zcu);
957 if (is_fn) {
958 const symidx_and_submap = self.fn_decl_table.get(decl.getFileScope(zcu)).?;
959 var submap = symidx_and_submap.functions;
960 if (submap.fetchSwapRemove(decl_index)) |removed_entry| {
961 gpa.free(removed_entry.value.code);
962 gpa.free(removed_entry.value.lineinfo);
963 }
964 if (submap.count() == 0) {
965 self.syms.items[symidx_and_submap.sym_index] = aout.Sym.undefined_symbol;
966 self.syms_index_free_list.append(gpa, symidx_and_submap.sym_index) catch {};
967 submap.deinit(gpa);
968 }
969 } else {
970 if (self.data_decl_table.fetchSwapRemove(decl_index)) |removed_entry| {
971 gpa.free(removed_entry.value);
972 }
973 }
974 if (self.decls.fetchRemove(decl_index)) |const_kv| {
975 var kv = const_kv;
976 const atom = self.getAtom(kv.value.index);
977 if (atom.got_index) |i| {
978 // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length
979 self.got_index_free_list.append(gpa, i) catch {};
980 }
981 if (atom.sym_index) |i| {
982 self.syms_index_free_list.append(gpa, i) catch {};
983 self.syms.items[i] = aout.Sym.undefined_symbol;
984 }
985 kv.value.exports.deinit(gpa);
986 }
987 {
988 const atom_index = self.decls.get(decl_index).?.index;
989 const relocs = self.relocs.getPtr(atom_index) orelse return;
990 relocs.clearAndFree(gpa);
991 assert(self.relocs.remove(atom_index));
992 }
993}
994fn createAtom(self: *Plan9) !Atom.Index {958fn createAtom(self: *Plan9) !Atom.Index {
995 const gpa = self.base.comp.gpa;959 const gpa = self.base.comp.gpa;
996 const index = @as(Atom.Index, @intCast(self.atoms.items.len));960 const index = @as(Atom.Index, @intCast(self.atoms.items.len));
...@@ -1043,7 +1007,7 @@ pub fn updateExports(...@@ -1043,7 +1007,7 @@ pub fn updateExports(
1043 self: *Plan9,1007 self: *Plan9,
1044 pt: Zcu.PerThread,1008 pt: Zcu.PerThread,
1045 exported: Zcu.Exported,1009 exported: Zcu.Exported,
1046 export_indices: []const u32,1010 export_indices: []const Zcu.Export.Index,
1047) !void {1011) !void {
1048 const gpa = self.base.comp.gpa;1012 const gpa = self.base.comp.gpa;
1049 switch (exported) {1013 switch (exported) {
...@@ -1054,7 +1018,7 @@ pub fn updateExports(...@@ -1054,7 +1018,7 @@ pub fn updateExports(
1054 gpa.free(kv.value);1018 gpa.free(kv.value);
1055 }1019 }
1056 try self.nav_exports.ensureUnusedCapacity(gpa, 1);1020 try self.nav_exports.ensureUnusedCapacity(gpa, 1);
1057 const duped_indices = try gpa.dupe(u32, export_indices);1021 const duped_indices = try gpa.dupe(Zcu.Export.Index, export_indices);
1058 self.nav_exports.putAssumeCapacityNoClobber(nav, duped_indices);1022 self.nav_exports.putAssumeCapacityNoClobber(nav, duped_indices);
1059 },1023 },
1060 }1024 }
...@@ -1085,12 +1049,19 @@ pub fn getOrCreateAtomForLazySymbol(self: *Plan9, pt: Zcu.PerThread, lazy_sym: F...@@ -1085,12 +1049,19 @@ pub fn getOrCreateAtomForLazySymbol(self: *Plan9, pt: Zcu.PerThread, lazy_sym: F
1085 return atom;1049 return atom;
1086}1050}
10871051
1088fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, atom_index: Atom.Index) !void {1052fn updateLazySymbolAtom(
1053 self: *Plan9,
1054 pt: Zcu.PerThread,
1055 sym: File.LazySymbol,
1056 atom_index: Atom.Index,
1057) error{ LinkFailure, OutOfMemory }!void {
1089 const gpa = pt.zcu.gpa;1058 const gpa = pt.zcu.gpa;
1059 const comp = self.base.comp;
1060 const diags = &comp.link_diags;
10901061
1091 var required_alignment: InternPool.Alignment = .none;1062 var required_alignment: InternPool.Alignment = .none;
1092 var code_buffer = std.ArrayList(u8).init(gpa);1063 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1093 defer code_buffer.deinit();1064 defer code_buffer.deinit(gpa);
10941065
1095 // create the symbol for the name1066 // create the symbol for the name
1096 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{1067 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{
...@@ -1107,7 +1078,7 @@ fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, a...@@ -1107,7 +1078,7 @@ fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, a
11071078
1108 // generate the code1079 // generate the code
1109 const src = Type.fromInterned(sym.ty).srcLocOrNull(pt.zcu) orelse Zcu.LazySrcLoc.unneeded;1080 const src = Type.fromInterned(sym.ty).srcLocOrNull(pt.zcu) orelse Zcu.LazySrcLoc.unneeded;
1110 const res = try codegen.generateLazySymbol(1081 codegen.generateLazySymbol(
1111 &self.base,1082 &self.base,
1112 pt,1083 pt,
1113 src,1084 src,
...@@ -1116,14 +1087,12 @@ fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, a...@@ -1116,14 +1087,12 @@ fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, a
1116 &code_buffer,1087 &code_buffer,
1117 .none,1088 .none,
1118 .{ .atom_index = @intCast(atom_index) },1089 .{ .atom_index = @intCast(atom_index) },
1119 );1090 ) catch |err| switch (err) {
1120 const code = switch (res) {1091 error.OutOfMemory => return error.OutOfMemory,
1121 .ok => code_buffer.items,1092 error.CodegenFail => return error.LinkFailure,
1122 .fail => |em| {1093 error.Overflow => return diags.fail("codegen failure: encountered number too big for compiler", .{}),
1123 log.err("{s}", .{em.msg});
1124 return error.CodegenFail;
1125 },
1126 };1094 };
1095 const code = code_buffer.items;
1127 // duped_code is freed when the atom is freed1096 // duped_code is freed when the atom is freed
1128 const duped_code = try gpa.dupe(u8, code);1097 const duped_code = try gpa.dupe(u8, code);
1129 errdefer gpa.free(duped_code);1098 errdefer gpa.free(duped_code);
...@@ -1283,7 +1252,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -1283,7 +1252,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
1283 try self.writeSym(writer, sym);1252 try self.writeSym(writer, sym);
1284 if (self.nav_exports.get(nav_index)) |export_indices| {1253 if (self.nav_exports.get(nav_index)) |export_indices| {
1285 for (export_indices) |export_idx| {1254 for (export_indices) |export_idx| {
1286 const exp = zcu.all_exports.items[export_idx];1255 const exp = export_idx.ptr(zcu);
1287 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {1256 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {
1288 try self.writeSym(writer, self.syms.items[exp_i]);1257 try self.writeSym(writer, self.syms.items[exp_i]);
1289 }1258 }
...@@ -1322,7 +1291,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -1322,7 +1291,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
1322 try self.writeSym(writer, sym);1291 try self.writeSym(writer, sym);
1323 if (self.nav_exports.get(nav_index)) |export_indices| {1292 if (self.nav_exports.get(nav_index)) |export_indices| {
1324 for (export_indices) |export_idx| {1293 for (export_indices) |export_idx| {
1325 const exp = zcu.all_exports.items[export_idx];1294 const exp = export_idx.ptr(zcu);
1326 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {1295 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {
1327 const s = self.syms.items[exp_i];1296 const s = self.syms.items[exp_i];
1328 if (mem.eql(u8, s.name, "_start"))1297 if (mem.eql(u8, s.name, "_start"))
...@@ -1432,19 +1401,16 @@ pub fn lowerUav(...@@ -1432,19 +1401,16 @@ pub fn lowerUav(
1432 const got_index = self.allocateGotIndex();1401 const got_index = self.allocateGotIndex();
1433 gop.value_ptr.* = index;1402 gop.value_ptr.* = index;
1434 // we need to free name latex1403 // we need to free name latex
1435 var code_buffer = std.ArrayList(u8).init(gpa);1404 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1436 const res = try codegen.generateSymbol(&self.base, pt, src_loc, val, &code_buffer, .{ .atom_index = index });1405 defer code_buffer.deinit(gpa);
1437 const code = switch (res) {1406 try codegen.generateSymbol(&self.base, pt, src_loc, val, &code_buffer, .{ .atom_index = index });
1438 .ok => code_buffer.items,
1439 .fail => |em| return .{ .fail = em },
1440 };
1441 const atom_ptr = self.getAtomPtr(index);1407 const atom_ptr = self.getAtomPtr(index);
1442 atom_ptr.* = .{1408 atom_ptr.* = .{
1443 .type = .d,1409 .type = .d,
1444 .offset = undefined,1410 .offset = undefined,
1445 .sym_index = null,1411 .sym_index = null,
1446 .got_index = got_index,1412 .got_index = got_index,
1447 .code = Atom.CodePtr.fromSlice(code),1413 .code = Atom.CodePtr.fromSlice(try code_buffer.toOwnedSlice(gpa)),
1448 };1414 };
1449 _ = try atom_ptr.getOrCreateSymbolTableEntry(self);1415 _ = try atom_ptr.getOrCreateSymbolTableEntry(self);
1450 self.syms.items[atom_ptr.sym_index.?] = .{1416 self.syms.items[atom_ptr.sym_index.?] = .{
src/link/SpirV.zig+26-18
...@@ -122,7 +122,13 @@ pub fn deinit(self: *SpirV) void {...@@ -122,7 +122,13 @@ pub fn deinit(self: *SpirV) void {
122 self.object.deinit();122 self.object.deinit();
123}123}
124124
125pub fn updateFunc(self: *SpirV, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {125pub fn updateFunc(
126 self: *SpirV,
127 pt: Zcu.PerThread,
128 func_index: InternPool.Index,
129 air: Air,
130 liveness: Liveness,
131) link.File.UpdateNavError!void {
126 if (build_options.skip_non_native) {132 if (build_options.skip_non_native) {
127 @panic("Attempted to compile for architecture that was disabled by build configuration");133 @panic("Attempted to compile for architecture that was disabled by build configuration");
128 }134 }
...@@ -134,7 +140,7 @@ pub fn updateFunc(self: *SpirV, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -134,7 +140,7 @@ pub fn updateFunc(self: *SpirV, pt: Zcu.PerThread, func_index: InternPool.Index,
134 try self.object.updateFunc(pt, func_index, air, liveness);140 try self.object.updateFunc(pt, func_index, air, liveness);
135}141}
136142
137pub fn updateNav(self: *SpirV, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {143pub fn updateNav(self: *SpirV, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void {
138 if (build_options.skip_non_native) {144 if (build_options.skip_non_native) {
139 @panic("Attempted to compile for architecture that was disabled by build configuration");145 @panic("Attempted to compile for architecture that was disabled by build configuration");
140 }146 }
...@@ -149,7 +155,7 @@ pub fn updateExports(...@@ -149,7 +155,7 @@ pub fn updateExports(
149 self: *SpirV,155 self: *SpirV,
150 pt: Zcu.PerThread,156 pt: Zcu.PerThread,
151 exported: Zcu.Exported,157 exported: Zcu.Exported,
152 export_indices: []const u32,158 export_indices: []const Zcu.Export.Index,
153) !void {159) !void {
154 const zcu = pt.zcu;160 const zcu = pt.zcu;
155 const ip = &zcu.intern_pool;161 const ip = &zcu.intern_pool;
...@@ -184,7 +190,7 @@ pub fn updateExports(...@@ -184,7 +190,7 @@ pub fn updateExports(
184 };190 };
185191
186 for (export_indices) |export_idx| {192 for (export_indices) |export_idx| {
187 const exp = zcu.all_exports.items[export_idx];193 const exp = export_idx.ptr(zcu);
188 try self.object.spv.declareEntryPoint(194 try self.object.spv.declareEntryPoint(
189 spv_decl_index,195 spv_decl_index,
190 exp.opts.name.toSlice(ip),196 exp.opts.name.toSlice(ip),
...@@ -196,16 +202,21 @@ pub fn updateExports(...@@ -196,16 +202,21 @@ pub fn updateExports(
196 // TODO: Export regular functions, variables, etc using Linkage attributes.202 // TODO: Export regular functions, variables, etc using Linkage attributes.
197}203}
198204
199pub fn freeDecl(self: *SpirV, decl_index: InternPool.DeclIndex) void {
200 _ = self;
201 _ = decl_index;
202}
203
204pub fn flush(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {205pub fn flush(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
205 return self.flushModule(arena, tid, prog_node);206 return self.flushModule(arena, tid, prog_node);
206}207}
207208
208pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {209pub fn flushModule(
210 self: *SpirV,
211 arena: Allocator,
212 tid: Zcu.PerThread.Id,
213 prog_node: std.Progress.Node,
214) link.File.FlushError!void {
215 // The goal is to never use this because it's only needed if we need to
216 // write to InternPool, but flushModule is too late to be writing to the
217 // InternPool.
218 _ = tid;
219
209 if (build_options.skip_non_native) {220 if (build_options.skip_non_native) {
210 @panic("Attempted to compile for architecture that was disabled by build configuration");221 @panic("Attempted to compile for architecture that was disabled by build configuration");
211 }222 }
...@@ -216,12 +227,11 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -216,12 +227,11 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
216 const sub_prog_node = prog_node.start("Flush Module", 0);227 const sub_prog_node = prog_node.start("Flush Module", 0);
217 defer sub_prog_node.end();228 defer sub_prog_node.end();
218229
219 const spv = &self.object.spv;
220
221 const comp = self.base.comp;230 const comp = self.base.comp;
231 const spv = &self.object.spv;
232 const diags = &comp.link_diags;
222 const gpa = comp.gpa;233 const gpa = comp.gpa;
223 const target = comp.getTarget();234 const target = comp.getTarget();
224 _ = tid;
225235
226 try writeCapabilities(spv, target);236 try writeCapabilities(spv, target);
227 try writeMemoryModel(spv, target);237 try writeMemoryModel(spv, target);
...@@ -264,13 +274,11 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -264,13 +274,11 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
264274
265 const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) {275 const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) {
266 error.OutOfMemory => return error.OutOfMemory,276 error.OutOfMemory => return error.OutOfMemory,
267 else => |other| {277 else => |other| return diags.fail("error while linking: {s}", .{@errorName(other)}),
268 log.err("error while linking: {s}", .{@errorName(other)});
269 return error.FlushFailure;
270 },
271 };278 };
272279
273 try self.base.file.?.writeAll(std.mem.sliceAsBytes(linked_module));280 self.base.file.?.writeAll(std.mem.sliceAsBytes(linked_module)) catch |err|
281 return diags.fail("failed to write: {s}", .{@errorName(err)});
274}282}
275283
276fn linkModule(self: *SpirV, a: Allocator, module: []Word, progress: std.Progress.Node) ![]Word {284fn linkModule(self: *SpirV, a: Allocator, module: []Word, progress: std.Progress.Node) ![]Word {
src/link/Wasm.zig+3698-3751
...@@ -1,45 +1,67 @@...@@ -1,45 +1,67 @@
1//! The overall strategy here is to load all the object file data into memory
2//! as inputs are parsed. During `prelink`, as much linking as possible is
3//! performed without any knowledge of functions and globals provided by the
4//! Zcu. If there is no Zcu, effectively all linking is done in `prelink`.
5//!
6//! `updateFunc`, `updateNav`, `updateExports`, and `deleteExport` are handled
7//! by merely tracking references to the relevant functions and globals. All
8//! the linking logic between objects and Zcu happens in `flush`. Many
9//! components of the final output are computed on-the-fly at this time rather
10//! than being precomputed and stored separately.
11
1const Wasm = @This();12const Wasm = @This();
2const build_options = @import("build_options");13const Archive = @import("Wasm/Archive.zig");
14const Object = @import("Wasm/Object.zig");
15pub const Flush = @import("Wasm/Flush.zig");
316
4const builtin = @import("builtin");17const builtin = @import("builtin");
5const native_endian = builtin.cpu.arch.endian();18const native_endian = builtin.cpu.arch.endian();
619
20const build_options = @import("build_options");
21
7const std = @import("std");22const std = @import("std");
8const Allocator = std.mem.Allocator;23const Allocator = std.mem.Allocator;
9const Cache = std.Build.Cache;24const Cache = std.Build.Cache;
10const Path = Cache.Path;25const Path = Cache.Path;
11const assert = std.debug.assert;26const assert = std.debug.assert;
12const fs = std.fs;27const fs = std.fs;
13const gc_log = std.log.scoped(.gc);
14const leb = std.leb;28const leb = std.leb;
15const log = std.log.scoped(.link);29const log = std.log.scoped(.link);
16const mem = std.mem;30const mem = std.mem;
1731
18const Air = @import("../Air.zig");32const Air = @import("../Air.zig");
19const Archive = @import("Wasm/Archive.zig");33const Mir = @import("../arch/wasm/Mir.zig");
20const CodeGen = @import("../arch/wasm/CodeGen.zig");34const CodeGen = @import("../arch/wasm/CodeGen.zig");
35const abi = @import("../arch/wasm/abi.zig");
21const Compilation = @import("../Compilation.zig");36const Compilation = @import("../Compilation.zig");
22const Dwarf = @import("Dwarf.zig");37const Dwarf = @import("Dwarf.zig");
23const InternPool = @import("../InternPool.zig");38const InternPool = @import("../InternPool.zig");
24const Liveness = @import("../Liveness.zig");39const Liveness = @import("../Liveness.zig");
25const LlvmObject = @import("../codegen/llvm.zig").Object;40const LlvmObject = @import("../codegen/llvm.zig").Object;
26const Object = @import("Wasm/Object.zig");
27const Symbol = @import("Wasm/Symbol.zig");
28const Type = @import("../Type.zig");
29const Value = @import("../Value.zig");
30const Zcu = @import("../Zcu.zig");41const Zcu = @import("../Zcu.zig");
31const ZigObject = @import("Wasm/ZigObject.zig");
32const codegen = @import("../codegen.zig");42const codegen = @import("../codegen.zig");
33const dev = @import("../dev.zig");43const dev = @import("../dev.zig");
34const link = @import("../link.zig");44const link = @import("../link.zig");
35const lldMain = @import("../main.zig").lldMain;45const lldMain = @import("../main.zig").lldMain;
36const trace = @import("../tracy.zig").trace;46const trace = @import("../tracy.zig").trace;
37const wasi_libc = @import("../wasi_libc.zig");47const wasi_libc = @import("../wasi_libc.zig");
48const Value = @import("../Value.zig");
3849
39base: link.File,50base: link.File,
40/// Null-terminated strings, indexes have type String and string_table provides51/// Null-terminated strings, indexes have type String and string_table provides
41/// lookup.52/// lookup.
53///
54/// There are a couple of sites that add things here without adding
55/// corresponding string_table entries. For such cases, when implementing
56/// serialization/deserialization, they should be adjusted to prefix that data
57/// with a null byte so that deserialization does not attempt to create
58/// string_table entries for them. Alternately those sites could be moved to
59/// use a different byte array for this purpose.
42string_bytes: std.ArrayListUnmanaged(u8),60string_bytes: std.ArrayListUnmanaged(u8),
61/// Sometimes we have logic that wants to borrow string bytes to store
62/// arbitrary things in there. In this case it is not allowed to intern new
63/// strings during this time. This safety lock is used to detect misuses.
64string_bytes_lock: std.debug.SafetyLock = .{},
43/// Omitted when serializing linker state.65/// Omitted when serializing linker state.
44string_table: String.Table,66string_table: String.Table,
45/// Symbol name of the entry function to export67/// Symbol name of the entry function to export
...@@ -62,2525 +84,3230 @@ export_table: bool,...@@ -62,2525 +84,3230 @@ export_table: bool,
62name: []const u8,84name: []const u8,
63/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.85/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
64llvm_object: ?LlvmObject.Ptr = null,86llvm_object: ?LlvmObject.Ptr = null,
65zig_object: ?*ZigObject,
66/// List of relocatable files to be linked into the final binary.87/// List of relocatable files to be linked into the final binary.
67objects: std.ArrayListUnmanaged(Object) = .{},88objects: std.ArrayListUnmanaged(Object) = .{},
89
90func_types: std.AutoArrayHashMapUnmanaged(FunctionType, void) = .empty,
91/// Provides a mapping of both imports and provided functions to symbol name.
92/// Local functions may be unnamed.
93/// Key is symbol name, however the `FunctionImport` may have an name override for the import name.
94object_function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImport) = .empty,
95/// All functions for all objects.
96object_functions: std.ArrayListUnmanaged(ObjectFunction) = .empty,
97
98/// Provides a mapping of both imports and provided globals to symbol name.
99/// Local globals may be unnamed.
100object_global_imports: std.AutoArrayHashMapUnmanaged(String, GlobalImport) = .empty,
101/// All globals for all objects.
102object_globals: std.ArrayListUnmanaged(ObjectGlobal) = .empty,
103
104/// All table imports for all objects.
105object_table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport) = .empty,
106/// All parsed table sections for all objects.
107object_tables: std.ArrayListUnmanaged(Table) = .empty,
108
109/// All memory imports for all objects.
110object_memory_imports: std.AutoArrayHashMapUnmanaged(String, MemoryImport) = .empty,
111/// All parsed memory sections for all objects.
112object_memories: std.ArrayListUnmanaged(ObjectMemory) = .empty,
113
114/// All relocations from all objects concatenated. `relocs_start` marks the end
115/// point of object relocations and start point of Zcu relocations.
116object_relocations: std.MultiArrayList(ObjectRelocation) = .empty,
117
118/// List of initialization functions. These must be called in order of priority
119/// by the (synthetic) `__wasm_call_ctors` function.
120object_init_funcs: std.ArrayListUnmanaged(InitFunc) = .empty,
121
122/// The data section of an object has many segments. Each segment corresponds
123/// logically to an object file's .data section, or .rodata section. In
124/// the case of `-fdata-sections` there will be one segment per data symbol.
125object_data_segments: std.ArrayListUnmanaged(ObjectDataSegment) = .empty,
126/// Each segment has many data symbols, which correspond logically to global
127/// constants.
128object_datas: std.ArrayListUnmanaged(ObjectData) = .empty,
129object_data_imports: std.AutoArrayHashMapUnmanaged(String, ObjectDataImport) = .empty,
130/// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations.
131object_custom_segments: std.AutoArrayHashMapUnmanaged(ObjectSectionIndex, CustomSegment) = .empty,
132
133/// All comdat information for all objects.
134object_comdats: std.ArrayListUnmanaged(Comdat) = .empty,
135/// A table that maps the relocations to be performed where the key represents
136/// the section (across all objects) that the slice of relocations applies to.
137object_relocations_table: std.AutoArrayHashMapUnmanaged(ObjectSectionIndex, ObjectRelocation.Slice) = .empty,
138/// Incremented across all objects in order to enable calculation of `ObjectSectionIndex` values.
139object_total_sections: u32 = 0,
140/// All comdat symbols from all objects concatenated.
141object_comdat_symbols: std.MultiArrayList(Comdat.Symbol) = .empty,
142
143/// Relocations to be emitted into an object file. Remains empty when not
144/// emitting an object file.
145out_relocs: std.MultiArrayList(OutReloc) = .empty,
146/// List of locations within `string_bytes` that must be patched with the virtual
147/// memory address of a Uav during `flush`.
148/// When emitting an object file, `out_relocs` is used instead.
149uav_fixups: std.ArrayListUnmanaged(UavFixup) = .empty,
150/// List of locations within `string_bytes` that must be patched with the virtual
151/// memory address of a Nav during `flush`.
152/// When emitting an object file, `out_relocs` is used instead.
153/// No functions here only global variables.
154nav_fixups: std.ArrayListUnmanaged(NavFixup) = .empty,
155/// When a nav reference is a function pointer, this tracks the required function
156/// table entry index that needs to overwrite the code in the final output.
157func_table_fixups: std.ArrayListUnmanaged(FuncTableFixup) = .empty,
158/// Symbols to be emitted into an object file. Remains empty when not emitting
159/// an object file.
160symbol_table: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
161
68/// When importing objects from the host environment, a name must be supplied.162/// When importing objects from the host environment, a name must be supplied.
69/// LLVM uses "env" by default when none is given. This would be a good default for Zig163/// LLVM uses "env" by default when none is given.
70/// to support existing code.164/// This value is passed to object files since wasm tooling conventions provides
71/// TODO: Allow setting this through a flag?165/// no way to specify the module name in the symbol table.
72host_name: String,166object_host_name: OptionalString,
73/// List of symbols generated by the linker.167
74synthetic_symbols: std.ArrayListUnmanaged(Symbol) = .empty,
75/// Maps atoms to their segment index
76atoms: std.AutoHashMapUnmanaged(Segment.Index, Atom.Index) = .empty,
77/// List of all atoms.
78managed_atoms: std.ArrayListUnmanaged(Atom) = .empty,
79
80/// The count of imported functions. This number will be appended
81/// to the function indexes as their index starts at the lowest non-extern function.
82imported_functions_count: u32 = 0,
83/// The count of imported wasm globals. This number will be appended
84/// to the global indexes when sections are merged.
85imported_globals_count: u32 = 0,
86/// The count of imported tables. This number will be appended
87/// to the table indexes when sections are merged.
88imported_tables_count: u32 = 0,
89/// Map of symbol locations, represented by its `Import`
90imports: std.AutoHashMapUnmanaged(SymbolLoc, Import) = .empty,
91/// Represents non-synthetic section entries.
92/// Used for code, data and custom sections.
93segments: std.ArrayListUnmanaged(Segment) = .empty,
94/// Maps a data segment key (such as .rodata) to the index into `segments`.
95data_segments: std.StringArrayHashMapUnmanaged(Segment.Index) = .empty,
96/// A table of `NamedSegment` which provide meta data
97/// about a data symbol such as its name where the key is
98/// the segment index, which can be found from `data_segments`
99segment_info: std.AutoArrayHashMapUnmanaged(Segment.Index, NamedSegment) = .empty,
100
101// Output sections
102/// Output type section
103func_types: std.ArrayListUnmanaged(std.wasm.Type) = .empty,
104/// Output function section where the key is the original
105/// function index and the value is function.
106/// This allows us to map multiple symbols to the same function.
107functions: std.AutoArrayHashMapUnmanaged(
108 struct {
109 /// `none` in the case of synthetic sections.
110 file: OptionalObjectId,
111 index: u32,
112 },
113 struct {
114 func: std.wasm.Func,
115 sym_index: Symbol.Index,
116 },
117) = .{},
118/// Output global section
119wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .empty,
120/// Memory section168/// Memory section
121memories: std.wasm.Memory = .{ .limits = .{169memories: std.wasm.Memory = .{ .limits = .{
122 .min = 0,170 .min = 0,
123 .max = undefined,171 .max = 0,
124 .flags = 0,172 .flags = .{ .has_max = false, .is_shared = false },
125} },173} },
126/// Output table section
127tables: std.ArrayListUnmanaged(std.wasm.Table) = .empty,
128/// Output export section
129exports: std.ArrayListUnmanaged(Export) = .empty,
130/// List of initialization functions. These must be called in order of priority
131/// by the (synthetic) __wasm_call_ctors function.
132init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .empty,
133/// Index to a function defining the entry of the wasm file
134entry: ?u32 = null,
135
136/// Indirect function table, used to call function pointers
137/// When this is non-zero, we must emit a table entry,
138/// as well as an 'elements' section.
139///
140/// Note: Key is symbol location, value represents the index into the table
141function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .empty,
142
143/// All archive files that are lazy loaded.
144/// e.g. when an undefined symbol references a symbol from the archive.
145/// None of this data is serialized to disk because it is trivially reloaded
146/// from unchanged archive files on the next start of the compiler process,
147/// or if those files have changed, the prelink phase needs to be restarted.
148lazy_archives: std.ArrayListUnmanaged(LazyArchive) = .empty,
149
150/// A map of global names to their symbol location
151globals: std.AutoArrayHashMapUnmanaged(String, SymbolLoc) = .empty,
152/// The list of GOT symbols and their location
153got_symbols: std.ArrayListUnmanaged(SymbolLoc) = .empty,
154/// Maps discarded symbols and their positions to the location of the symbol
155/// it was resolved to
156discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .empty,
157/// List of all symbol locations which have been resolved by the linker and will be emit
158/// into the final binary.
159resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .empty,
160/// Symbols that remain undefined after symbol resolution.
161undefs: std.AutoArrayHashMapUnmanaged(String, SymbolLoc) = .empty,
162/// Maps a symbol's location to an atom. This can be used to find meta
163/// data of a symbol, such as its size, or its offset to perform a relocation.
164/// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.
165symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .empty,
166174
167/// `--verbose-link` output.175/// `--verbose-link` output.
168/// Initialized on creation, appended to as inputs are added, printed during `flush`.176/// Initialized on creation, appended to as inputs are added, printed during `flush`.
169/// String data is allocated into Compilation arena.177/// String data is allocated into Compilation arena.
170dump_argv_list: std.ArrayListUnmanaged([]const u8),178dump_argv_list: std.ArrayListUnmanaged([]const u8),
171179
172/// Represents the index into `segments` where the 'code' section lives.
173code_section_index: Segment.OptionalIndex = .none,
174custom_sections: CustomSections,
175preloaded_strings: PreloadedStrings,180preloaded_strings: PreloadedStrings,
176181
177/// Type reflection is used on the field names to autopopulate each field182/// This field is used when emitting an object; `navs_exe` used otherwise.
178/// during initialization.183/// Does not include externs since that data lives elsewhere.
179const PreloadedStrings = struct {184navs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataObj) = .empty,
180 __heap_base: String,185/// This field is unused when emitting an object; `navs_obj` used otherwise.
181 __heap_end: String,186/// Does not include externs since that data lives elsewhere.
182 __indirect_function_table: String,187navs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataExe) = .empty,
183 __linear_memory: String,188/// Tracks all InternPool values referenced by codegen. Needed for outputting
184 __stack_pointer: String,189/// the data segment. This one does not track ref count because object files
185 __tls_align: String,190/// require using max LEB encoding for these references anyway.
186 __tls_base: String,191uavs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataObj) = .empty,
187 __tls_size: String,192/// Tracks ref count to optimize LEB encodings for UAV references.
188 __wasm_apply_global_tls_relocs: String,193uavs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataExe) = .empty,
189 __wasm_call_ctors: String,194/// Sparse table of uavs that need to be emitted with greater alignment than
190 __wasm_init_memory: String,195/// the default for the type.
191 __wasm_init_memory_flag: String,196overaligned_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment) = .empty,
192 __wasm_init_tls: String,197/// When the key is an enum type, this represents a `@tagName` function.
193 __zig_err_name_table: String,198zcu_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuFunc) = .empty,
194 __zig_err_names: String,199nav_exports: std.AutoArrayHashMapUnmanaged(NavExport, Zcu.Export.Index) = .empty,
195 __zig_errors_len: String,200uav_exports: std.AutoArrayHashMapUnmanaged(UavExport, Zcu.Export.Index) = .empty,
196 _initialize: String,201imports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
197 _start: String,202
198 memory: String,203dwarf: ?Dwarf = null,
204
205flush_buffer: Flush = .{},
206
207/// Empty until `prelink`. There it is populated based on object files.
208/// Next, it is copied into `Flush.missing_exports` just before `flush`
209/// and that data is used during `flush`.
210missing_exports: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
211entry_resolution: FunctionImport.Resolution = .unresolved,
212
213/// Empty when outputting an object.
214function_exports: std.AutoArrayHashMapUnmanaged(String, FunctionIndex) = .empty,
215hidden_function_exports: std.AutoArrayHashMapUnmanaged(String, FunctionIndex) = .empty,
216global_exports: std.ArrayListUnmanaged(GlobalExport) = .empty,
217/// Tracks the value at the end of prelink.
218global_exports_len: u32 = 0,
219
220/// Ordered list of non-import functions that will appear in the final binary.
221/// Empty until prelink.
222functions: std.AutoArrayHashMapUnmanaged(FunctionImport.Resolution, void) = .empty,
223/// Tracks the value at the end of prelink, at which point `functions`
224/// contains only object file functions, and nothing from the Zcu yet.
225functions_end_prelink: u32 = 0,
226
227function_imports_len_prelink: u32 = 0,
228data_imports_len_prelink: u32 = 0,
229/// At the end of prelink, this is populated with needed functions from
230/// objects.
231///
232/// During the Zcu phase, entries are not deleted from this table
233/// because doing so would be irreversible when a `deleteExport` call is
234/// handled. However, entries are added during the Zcu phase when extern
235/// functions are passed to `updateNav`.
236///
237/// `flush` gets a copy of this table, and then Zcu exports are applied to
238/// remove elements from the table, and the remainder are either undefined
239/// symbol errors, or import section entries depending on the output mode.
240function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImportId) = .empty,
241
242/// At the end of prelink, this is populated with data symbols needed by
243/// objects.
244///
245/// During the Zcu phase, entries are not deleted from this table
246/// because doing so would be irreversible when a `deleteExport` call is
247/// handled. However, entries are added during the Zcu phase when extern
248/// functions are passed to `updateNav`.
249///
250/// `flush` gets a copy of this table, and then Zcu exports are applied to
251/// remove elements from the table, and the remainder are either undefined
252/// symbol errors, or symbol table entries depending on the output mode.
253data_imports: std.AutoArrayHashMapUnmanaged(String, DataImportId) = .empty,
254/// Set of data symbols that will appear in the final binary. Used to populate
255/// `Flush.data_segments` before sorting.
256data_segments: std.AutoArrayHashMapUnmanaged(DataSegmentId, void) = .empty,
257
258/// Ordered list of non-import globals that will appear in the final binary.
259/// Empty until prelink.
260globals: std.AutoArrayHashMapUnmanaged(GlobalImport.Resolution, void) = .empty,
261/// Tracks the value at the end of prelink, at which point `globals`
262/// contains only object file globals, and nothing from the Zcu yet.
263globals_end_prelink: u32 = 0,
264global_imports: std.AutoArrayHashMapUnmanaged(String, GlobalImportId) = .empty,
265
266/// Ordered list of non-import tables that will appear in the final binary.
267/// Empty until prelink.
268tables: std.AutoArrayHashMapUnmanaged(TableImport.Resolution, void) = .empty,
269table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport.Index) = .empty,
270
271/// All functions that have had their address taken and therefore might be
272/// called via a `call_indirect` function.
273zcu_indirect_function_set: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
274object_indirect_function_import_set: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
275object_indirect_function_set: std.AutoArrayHashMapUnmanaged(ObjectFunctionIndex, void) = .empty,
276
277error_name_table_ref_count: u32 = 0,
278tag_name_table_ref_count: u32 = 0,
279
280/// Set to true if any `GLOBAL_INDEX` relocation is encountered with
281/// `SymbolFlags.tls` set to true. This is for objects only; final
282/// value must be this OR'd with the same logic for zig functions
283/// (set to true if any threadlocal global is used).
284any_tls_relocs: bool = false,
285any_passive_inits: bool = false,
286
287/// All MIR instructions for all Zcu functions.
288mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
289/// Corresponds to `mir_instructions`.
290mir_extra: std.ArrayListUnmanaged(u32) = .empty,
291/// All local types for all Zcu functions.
292all_zcu_locals: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,
293
294params_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,
295returns_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,
296
297/// All Zcu error names in order, null-terminated, concatenated. No need to
298/// serialize; trivially reconstructed.
299error_name_bytes: std.ArrayListUnmanaged(u8) = .empty,
300/// For each Zcu error, in order, offset into `error_name_bytes` where the name
301/// is stored. No need to serialize; trivially reconstructed.
302error_name_offs: std.ArrayListUnmanaged(u32) = .empty,
303
304tag_name_bytes: std.ArrayListUnmanaged(u8) = .empty,
305tag_name_offs: std.ArrayListUnmanaged(u32) = .empty,
306
307pub const TagNameOff = extern struct {
308 off: u32,
309 len: u32,
199};310};
200311
201/// Type reflection is used on the field names to autopopulate each inner `name` field.312/// Index into `Wasm.zcu_indirect_function_set`.
202const CustomSections = struct {313pub const ZcuIndirectFunctionSetIndex = enum(u32) {
203 @".debug_info": CustomSection,314 _,
204 @".debug_pubtypes": CustomSection,
205 @".debug_abbrev": CustomSection,
206 @".debug_line": CustomSection,
207 @".debug_str": CustomSection,
208 @".debug_pubnames": CustomSection,
209 @".debug_loc": CustomSection,
210 @".debug_ranges": CustomSection,
211};315};
212316
213const CustomSection = struct {317pub const UavFixup = extern struct {
214 name: String,318 uavs_exe_index: UavsExeIndex,
215 index: Segment.OptionalIndex,319 /// Index into `string_bytes`.
320 offset: u32,
321 addend: u32,
216};322};
217323
218/// Index into string_bytes324pub const NavFixup = extern struct {
219pub const String = enum(u32) {325 navs_exe_index: NavsExeIndex,
220 _,326 /// Index into `string_bytes`.
327 offset: u32,
328 addend: u32,
329};
221330
222 const Table = std.HashMapUnmanaged(String, void, TableContext, std.hash_map.default_max_load_percentage);331pub const FuncTableFixup = extern struct {
332 table_index: ZcuIndirectFunctionSetIndex,
333 /// Index into `string_bytes`.
334 offset: u32,
335};
223336
224 const TableContext = struct {337/// Index into `objects`.
225 bytes: []const u8,338pub const ObjectIndex = enum(u32) {
339 _,
226340
227 pub fn eql(_: @This(), a: String, b: String) bool {341 pub fn ptr(index: ObjectIndex, wasm: *const Wasm) *Object {
228 return a == b;342 return &wasm.objects.items[@intFromEnum(index)];
229 }343 }
344};
230345
231 pub fn hash(ctx: @This(), key: String) u64 {346/// Index into `Wasm.functions`.
232 return std.hash_map.hashString(mem.sliceTo(ctx.bytes[@intFromEnum(key)..], 0));347pub const FunctionIndex = enum(u32) {
233 }348 _,
234 };
235349
236 const TableIndexAdapter = struct {350 pub fn ptr(index: FunctionIndex, wasm: *const Wasm) *FunctionImport.Resolution {
237 bytes: []const u8,351 return &wasm.functions.keys()[@intFromEnum(index)];
352 }
238353
239 pub fn eql(ctx: @This(), a: []const u8, b: String) bool {354 pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) ?FunctionIndex {
240 return mem.eql(u8, a, mem.sliceTo(ctx.bytes[@intFromEnum(b)..], 0));355 return fromResolution(wasm, .fromIpNav(wasm, nav_index));
241 }356 }
242357
243 pub fn hash(_: @This(), adapted_key: []const u8) u64 {358 pub fn fromTagNameType(wasm: *const Wasm, tag_type: InternPool.Index) ?FunctionIndex {
244 assert(mem.indexOfScalar(u8, adapted_key, 0) == null);359 const zcu_func: ZcuFunc.Index = @enumFromInt(wasm.zcu_funcs.getIndex(tag_type) orelse return null);
245 return std.hash_map.hashString(adapted_key);360 return fromResolution(wasm, .pack(wasm, .{ .zcu_func = zcu_func }));
361 }
362
363 pub fn fromSymbolName(wasm: *const Wasm, name: String) ?FunctionIndex {
364 if (wasm.object_function_imports.getPtr(name)) |import| {
365 return fromResolution(wasm, import.resolution);
246 }366 }
247 };367 if (wasm.function_exports.get(name)) |index| return index;
368 if (wasm.hidden_function_exports.get(name)) |index| return index;
369 return null;
370 }
248371
249 pub fn toOptional(i: String) OptionalString {372 pub fn fromResolution(wasm: *const Wasm, resolution: FunctionImport.Resolution) ?FunctionIndex {
250 const result: OptionalString = @enumFromInt(@intFromEnum(i));373 const i = wasm.functions.getIndex(resolution) orelse return null;
251 assert(result != .none);374 return @enumFromInt(i);
252 return result;
253 }375 }
254};376};
255377
256pub const OptionalString = enum(u32) {378pub const GlobalExport = extern struct {
257 none = std.math.maxInt(u32),379 name: String,
380 global_index: GlobalIndex,
381};
382
383/// 0. Index into `Flush.function_imports`
384/// 1. Index into `functions`.
385///
386/// Note that function_imports indexes are subject to swap removals during
387/// `flush`.
388pub const OutputFunctionIndex = enum(u32) {
258 _,389 _,
259390
260 pub fn unwrap(i: OptionalString) ?String {391 pub fn fromResolution(wasm: *const Wasm, resolution: FunctionImport.Resolution) ?OutputFunctionIndex {
261 if (i == .none) return null;392 return fromFunctionIndex(wasm, FunctionIndex.fromResolution(wasm, resolution) orelse return null);
262 return @enumFromInt(@intFromEnum(i));393 }
394
395 pub fn fromFunctionIndex(wasm: *const Wasm, index: FunctionIndex) OutputFunctionIndex {
396 return @enumFromInt(wasm.flush_buffer.function_imports.entries.len + @intFromEnum(index));
397 }
398
399 pub fn fromObjectFunction(wasm: *const Wasm, index: ObjectFunctionIndex) OutputFunctionIndex {
400 return fromResolution(wasm, .fromObjectFunction(wasm, index)).?;
401 }
402
403 pub fn fromObjectFunctionHandlingWeak(wasm: *const Wasm, index: ObjectFunctionIndex) OutputFunctionIndex {
404 const ptr = index.ptr(wasm);
405 if (ptr.flags.binding == .weak) {
406 const name = ptr.name.unwrap().?;
407 const import = wasm.object_function_imports.getPtr(name).?;
408 assert(import.resolution != .unresolved);
409 return fromResolution(wasm, import.resolution).?;
410 }
411 return fromResolution(wasm, .fromObjectFunction(wasm, index)).?;
412 }
413
414 pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) OutputFunctionIndex {
415 const zcu = wasm.base.comp.zcu.?;
416 const ip = &zcu.intern_pool;
417 return switch (ip.indexToKey(ip_index)) {
418 .@"extern" => |ext| {
419 const name = wasm.getExistingString(ext.name.toSlice(ip)).?;
420 return fromSymbolName(wasm, name);
421 },
422 else => fromResolution(wasm, .fromIpIndex(wasm, ip_index)).?,
423 };
424 }
425
426 pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) OutputFunctionIndex {
427 const zcu = wasm.base.comp.zcu.?;
428 const ip = &zcu.intern_pool;
429 const nav = ip.getNav(nav_index);
430 return fromIpIndex(wasm, nav.status.fully_resolved.val);
431 }
432
433 pub fn fromTagNameType(wasm: *const Wasm, tag_type: InternPool.Index) OutputFunctionIndex {
434 return fromFunctionIndex(wasm, FunctionIndex.fromTagNameType(wasm, tag_type).?);
435 }
436
437 pub fn fromSymbolName(wasm: *const Wasm, name: String) OutputFunctionIndex {
438 if (wasm.flush_buffer.function_imports.getIndex(name)) |i| return @enumFromInt(i);
439 return fromFunctionIndex(wasm, FunctionIndex.fromSymbolName(wasm, name).?);
263 }440 }
264};441};
265442
266/// Index into objects array or the zig object.443/// Index into `Wasm.globals`.
267pub const ObjectId = enum(u16) {444pub const GlobalIndex = enum(u32) {
268 zig_object = std.math.maxInt(u16) - 1,
269 _,445 _,
270446
271 pub fn toOptional(i: ObjectId) OptionalObjectId {447 /// This is only accurate when not emitting an object and there is a Zcu.
272 const result: OptionalObjectId = @enumFromInt(@intFromEnum(i));448 pub const stack_pointer: GlobalIndex = @enumFromInt(0);
273 assert(result != .none);449
274 return result;450 /// Same as `stack_pointer` but with a safety assertion.
451 pub fn stackPointer(wasm: *const Wasm) ObjectGlobal.Index {
452 const comp = wasm.base.comp;
453 assert(comp.config.output_mode != .Obj);
454 assert(comp.zcu != null);
455 return .stack_pointer;
456 }
457
458 pub fn ptr(index: GlobalIndex, f: *const Flush) *Wasm.GlobalImport.Resolution {
459 return &f.globals.items[@intFromEnum(index)];
460 }
461
462 pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) ?GlobalIndex {
463 const i = wasm.globals.getIndex(.fromIpNav(wasm, nav_index)) orelse return null;
464 return @enumFromInt(i);
465 }
466
467 pub fn fromObjectGlobal(wasm: *const Wasm, i: ObjectGlobalIndex) GlobalIndex {
468 return @enumFromInt(wasm.globals.getIndex(.fromObjectGlobal(wasm, i)).?);
469 }
470
471 pub fn fromObjectGlobalHandlingWeak(wasm: *const Wasm, index: ObjectGlobalIndex) GlobalIndex {
472 const global = index.ptr(wasm);
473 return if (global.flags.binding == .weak)
474 fromSymbolName(wasm, global.name.unwrap().?)
475 else
476 fromObjectGlobal(wasm, index);
477 }
478
479 pub fn fromSymbolName(wasm: *const Wasm, name: String) GlobalIndex {
480 const import = wasm.object_global_imports.getPtr(name).?;
481 return @enumFromInt(wasm.globals.getIndex(import.resolution).?);
275 }482 }
276};483};
277484
278/// Optional index into objects array or the zig object.485/// Index into `tables`.
279pub const OptionalObjectId = enum(u16) {486pub const TableIndex = enum(u32) {
280 zig_object = std.math.maxInt(u16) - 1,
281 none = std.math.maxInt(u16),
282 _,487 _,
283488
284 pub fn unwrap(i: OptionalObjectId) ?ObjectId {489 pub fn ptr(index: TableIndex, f: *const Flush) *Wasm.TableImport.Resolution {
285 if (i == .none) return null;490 return &f.tables.items[@intFromEnum(index)];
286 return @enumFromInt(@intFromEnum(i));
287 }491 }
288};
289492
290/// None of this data is serialized since it can be re-loaded from disk, or if493 pub fn fromObjectTable(wasm: *const Wasm, i: ObjectTableIndex) TableIndex {
291/// it has been changed, the data must be discarded.494 return @enumFromInt(wasm.tables.getIndex(.fromObjectTable(i)).?);
292const LazyArchive = struct {495 }
293 path: Path,
294 file_contents: []const u8,
295 archive: Archive,
296496
297 fn deinit(la: *LazyArchive, gpa: Allocator) void {497 pub fn fromSymbolName(wasm: *const Wasm, name: String) TableIndex {
298 la.archive.deinit(gpa);498 const import = wasm.object_table_imports.getPtr(name).?;
299 gpa.free(la.path.sub_path);499 return @enumFromInt(wasm.tables.getIndex(import.resolution).?);
300 gpa.free(la.file_contents);
301 la.* = undefined;
302 }500 }
303};501};
304502
305pub const Segment = struct {503/// The first N indexes correspond to input objects (`objects`) array.
306 alignment: Alignment,504/// After that, the indexes correspond to the `source_locations` array,
307 size: u32,505/// representing a location in a Zig source file that can be pinpointed
308 offset: u32,506/// precisely via AST node and token.
309 flags: u32,507pub const SourceLocation = enum(u32) {
508 /// From the Zig compilation unit but no precise source location.
509 zig_object_nofile = std.math.maxInt(u32) - 1,
510 none = std.math.maxInt(u32),
511 _,
310512
311 const Index = enum(u32) {513 /// Index into `source_locations`.
514 pub const Index = enum(u32) {
312 _,515 _,
313
314 pub fn toOptional(i: Index) OptionalIndex {
315 const result: OptionalIndex = @enumFromInt(@intFromEnum(i));
316 assert(result != .none);
317 return result;
318 }
319 };516 };
320517
321 const OptionalIndex = enum(u32) {518 pub const Unpacked = union(enum) {
322 none = std.math.maxInt(u32),519 none,
323 _,520 zig_object_nofile,
324521 object_index: ObjectIndex,
325 pub fn unwrap(i: OptionalIndex) ?Index {522 source_location_index: Index,
326 if (i == .none) return null;
327 return @enumFromInt(@intFromEnum(i));
328 }
329 };523 };
330524
331 pub const Flag = enum(u32) {525 pub fn pack(unpacked: Unpacked, wasm: *const Wasm) SourceLocation {
332 WASM_DATA_SEGMENT_IS_PASSIVE = 0x01,526 _ = wasm;
333 WASM_DATA_SEGMENT_HAS_MEMINDEX = 0x02,527 return switch (unpacked) {
334 };528 .zig_object_nofile => .zig_object_nofile,
529 .none => .none,
530 .object_index => |object_index| @enumFromInt(@intFromEnum(object_index)),
531 .source_location_index => @panic("TODO"),
532 };
533 }
534
535 pub fn unpack(sl: SourceLocation, wasm: *const Wasm) Unpacked {
536 return switch (sl) {
537 .zig_object_nofile => .zig_object_nofile,
538 .none => .none,
539 _ => {
540 const i = @intFromEnum(sl);
541 if (i < wasm.objects.items.len) return .{ .object_index = @enumFromInt(i) };
542 const sl_index = i - wasm.objects.items.len;
543 _ = sl_index;
544 @panic("TODO");
545 },
546 };
547 }
335548
336 pub fn isPassive(segment: Segment) bool {549 pub fn fromObject(object_index: ObjectIndex, wasm: *const Wasm) SourceLocation {
337 return segment.flags & @intFromEnum(Flag.WASM_DATA_SEGMENT_IS_PASSIVE) != 0;550 return pack(.{ .object_index = object_index }, wasm);
338 }551 }
339552
340 /// For a given segment, determines if it needs passive initialization553 pub fn addError(sl: SourceLocation, wasm: *Wasm, comptime f: []const u8, args: anytype) void {
341 fn needsPassiveInitialization(segment: Segment, import_mem: bool, name: []const u8) bool {554 const diags = &wasm.base.comp.link_diags;
342 if (import_mem and !std.mem.eql(u8, name, ".bss")) {555 switch (sl.unpack(wasm)) {
343 return true;556 .none => unreachable,
557 .zig_object_nofile => diags.addError("zig compilation unit: " ++ f, args),
558 .object_index => |i| diags.addError("{}: " ++ f, .{i.ptr(wasm).path} ++ args),
559 .source_location_index => @panic("TODO"),
344 }560 }
345 return segment.isPassive();
346 }561 }
347};
348562
349pub const SymbolLoc = struct {563 pub fn addNote(
350 /// The index of the symbol within the specified file564 sl: SourceLocation,
351 index: Symbol.Index,565 err: *link.Diags.ErrorWithNotes,
352 /// The index of the object file where the symbol resides.566 comptime f: []const u8,
353 file: OptionalObjectId,567 args: anytype,
354};568 ) void {
569 err.addNote(f, args);
570 const err_msg = &err.diags.msgs.items[err.index];
571 err_msg.notes[err.note_slot - 1].source_location = .{ .wasm = sl };
572 }
355573
356/// From a given location, returns the corresponding symbol in the wasm binary574 pub fn fail(sl: SourceLocation, diags: *link.Diags, comptime format: []const u8, args: anytype) error{LinkFailure} {
357pub fn symbolLocSymbol(wasm: *const Wasm, loc: SymbolLoc) *Symbol {575 return diags.failSourceLocation(.{ .wasm = sl }, format, args);
358 if (wasm.discarded.get(loc)) |new_loc| {
359 return symbolLocSymbol(wasm, new_loc);
360 }576 }
361 return switch (loc.file) {577
362 .none => &wasm.synthetic_symbols.items[@intFromEnum(loc.index)],578 pub fn string(
363 .zig_object => wasm.zig_object.?.symbol(loc.index),579 sl: SourceLocation,
364 _ => &wasm.objects.items[@intFromEnum(loc.file)].symtable[@intFromEnum(loc.index)],580 msg: []const u8,
581 bundle: *std.zig.ErrorBundle.Wip,
582 wasm: *const Wasm,
583 ) Allocator.Error!std.zig.ErrorBundle.String {
584 return switch (sl.unpack(wasm)) {
585 .none => try bundle.addString(msg),
586 .zig_object_nofile => try bundle.printString("zig compilation unit: {s}", .{msg}),
587 .object_index => |i| {
588 const obj = i.ptr(wasm);
589 return if (obj.archive_member_name.slice(wasm)) |obj_name|
590 try bundle.printString("{} ({s}): {s}", .{ obj.path, std.fs.path.basename(obj_name), msg })
591 else
592 try bundle.printString("{}: {s}", .{ obj.path, msg });
593 },
594 .source_location_index => @panic("TODO"),
595 };
596 }
597};
598
599/// The lower bits of this ABI-match the flags here:
600/// https://github.com/WebAssembly/tool-conventions/blob/df8d737539eb8a8f446ba5eab9dc670c40dfb81e/Linking.md#symbol-table-subsection
601/// The upper bits are used for nefarious purposes.
602pub const SymbolFlags = packed struct(u32) {
603 binding: Binding = .strong,
604 /// Indicating that this is a hidden symbol. Hidden symbols are not to be
605 /// exported when performing the final link, but may be linked to other
606 /// modules.
607 visibility_hidden: bool = false,
608 padding0: u1 = 0,
609 /// For non-data symbols, this must match whether the symbol is an import
610 /// or is defined; for data symbols, determines whether a segment is
611 /// specified.
612 undefined: bool = false,
613 /// The symbol is intended to be exported from the wasm module to the host
614 /// environment. This differs from the visibility flags in that it affects
615 /// static linking.
616 exported: bool = false,
617 /// The symbol uses an explicit symbol name, rather than reusing the name
618 /// from a wasm import. This allows it to remap imports from foreign
619 /// WebAssembly modules into local symbols with different names.
620 explicit_name: bool = false,
621 /// The symbol is intended to be included in the linker output, regardless
622 /// of whether it is used by the program. Same meaning as `retain`.
623 no_strip: bool = false,
624 /// The symbol resides in thread local storage.
625 tls: bool = false,
626 /// The symbol represents an absolute address. This means its offset is
627 /// relative to the start of the wasm memory as opposed to being relative
628 /// to a data segment.
629 absolute: bool = false,
630
631 // Above here matches the tooling conventions ABI.
632
633 padding1: u13 = 0,
634 /// Zig-specific. Dead things are allowed to be garbage collected.
635 alive: bool = false,
636 /// Zig-specific. This symbol comes from an object that must be included in
637 /// the final link.
638 must_link: bool = false,
639 /// Zig-specific.
640 global_type: GlobalType4 = .zero,
641 /// Zig-specific.
642 limits_has_max: bool = false,
643 /// Zig-specific.
644 limits_is_shared: bool = false,
645 /// Zig-specific.
646 ref_type: RefType1 = .funcref,
647
648 pub const Binding = enum(u2) {
649 strong = 0,
650 /// Indicating that this is a weak symbol. When linking multiple modules
651 /// defining the same symbol, all weak definitions are discarded if any
652 /// strong definitions exist; then if multiple weak definitions exist all
653 /// but one (unspecified) are discarded; and finally it is an error if more
654 /// than one definition remains.
655 weak = 1,
656 /// Indicating that this is a local symbol. Local symbols are not to be
657 /// exported, or linked to other modules/sections. The names of all
658 /// non-local symbols must be unique, but the names of local symbols
659 /// are not considered for uniqueness. A local function or global
660 /// symbol cannot reference an import.
661 local = 2,
365 };662 };
366}
367663
368/// From a given location, returns the name of the symbol.664 pub fn initZigSpecific(flags: *SymbolFlags, must_link: bool, no_strip: bool) void {
369pub fn symbolLocName(wasm: *const Wasm, loc: SymbolLoc) [:0]const u8 {665 flags.no_strip = no_strip;
370 return wasm.stringSlice(wasm.symbolLocSymbol(loc).name);666 flags.alive = false;
371}667 flags.must_link = must_link;
668 flags.global_type = .zero;
669 flags.limits_has_max = false;
670 flags.limits_is_shared = false;
671 flags.ref_type = .funcref;
672 }
372673
373/// From a given symbol location, returns the final location.674 pub fn isIncluded(flags: SymbolFlags, is_dynamic: bool) bool {
374/// e.g. when a symbol was resolved and replaced by the symbol675 return flags.exported or
375/// in a different file, this will return said location.676 (is_dynamic and !flags.visibility_hidden) or
376/// If the symbol wasn't replaced by another, this will return677 (flags.no_strip and flags.must_link);
377/// the given location itwasm.
378pub fn symbolLocFinalLoc(wasm: *const Wasm, loc: SymbolLoc) SymbolLoc {
379 if (wasm.discarded.get(loc)) |new_loc| {
380 return symbolLocFinalLoc(wasm, new_loc);
381 }678 }
382 return loc;
383}
384679
385// Contains the location of the function symbol, as well as680 pub fn isExported(flags: SymbolFlags, is_dynamic: bool) bool {
386/// the priority itself of the initialization function.681 if (flags.undefined or flags.binding == .local) return false;
387pub const InitFuncLoc = struct {682 if (is_dynamic and !flags.visibility_hidden) return true;
388 /// object file index in the list of objects.683 return flags.exported;
389 /// Unlike `SymbolLoc` this cannot be `null` as we never define684 }
390 /// our own ctors.
391 file: ObjectId,
392 /// Symbol index within the corresponding object file.
393 index: Symbol.Index,
394 /// The priority in which the constructor must be called.
395 priority: u32,
396685
397 /// From a given `InitFuncLoc` returns the corresponding function symbol686 /// Returns the name as how it will be output into the final object
398 fn getSymbol(loc: InitFuncLoc, wasm: *const Wasm) *Symbol {687 /// file or binary. When `merge` is true, this will return the
399 return wasm.symbolLocSymbol(getSymbolLoc(loc));688 /// short name. i.e. ".rodata". When false, it returns the entire name instead.
689 pub fn outputName(flags: SymbolFlags, name: []const u8, merge: bool) []const u8 {
690 if (flags.tls) return ".tdata";
691 if (!merge) return name;
692 if (mem.startsWith(u8, name, ".rodata.")) return ".rodata";
693 if (mem.startsWith(u8, name, ".text.")) return ".text";
694 if (mem.startsWith(u8, name, ".data.")) return ".data";
695 if (mem.startsWith(u8, name, ".bss.")) return ".bss";
696 return name;
697 }
698
699 /// Masks off the Zig-specific stuff.
700 pub fn toAbiInteger(flags: SymbolFlags) u32 {
701 var copy = flags;
702 copy.initZigSpecific(false, false);
703 return @bitCast(copy);
400 }704 }
705};
401706
402 /// Turns the given `InitFuncLoc` into a `SymbolLoc`707pub const GlobalType4 = packed struct(u4) {
403 fn getSymbolLoc(loc: InitFuncLoc) SymbolLoc {708 valtype: Valtype3,
709 mutable: bool,
710
711 pub const zero: GlobalType4 = @bitCast(@as(u4, 0));
712
713 pub fn to(gt: GlobalType4) ObjectGlobal.Type {
404 return .{714 return .{
405 .file = loc.file.toOptional(),715 .valtype = gt.valtype.to(),
406 .index = loc.index,716 .mutable = gt.mutable,
407 };717 };
408 }718 }
719};
409720
410 /// Returns true when `lhs` has a higher priority (e.i. value closer to 0) than `rhs`.721pub const Valtype3 = enum(u3) {
411 fn lessThan(ctx: void, lhs: InitFuncLoc, rhs: InitFuncLoc) bool {722 i32,
412 _ = ctx;723 i64,
413 return lhs.priority < rhs.priority;724 f32,
725 f64,
726 v128,
727
728 pub fn from(v: std.wasm.Valtype) Valtype3 {
729 return switch (v) {
730 .i32 => .i32,
731 .i64 => .i64,
732 .f32 => .f32,
733 .f64 => .f64,
734 .v128 => .v128,
735 };
736 }
737
738 pub fn to(v: Valtype3) std.wasm.Valtype {
739 return switch (v) {
740 .i32 => .i32,
741 .i64 => .i64,
742 .f32 => .f32,
743 .f64 => .f64,
744 .v128 => .v128,
745 };
414 }746 }
415};747};
416748
417pub fn open(749/// Index into `Wasm.navs_obj`.
418 arena: Allocator,750pub const NavsObjIndex = enum(u32) {
419 comp: *Compilation,751 _,
420 emit: Path,
421 options: link.File.OpenOptions,
422) !*Wasm {
423 // TODO: restore saved linker state, don't truncate the file, and
424 // participate in incremental compilation.
425 return createEmpty(arena, comp, emit, options);
426}
427752
428pub fn createEmpty(753 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index {
429 arena: Allocator,754 return &wasm.navs_obj.keys()[@intFromEnum(i)];
430 comp: *Compilation,755 }
431 emit: Path,
432 options: link.File.OpenOptions,
433) !*Wasm {
434 const gpa = comp.gpa;
435 const target = comp.root_mod.resolved_target.result;
436 assert(target.ofmt == .wasm);
437756
438 const use_lld = build_options.have_llvm and comp.config.use_lld;757 pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataObj {
439 const use_llvm = comp.config.use_llvm;758 return &wasm.navs_obj.values()[@intFromEnum(i)];
440 const output_mode = comp.config.output_mode;759 }
441 const shared_memory = comp.config.shared_memory;
442 const wasi_exec_model = comp.config.wasi_exec_model;
443760
444 // If using LLD to link, this code should produce an object file so that it761 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {
445 // can be passed to LLD.762 const zcu = wasm.base.comp.zcu.?;
446 // If using LLVM to generate the object file for the zig compilation unit,763 const ip = &zcu.intern_pool;
447 // we need a place to put the object file so that it can be subsequently764 const nav = ip.getNav(i.key(wasm).*);
448 // handled.765 return nav.fqn.toSlice(ip);
449 const zcu_object_sub_path = if (!use_lld and !use_llvm)766 }
450 null767};
451 else
452 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
453768
454 const wasm = try arena.create(Wasm);769/// Index into `Wasm.navs_exe`.
455 wasm.* = .{770pub const NavsExeIndex = enum(u32) {
456 .base = .{771 _,
457 .tag = .wasm,
458 .comp = comp,
459 .emit = emit,
460 .zcu_object_sub_path = zcu_object_sub_path,
461 .gc_sections = options.gc_sections orelse (output_mode != .Obj),
462 .print_gc_sections = options.print_gc_sections,
463 .stack_size = options.stack_size orelse switch (target.os.tag) {
464 .freestanding => 1 * 1024 * 1024, // 1 MiB
465 else => 16 * 1024 * 1024, // 16 MiB
466 },
467 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
468 .file = null,
469 .disable_lld_caching = options.disable_lld_caching,
470 .build_id = options.build_id,
471 },
472 .name = undefined,
473 .string_table = .empty,
474 .string_bytes = .empty,
475 .import_table = options.import_table,
476 .export_table = options.export_table,
477 .import_symbols = options.import_symbols,
478 .export_symbol_names = options.export_symbol_names,
479 .global_base = options.global_base,
480 .initial_memory = options.initial_memory,
481 .max_memory = options.max_memory,
482772
483 .entry_name = undefined,773 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index {
484 .zig_object = null,774 return &wasm.navs_exe.keys()[@intFromEnum(i)];
485 .dump_argv_list = .empty,
486 .host_name = undefined,
487 .custom_sections = undefined,
488 .preloaded_strings = undefined,
489 };
490 if (use_llvm and comp.config.have_zcu) {
491 wasm.llvm_object = try LlvmObject.create(arena, comp);
492 }775 }
493 errdefer wasm.base.destroy();
494776
495 wasm.host_name = try wasm.internString("env");777 pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataExe {
496778 return &wasm.navs_exe.values()[@intFromEnum(i)];
497 inline for (@typeInfo(CustomSections).@"struct".fields) |field| {
498 @field(wasm.custom_sections, field.name) = .{
499 .index = .none,
500 .name = try wasm.internString(field.name),
501 };
502 }779 }
503780
504 inline for (@typeInfo(PreloadedStrings).@"struct".fields) |field| {781 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {
505 @field(wasm.preloaded_strings, field.name) = try wasm.internString(field.name);782 const zcu = wasm.base.comp.zcu.?;
783 const ip = &zcu.intern_pool;
784 const nav = ip.getNav(i.key(wasm).*);
785 return nav.fqn.toSlice(ip);
506 }786 }
787};
507788
508 wasm.entry_name = switch (options.entry) {789/// Index into `Wasm.uavs_obj`.
509 .disabled => .none,790pub const UavsObjIndex = enum(u32) {
510 .default => if (output_mode != .Exe) .none else defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(),791 _,
511 .enabled => defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(),
512 .named => |name| (try wasm.internString(name)).toOptional(),
513 };
514792
515 if (use_lld and (use_llvm or !comp.config.have_zcu)) {793 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index {
516 // LLVM emits the object file (if any); LLD links it into the final product.794 return &wasm.uavs_obj.keys()[@intFromEnum(i)];
517 return wasm;
518 }795 }
519796
520 // What path should this Wasm linker code output to?797 pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataObj {
521 // If using LLD to link, this code should produce an object file so that it798 return &wasm.uavs_obj.values()[@intFromEnum(i)];
522 // can be passed to LLD.799 }
523 const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path;800};
524801
525 wasm.base.file = try emit.root_dir.handle.createFile(sub_path, .{802/// Index into `Wasm.uavs_exe`.
526 .truncate = true,803pub const UavsExeIndex = enum(u32) {
527 .read = true,804 _,
528 .mode = if (fs.has_executable_bit)
529 if (target.os.tag == .wasi and output_mode == .Exe)
530 fs.File.default_mode | 0b001_000_000
531 else
532 fs.File.default_mode
533 else
534 0,
535 });
536 wasm.name = sub_path;
537805
538 // create stack pointer symbol806 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index {
539 {807 return &wasm.uavs_exe.keys()[@intFromEnum(i)];
540 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__stack_pointer, .global);808 }
541 const symbol = wasm.symbolLocSymbol(loc);809
542 // For object files we will import the stack pointer symbol810 pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataExe {
543 if (output_mode == .Obj) {811 return &wasm.uavs_exe.values()[@intFromEnum(i)];
544 symbol.setUndefined(true);812 }
545 symbol.index = @intCast(wasm.imported_globals_count);813};
546 wasm.imported_globals_count += 1;814
547 try wasm.imports.putNoClobber(gpa, loc, .{815/// Used when emitting a relocatable object.
548 .module_name = wasm.host_name,816pub const ZcuDataObj = extern struct {
549 .name = symbol.name,817 code: DataPayload,
550 .kind = .{ .global = .{ .valtype = .i32, .mutable = true } },818 relocs: OutReloc.Slice,
551 });819};
552 } else {820
553 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);821/// Used when not emitting a relocatable object.
554 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);822pub const ZcuDataExe = extern struct {
555 const global = try wasm.wasm_globals.addOne(gpa);823 code: DataPayload,
556 global.* = .{824 /// Tracks how many references there are for the purposes of sorting data segments.
557 .global_type = .{825 count: u32,
558 .valtype = .i32,826};
559 .mutable = true,827
560 },828/// An abstraction for calling `lowerZcuData` repeatedly until all data entries
561 .init = .{ .i32_const = 0 },829/// are populated.
562 };830const ZcuDataStarts = struct {
563 }831 uavs_i: u32,
832
833 fn init(wasm: *const Wasm) ZcuDataStarts {
834 const comp = wasm.base.comp;
835 const is_obj = comp.config.output_mode == .Obj;
836 return if (is_obj) initObj(wasm) else initExe(wasm);
564 }837 }
565838
566 // create indirect function pointer symbol839 fn initObj(wasm: *const Wasm) ZcuDataStarts {
567 {840 return .{
568 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__indirect_function_table, .table);841 .uavs_i = @intCast(wasm.uavs_obj.entries.len),
569 const symbol = wasm.symbolLocSymbol(loc);
570 const table: std.wasm.Table = .{
571 .limits = .{ .flags = 0, .min = 0, .max = undefined }, // will be overwritten during `mapFunctionTable`
572 .reftype = .funcref,
573 };842 };
574 if (output_mode == .Obj or options.import_table) {
575 symbol.setUndefined(true);
576 symbol.index = @intCast(wasm.imported_tables_count);
577 wasm.imported_tables_count += 1;
578 try wasm.imports.put(gpa, loc, .{
579 .module_name = wasm.host_name,
580 .name = symbol.name,
581 .kind = .{ .table = table },
582 });
583 } else {
584 symbol.index = @as(u32, @intCast(wasm.imported_tables_count + wasm.tables.items.len));
585 try wasm.tables.append(gpa, table);
586 if (wasm.export_table) {
587 symbol.setFlag(.WASM_SYM_EXPORTED);
588 } else {
589 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
590 }
591 }
592 }843 }
593844
594 // create __wasm_call_ctors845 fn initExe(wasm: *const Wasm) ZcuDataStarts {
595 {846 return .{
596 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_call_ctors, .function);847 .uavs_i = @intCast(wasm.uavs_exe.entries.len),
597 const symbol = wasm.symbolLocSymbol(loc);848 };
598 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
599 // we do not know the function index until after we merged all sections.
600 // Therefore we set `symbol.index` and create its corresponding references
601 // at the end during `initializeCallCtorsFunction`.
602 }849 }
603850
604 // shared-memory symbols for TLS support851 fn finish(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void {
605 if (shared_memory) {852 const comp = wasm.base.comp;
606 {853 const is_obj = comp.config.output_mode == .Obj;
607 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__tls_base, .global);854 return if (is_obj) finishObj(zds, wasm, pt) else finishExe(zds, wasm, pt);
608 const symbol = wasm.symbolLocSymbol(loc);855 }
609 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);856
610 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);857 fn finishObj(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void {
611 symbol.mark();858 var uavs_i = zds.uavs_i;
612 try wasm.wasm_globals.append(gpa, .{859 while (uavs_i < wasm.uavs_obj.entries.len) : (uavs_i += 1) {
613 .global_type = .{ .valtype = .i32, .mutable = true },860 // Call to `lowerZcuData` here possibly creates more entries in these tables.
614 .init = .{ .i32_const = undefined },861 wasm.uavs_obj.values()[uavs_i] = try lowerZcuData(wasm, pt, wasm.uavs_obj.keys()[uavs_i]);
615 });
616 }
617 {
618 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__tls_size, .global);
619 const symbol = wasm.symbolLocSymbol(loc);
620 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
621 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
622 symbol.mark();
623 try wasm.wasm_globals.append(gpa, .{
624 .global_type = .{ .valtype = .i32, .mutable = false },
625 .init = .{ .i32_const = undefined },
626 });
627 }
628 {
629 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__tls_align, .global);
630 const symbol = wasm.symbolLocSymbol(loc);
631 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
632 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
633 symbol.mark();
634 try wasm.wasm_globals.append(gpa, .{
635 .global_type = .{ .valtype = .i32, .mutable = false },
636 .init = .{ .i32_const = undefined },
637 });
638 }
639 {
640 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_tls, .function);
641 const symbol = wasm.symbolLocSymbol(loc);
642 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
643 }862 }
644 }863 }
645864
646 if (comp.zcu) |zcu| {865 fn finishExe(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void {
647 if (!use_llvm) {866 var uavs_i = zds.uavs_i;
648 const zig_object = try arena.create(ZigObject);867 while (uavs_i < wasm.uavs_exe.entries.len) : (uavs_i += 1) {
649 wasm.zig_object = zig_object;868 // Call to `lowerZcuData` here possibly creates more entries in these tables.
650 zig_object.* = .{869 const zcu_data = try lowerZcuData(wasm, pt, wasm.uavs_exe.keys()[uavs_i]);
651 .path = .{870 wasm.uavs_exe.values()[uavs_i].code = zcu_data.code;
652 .root_dir = std.Build.Cache.Directory.cwd(),
653 .sub_path = try std.fmt.allocPrint(gpa, "{s}.o", .{fs.path.stem(zcu.main_mod.root_src_path)}),
654 },
655 .stack_pointer_sym = .null,
656 };
657 try zig_object.init(wasm);
658 }871 }
659 }872 }
873};
660874
661 return wasm;875pub const ZcuFunc = union {
662}876 function: CodeGen.Function,
877 tag_name: TagName,
663878
664pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 {879 pub const TagName = extern struct {
665 var index: u32 = 0;880 symbol_name: String,
666 while (index < wasm.func_types.items.len) : (index += 1) {881 type_index: FunctionType.Index,
667 if (wasm.func_types.items[index].eql(func_type)) return index;882 /// Index into `Wasm.tag_name_offs`.
668 }883 table_index: u32,
669 return null;884 };
670}
671885
672/// Either creates a new import, or updates one if existing.886 /// Index into `Wasm.zcu_funcs`.
673/// When `type_index` is non-null, we assume an external function.887 /// Note that swapRemove is sometimes performed on `zcu_funcs`.
674/// In all other cases, a data-symbol will be created instead.888 pub const Index = enum(u32) {
675pub fn addOrUpdateImport(889 _,
676 wasm: *Wasm,
677 /// Name of the import
678 name: []const u8,
679 /// Symbol index that is external
680 symbol_index: Symbol.Index,
681 /// Optional library name (i.e. `extern "c" fn foo() void`
682 lib_name: ?[:0]const u8,
683 /// The index of the type that represents the function signature
684 /// when the extern is a function. When this is null, a data-symbol
685 /// is asserted instead.
686 type_index: ?u32,
687) !void {
688 return wasm.zig_object.?.addOrUpdateImport(wasm, name, symbol_index, lib_name, type_index);
689}
690890
691/// For a given name, creates a new global synthetic symbol.891 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index {
692/// Leaves index undefined and the default flags (0).892 return &wasm.zcu_funcs.keys()[@intFromEnum(i)];
693fn createSyntheticSymbol(wasm: *Wasm, name: String, tag: Symbol.Tag) !SymbolLoc {893 }
694 return wasm.createSyntheticSymbolOffset(name, tag);
695}
696894
697fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: String, tag: Symbol.Tag) !SymbolLoc {895 pub fn value(i: @This(), wasm: *const Wasm) *ZcuFunc {
698 const sym_index: Symbol.Index = @enumFromInt(wasm.synthetic_symbols.items.len);896 return &wasm.zcu_funcs.values()[@intFromEnum(i)];
699 const loc: SymbolLoc = .{ .index = sym_index, .file = .none };897 }
700 const gpa = wasm.base.comp.gpa;
701 try wasm.synthetic_symbols.append(gpa, .{
702 .name = name_offset,
703 .flags = 0,
704 .tag = tag,
705 .index = undefined,
706 .virtual_address = undefined,
707 });
708 try wasm.resolved_symbols.putNoClobber(gpa, loc, {});
709 try wasm.globals.put(gpa, name_offset, loc);
710 return loc;
711}
712898
713fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void {899 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {
714 const diags = &wasm.base.comp.link_diags;900 const zcu = wasm.base.comp.zcu.?;
715 const obj = link.openObject(path, false, false) catch |err| {901 const ip = &zcu.intern_pool;
716 switch (diags.failParse(path, "failed to open object: {s}", .{@errorName(err)})) {902 const ip_index = i.key(wasm).*;
717 error.LinkFailure => return,903 switch (ip.indexToKey(ip_index)) {
904 .func => |func| {
905 const nav = ip.getNav(func.owner_nav);
906 return nav.fqn.toSlice(ip);
907 },
908 .enum_type => {
909 return i.value(wasm).tag_name.symbol_name.slice(wasm);
910 },
911 else => unreachable,
912 }
718 }913 }
719 };914
720 wasm.parseObject(obj) catch |err| {915 pub fn typeIndex(i: @This(), wasm: *Wasm) FunctionType.Index {
721 switch (diags.failParse(path, "failed to parse object: {s}", .{@errorName(err)})) {916 const comp = wasm.base.comp;
722 error.LinkFailure => return,917 const zcu = comp.zcu.?;
918 const target = &comp.root_mod.resolved_target.result;
919 const ip = &zcu.intern_pool;
920 switch (ip.indexToKey(i.key(wasm).*)) {
921 .func => |func| {
922 const fn_ty = zcu.navValue(func.owner_nav).typeOf(zcu);
923 const fn_info = zcu.typeToFunc(fn_ty).?;
924 return wasm.getExistingFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?;
925 },
926 .enum_type => {
927 return i.value(wasm).tag_name.type_index;
928 },
929 else => unreachable,
930 }
723 }931 }
724 };932 };
725}933};
726
727fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {
728 defer obj.file.close();
729 const gpa = wasm.base.comp.gpa;
730 try wasm.objects.ensureUnusedCapacity(gpa, 1);
731 const stat = try obj.file.stat();
732 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
733934
734 const file_contents = try gpa.alloc(u8, size);935pub const NavExport = extern struct {
735 defer gpa.free(file_contents);936 name: String,
937 nav_index: InternPool.Nav.Index,
938};
736939
737 const n = try obj.file.preadAll(file_contents, 0);940pub const UavExport = extern struct {
738 if (n != file_contents.len) return error.UnexpectedEndOfFile;941 name: String,
942 uav_index: InternPool.Index,
943};
739944
740 wasm.objects.appendAssumeCapacity(try Object.create(wasm, file_contents, obj.path, null));945pub const FunctionImport = extern struct {
741}946 flags: SymbolFlags,
947 module_name: OptionalString,
948 /// May be different than the key which is a symbol name.
949 name: String,
950 source_location: SourceLocation,
951 resolution: Resolution,
952 type: FunctionType.Index,
953
954 /// Represents a synthetic function, a function from an object, or a
955 /// function from the Zcu.
956 pub const Resolution = enum(u32) {
957 unresolved,
958 __wasm_apply_global_tls_relocs,
959 __wasm_call_ctors,
960 __wasm_init_memory,
961 __wasm_init_tls,
962 // Next, index into `object_functions`.
963 // Next, index into `zcu_funcs`.
964 _,
742965
743/// Creates a new empty `Atom` and returns its `Atom.Index`966 const first_object_function = @intFromEnum(Resolution.__wasm_init_tls) + 1;
744pub fn createAtom(wasm: *Wasm, sym_index: Symbol.Index, object_index: OptionalObjectId) !Atom.Index {
745 const gpa = wasm.base.comp.gpa;
746 const index: Atom.Index = @enumFromInt(wasm.managed_atoms.items.len);
747 const atom = try wasm.managed_atoms.addOne(gpa);
748 atom.* = .{
749 .file = object_index,
750 .sym_index = sym_index,
751 };
752 try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), index);
753967
754 return index;968 pub const Unpacked = union(enum) {
755}969 unresolved,
970 __wasm_apply_global_tls_relocs,
971 __wasm_call_ctors,
972 __wasm_init_memory,
973 __wasm_init_tls,
974 object_function: ObjectFunctionIndex,
975 zcu_func: ZcuFunc.Index,
976 };
756977
757pub fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom {978 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
758 return wasm.managed_atoms.items[@intFromEnum(index)];979 return switch (r) {
759}980 .unresolved => .unresolved,
981 .__wasm_apply_global_tls_relocs => .__wasm_apply_global_tls_relocs,
982 .__wasm_call_ctors => .__wasm_call_ctors,
983 .__wasm_init_memory => .__wasm_init_memory,
984 .__wasm_init_tls => .__wasm_init_tls,
985 _ => {
986 const object_function_index = @intFromEnum(r) - first_object_function;
760987
761pub fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {988 const zcu_func_index = if (object_function_index < wasm.object_functions.items.len)
762 return &wasm.managed_atoms.items[@intFromEnum(index)];989 return .{ .object_function = @enumFromInt(object_function_index) }
763}990 else
991 object_function_index - wasm.object_functions.items.len;
764992
765fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {993 return .{ .zcu_func = @enumFromInt(zcu_func_index) };
766 const gpa = wasm.base.comp.gpa;994 },
995 };
996 }
767997
768 defer obj.file.close();998 pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution {
999 return switch (unpacked) {
1000 .unresolved => .unresolved,
1001 .__wasm_apply_global_tls_relocs => .__wasm_apply_global_tls_relocs,
1002 .__wasm_call_ctors => .__wasm_call_ctors,
1003 .__wasm_init_memory => .__wasm_init_memory,
1004 .__wasm_init_tls => .__wasm_init_tls,
1005 .object_function => |i| @enumFromInt(first_object_function + @intFromEnum(i)),
1006 .zcu_func => |i| @enumFromInt(first_object_function + wasm.object_functions.items.len + @intFromEnum(i)),
1007 };
1008 }
7691009
770 const stat = try obj.file.stat();1010 pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) Resolution {
771 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;1011 const zcu = wasm.base.comp.zcu.?;
1012 const ip = &zcu.intern_pool;
1013 return fromIpIndex(wasm, ip.getNav(nav_index).status.fully_resolved.val);
1014 }
7721015
773 const file_contents = try gpa.alloc(u8, size);1016 pub fn fromZcuFunc(wasm: *const Wasm, i: ZcuFunc.Index) Resolution {
774 var keep_file_contents = false;1017 return pack(wasm, .{ .zcu_func = i });
775 defer if (!keep_file_contents) gpa.free(file_contents);1018 }
7761019
777 const n = try obj.file.preadAll(file_contents, 0);1020 pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution {
778 if (n != file_contents.len) return error.UnexpectedEndOfFile;1021 return fromZcuFunc(wasm, @enumFromInt(wasm.zcu_funcs.getIndex(ip_index).?));
1022 }
7791023
780 var archive = try Archive.parse(gpa, file_contents);1024 pub fn fromObjectFunction(wasm: *const Wasm, object_function: ObjectFunctionIndex) Resolution {
1025 return pack(wasm, .{ .object_function = object_function });
1026 }
7811027
782 if (!obj.must_link) {1028 pub fn isNavOrUnresolved(r: Resolution, wasm: *const Wasm) bool {
783 errdefer archive.deinit(gpa);1029 return switch (r.unpack(wasm)) {
784 try wasm.lazy_archives.append(gpa, .{1030 .unresolved, .zcu_func => true,
785 .path = .{1031 else => false,
786 .root_dir = obj.path.root_dir,1032 };
787 .sub_path = try gpa.dupe(u8, obj.path.sub_path),1033 }
788 },
789 .file_contents = file_contents,
790 .archive = archive,
791 });
792 keep_file_contents = true;
793 return;
794 }
7951034
796 defer archive.deinit(gpa);1035 pub fn typeIndex(r: Resolution, wasm: *Wasm) FunctionType.Index {
1036 return switch (unpack(r, wasm)) {
1037 .unresolved => unreachable,
1038 .__wasm_apply_global_tls_relocs,
1039 .__wasm_call_ctors,
1040 .__wasm_init_memory,
1041 => getExistingFuncType2(wasm, &.{}, &.{}),
1042 .__wasm_init_tls => getExistingFuncType2(wasm, &.{.i32}, &.{}),
1043 .object_function => |i| i.ptr(wasm).type_index,
1044 .zcu_func => |i| i.typeIndex(wasm),
1045 };
1046 }
7971047
798 // In this case we must force link all embedded object files within the archive1048 pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 {
799 // We loop over all symbols, and then group them by offset as the offset1049 return switch (unpack(r, wasm)) {
800 // notates where the object file starts.1050 .unresolved => unreachable,
801 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);1051 .__wasm_apply_global_tls_relocs => @tagName(Unpacked.__wasm_apply_global_tls_relocs),
802 defer offsets.deinit();1052 .__wasm_call_ctors => @tagName(Unpacked.__wasm_call_ctors),
803 for (archive.toc.values()) |symbol_offsets| {1053 .__wasm_init_memory => @tagName(Unpacked.__wasm_init_memory),
804 for (symbol_offsets.items) |sym_offset| {1054 .__wasm_init_tls => @tagName(Unpacked.__wasm_init_tls),
805 try offsets.put(sym_offset, {});1055 .object_function => |i| i.ptr(wasm).name.slice(wasm),
1056 .zcu_func => |i| i.name(wasm),
1057 };
806 }1058 }
807 }1059 };
8081060
809 for (offsets.keys()) |file_offset| {1061 /// Index into `object_function_imports`.
810 const object = try archive.parseObject(wasm, file_contents[file_offset..], obj.path);1062 pub const Index = enum(u32) {
811 try wasm.objects.append(gpa, object);1063 _,
812 }
813}
8141064
815fn requiresTLSReloc(wasm: *const Wasm) bool {1065 pub fn key(index: Index, wasm: *const Wasm) *String {
816 for (wasm.got_symbols.items) |loc| {1066 return &wasm.object_function_imports.keys()[@intFromEnum(index)];
817 if (wasm.symbolLocSymbol(loc).isTLS()) {
818 return true;
819 }1067 }
820 }
821 return false;
822}
823
824fn objectPath(wasm: *const Wasm, object_id: ObjectId) Path {
825 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.path;
826 return obj.path;
827}
8281068
829fn objectSymbols(wasm: *const Wasm, object_id: ObjectId) []const Symbol {1069 pub fn value(index: Index, wasm: *const Wasm) *FunctionImport {
830 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.symbols.items;1070 return &wasm.object_function_imports.values()[@intFromEnum(index)];
831 return obj.symtable;1071 }
832}
8331072
834fn objectSymbol(wasm: *const Wasm, object_id: ObjectId, index: Symbol.Index) *Symbol {1073 pub fn symbolName(index: Index, wasm: *const Wasm) String {
835 const obj = wasm.objectById(object_id) orelse return &wasm.zig_object.?.symbols.items[@intFromEnum(index)];1074 return index.key(wasm).*;
836 return &obj.symtable[@intFromEnum(index)];1075 }
837}
8381076
839fn objectFunction(wasm: *const Wasm, object_id: ObjectId, sym_index: Symbol.Index) std.wasm.Func {1077 pub fn importName(index: Index, wasm: *const Wasm) String {
840 const obj = wasm.objectById(object_id) orelse {1078 return index.value(wasm).name;
841 const zo = wasm.zig_object.?;1079 }
842 const sym = zo.symbols.items[@intFromEnum(sym_index)];
843 return zo.functions.items[sym.index];
844 };
845 const sym = obj.symtable[@intFromEnum(sym_index)];
846 return obj.functions[sym.index - obj.imported_functions_count];
847}
8481080
849fn objectImportedFunctions(wasm: *const Wasm, object_id: ObjectId) u32 {1081 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
850 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imported_functions_count;1082 return index.value(wasm).module_name;
851 return obj.imported_functions_count;1083 }
852}
8531084
854fn objectGlobals(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Global {1085 pub fn functionType(index: Index, wasm: *const Wasm) FunctionType.Index {
855 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.globals.items;1086 return value(index, wasm).type;
856 return obj.globals;1087 }
857}1088 };
1089};
8581090
859fn objectFuncTypes(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Type {1091pub const ObjectFunction = extern struct {
860 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.func_types.items;1092 flags: SymbolFlags,
861 return obj.func_types;1093 /// `none` if this function has no symbol describing it.
862}1094 name: OptionalString,
1095 type_index: FunctionType.Index,
1096 code: Code,
1097 /// The offset within the code section where the data starts.
1098 offset: u32,
1099 /// The object file whose code section contains this function.
1100 object_index: ObjectIndex,
8631101
864fn objectSegmentInfo(wasm: *const Wasm, object_id: ObjectId) []const NamedSegment {1102 pub const Code = DataPayload;
865 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.segment_info.items;
866 return obj.segment_info;
867}
8681103
869/// For a given symbol index, find its corresponding import.1104 pub fn relocations(of: *const ObjectFunction, wasm: *const Wasm) ObjectRelocation.IterableSlice {
870/// Asserts import exists.1105 const code_section_index = of.object_index.ptr(wasm).code_section_index.?;
871fn objectImport(wasm: *const Wasm, object_id: ObjectId, symbol_index: Symbol.Index) Import {1106 const relocs = wasm.object_relocations_table.get(code_section_index) orelse return .empty;
872 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imports.get(symbol_index).?;1107 return .init(relocs, of.offset, of.code.len, wasm);
873 return obj.findImport(obj.symtable[@intFromEnum(symbol_index)]);1108 }
874}1109};
8751110
876/// Returns the object element pointer, or null if it is the ZigObject.1111pub const GlobalImport = extern struct {
877fn objectById(wasm: *const Wasm, object_id: ObjectId) ?*Object {1112 flags: SymbolFlags,
878 if (object_id == .zig_object) return null;1113 module_name: OptionalString,
879 return &wasm.objects.items[@intFromEnum(object_id)];1114 /// May be different than the key which is a symbol name.
880}1115 name: String,
1116 source_location: SourceLocation,
1117 resolution: Resolution,
1118
1119 /// Represents a synthetic global, a global from an object, or a global
1120 /// from the Zcu.
1121 pub const Resolution = enum(u32) {
1122 unresolved,
1123 __heap_base,
1124 __heap_end,
1125 __stack_pointer,
1126 __tls_align,
1127 __tls_base,
1128 __tls_size,
1129 // Next, index into `object_globals`.
1130 // Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object.
1131 _,
8811132
882fn resolveSymbolsInObject(wasm: *Wasm, object_id: ObjectId) !void {1133 const first_object_global = @intFromEnum(Resolution.__tls_size) + 1;
883 const gpa = wasm.base.comp.gpa;1134
884 const diags = &wasm.base.comp.link_diags;1135 pub const Unpacked = union(enum) {
885 const obj_path = objectPath(wasm, object_id);1136 unresolved,
886 log.debug("Resolving symbols in object: '{'}'", .{obj_path});1137 __heap_base,
887 const symbols = objectSymbols(wasm, object_id);1138 __heap_end,
8881139 __stack_pointer,
889 for (symbols, 0..) |symbol, i| {1140 __tls_align,
890 const sym_index: Symbol.Index = @enumFromInt(i);1141 __tls_base,
891 const location: SymbolLoc = .{1142 __tls_size,
892 .file = object_id.toOptional(),1143 object_global: ObjectGlobalIndex,
893 .index = sym_index,1144 nav_exe: NavsExeIndex,
1145 nav_obj: NavsObjIndex,
894 };1146 };
895 if (symbol.name == wasm.preloaded_strings.__indirect_function_table) continue;
8961147
897 if (symbol.isLocal()) {1148 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
898 if (symbol.isUndefined()) {1149 return switch (r) {
899 diags.addParseError(obj_path, "local symbol '{s}' references import", .{1150 .unresolved => .unresolved,
900 wasm.stringSlice(symbol.name),1151 .__heap_base => .__heap_base,
901 });1152 .__heap_end => .__heap_end,
902 }1153 .__stack_pointer => .__stack_pointer,
903 try wasm.resolved_symbols.putNoClobber(gpa, location, {});1154 .__tls_align => .__tls_align,
904 continue;1155 .__tls_base => .__tls_base,
1156 .__tls_size => .__tls_size,
1157 _ => {
1158 const i: u32 = @intFromEnum(r);
1159 const object_global_index = i - first_object_global;
1160 if (object_global_index < wasm.object_globals.items.len)
1161 return .{ .object_global = @enumFromInt(object_global_index) };
1162 const comp = wasm.base.comp;
1163 const is_obj = comp.config.output_mode == .Obj;
1164 const nav_index = object_global_index - wasm.object_globals.items.len;
1165 return if (is_obj) .{
1166 .nav_obj = @enumFromInt(nav_index),
1167 } else .{
1168 .nav_exe = @enumFromInt(nav_index),
1169 };
1170 },
1171 };
905 }1172 }
9061173
907 const maybe_existing = try wasm.globals.getOrPut(gpa, symbol.name);1174 pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution {
908 if (!maybe_existing.found_existing) {1175 return switch (unpacked) {
909 maybe_existing.value_ptr.* = location;1176 .unresolved => .unresolved,
910 try wasm.resolved_symbols.putNoClobber(gpa, location, {});1177 .__heap_base => .__heap_base,
9111178 .__heap_end => .__heap_end,
912 if (symbol.isUndefined()) {1179 .__stack_pointer => .__stack_pointer,
913 try wasm.undefs.putNoClobber(gpa, symbol.name, location);1180 .__tls_align => .__tls_align,
914 }1181 .__tls_base => .__tls_base,
915 continue;1182 .__tls_size => .__tls_size,
1183 .object_global => |i| @enumFromInt(first_object_global + @intFromEnum(i)),
1184 .nav_obj => |i| @enumFromInt(first_object_global + wasm.object_globals.items.len + @intFromEnum(i)),
1185 .nav_exe => |i| @enumFromInt(first_object_global + wasm.object_globals.items.len + @intFromEnum(i)),
1186 };
916 }1187 }
9171188
918 const existing_loc = maybe_existing.value_ptr.*;1189 pub fn fromIpNav(wasm: *const Wasm, ip_nav: InternPool.Nav.Index) Resolution {
919 const existing_sym: *Symbol = wasm.symbolLocSymbol(existing_loc);1190 const comp = wasm.base.comp;
920 const existing_file_path: Path = if (existing_loc.file.unwrap()) |id| objectPath(wasm, id) else .{1191 const is_obj = comp.config.output_mode == .Obj;
921 .root_dir = std.Build.Cache.Directory.cwd(),1192 return pack(wasm, if (is_obj) .{
922 .sub_path = wasm.name,1193 .nav_obj = @enumFromInt(wasm.navs_obj.getIndex(ip_nav).?),
923 };1194 } else .{
9241195 .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(ip_nav).?),
925 if (!existing_sym.isUndefined()) outer: {1196 });
926 if (!symbol.isUndefined()) inner: {1197 }
927 if (symbol.isWeak()) {
928 break :inner; // ignore the new symbol (discard it)
929 }
930 if (existing_sym.isWeak()) {
931 break :outer; // existing is weak, while new one isn't. Replace it.
932 }
933 // both are defined and weak, we have a symbol collision.
934 var err = try diags.addErrorWithNotes(2);
935 try err.addMsg("symbol '{s}' defined multiple times", .{wasm.stringSlice(symbol.name)});
936 try err.addNote("first definition in '{'}'", .{existing_file_path});
937 try err.addNote("next definition in '{'}'", .{obj_path});
938 }
9391198
940 try wasm.discarded.put(gpa, location, existing_loc);1199 pub fn fromObjectGlobal(wasm: *const Wasm, object_global: ObjectGlobalIndex) Resolution {
941 continue; // Do not overwrite defined symbols with undefined symbols1200 return pack(wasm, .{ .object_global = object_global });
942 }1201 }
9431202
944 if (symbol.tag != existing_sym.tag) {1203 pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 {
945 var err = try diags.addErrorWithNotes(2);1204 return switch (unpack(r, wasm)) {
946 try err.addMsg("symbol '{s}' mismatching types '{s}' and '{s}'", .{1205 .unresolved => unreachable,
947 wasm.stringSlice(symbol.name), @tagName(symbol.tag), @tagName(existing_sym.tag),1206 .__heap_base => @tagName(Unpacked.__heap_base),
948 });1207 .__heap_end => @tagName(Unpacked.__heap_end),
949 try err.addNote("first definition in '{'}'", .{existing_file_path});1208 .__stack_pointer => @tagName(Unpacked.__stack_pointer),
950 try err.addNote("next definition in '{'}'", .{obj_path});1209 .__tls_align => @tagName(Unpacked.__tls_align),
1210 .__tls_base => @tagName(Unpacked.__tls_base),
1211 .__tls_size => @tagName(Unpacked.__tls_size),
1212 .object_global => |i| i.name(wasm).slice(wasm),
1213 .nav_obj => |i| i.name(wasm),
1214 .nav_exe => |i| i.name(wasm),
1215 };
951 }1216 }
1217 };
9521218
953 if (existing_sym.isUndefined() and symbol.isUndefined()) {1219 /// Index into `Wasm.object_global_imports`.
954 // only verify module/import name for function symbols1220 pub const Index = enum(u32) {
955 if (symbol.tag == .function) {1221 _,
956 const existing_name = if (existing_loc.file.unwrap()) |existing_obj_id|
957 objectImport(wasm, existing_obj_id, existing_loc.index).module_name
958 else
959 wasm.imports.get(existing_loc).?.module_name;
960
961 const module_name = objectImport(wasm, object_id, sym_index).module_name;
962 if (existing_name != module_name) {
963 var err = try diags.addErrorWithNotes(2);
964 try err.addMsg("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
965 wasm.stringSlice(symbol.name),
966 wasm.stringSlice(existing_name),
967 wasm.stringSlice(module_name),
968 });
969 try err.addNote("first definition in '{'}'", .{existing_file_path});
970 try err.addNote("next definition in '{'}'", .{obj_path});
971 }
972 }
9731222
974 // both undefined so skip overwriting existing symbol and discard the new symbol1223 pub fn key(index: Index, wasm: *const Wasm) *String {
975 try wasm.discarded.put(gpa, location, existing_loc);1224 return &wasm.object_global_imports.keys()[@intFromEnum(index)];
976 continue;
977 }1225 }
9781226
979 if (existing_sym.tag == .global) {1227 pub fn value(index: Index, wasm: *const Wasm) *GlobalImport {
980 const existing_ty = wasm.getGlobalType(existing_loc);1228 return &wasm.object_global_imports.values()[@intFromEnum(index)];
981 const new_ty = wasm.getGlobalType(location);
982 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
983 var err = try diags.addErrorWithNotes(2);
984 try err.addMsg("symbol '{s}' mismatching global types", .{wasm.stringSlice(symbol.name)});
985 try err.addNote("first definition in '{'}'", .{existing_file_path});
986 try err.addNote("next definition in '{'}'", .{obj_path});
987 }
988 }1229 }
9891230
990 if (existing_sym.tag == .function) {1231 pub fn symbolName(index: Index, wasm: *const Wasm) String {
991 const existing_ty = wasm.getFunctionSignature(existing_loc);1232 return index.key(wasm).*;
992 const new_ty = wasm.getFunctionSignature(location);
993 if (!existing_ty.eql(new_ty)) {
994 var err = try diags.addErrorWithNotes(3);
995 try err.addMsg("symbol '{s}' mismatching function signatures.", .{wasm.stringSlice(symbol.name)});
996 try err.addNote("expected signature {}, but found signature {}", .{ existing_ty, new_ty });
997 try err.addNote("first definition in '{'}'", .{existing_file_path});
998 try err.addNote("next definition in '{'}'", .{obj_path});
999 }
1000 }1233 }
10011234
1002 // when both symbols are weak, we skip overwriting unless the existing1235 pub fn importName(index: Index, wasm: *const Wasm) String {
1003 // symbol is weak and the new one isn't, in which case we *do* overwrite it.1236 return index.value(wasm).name;
1004 if (existing_sym.isWeak() and symbol.isWeak()) blk: {
1005 if (existing_sym.isUndefined() and !symbol.isUndefined()) break :blk;
1006 try wasm.discarded.put(gpa, location, existing_loc);
1007 continue;
1008 }1237 }
10091238
1010 // simply overwrite with the new symbol1239 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
1011 log.debug("Overwriting symbol '{s}'", .{wasm.stringSlice(symbol.name)});1240 return index.value(wasm).module_name;
1012 log.debug(" old definition in '{'}'", .{existing_file_path});
1013 log.debug(" new definition in '{'}'", .{obj_path});
1014 try wasm.discarded.putNoClobber(gpa, existing_loc, location);
1015 maybe_existing.value_ptr.* = location;
1016 try wasm.globals.put(gpa, symbol.name, location);
1017 try wasm.resolved_symbols.put(gpa, location, {});
1018 assert(wasm.resolved_symbols.swapRemove(existing_loc));
1019 if (existing_sym.isUndefined()) {
1020 _ = wasm.undefs.swapRemove(symbol.name);
1021 }1241 }
1022 }
1023}
10241242
1025fn resolveSymbolsInArchives(wasm: *Wasm) !void {1243 pub fn globalType(index: Index, wasm: *const Wasm) ObjectGlobal.Type {
1026 if (wasm.lazy_archives.items.len == 0) return;1244 return value(index, wasm).type();
1027 const gpa = wasm.base.comp.gpa;1245 }
1028 const diags = &wasm.base.comp.link_diags;1246 };
10291247
1030 log.debug("Resolving symbols in lazy_archives", .{});1248 pub fn @"type"(gi: *const GlobalImport) ObjectGlobal.Type {
1031 var index: u32 = 0;1249 return gi.flags.global_type.to();
1032 undef_loop: while (index < wasm.undefs.count()) {1250 }
1033 const sym_name_index = wasm.undefs.keys()[index];1251};
10341252
1035 for (wasm.lazy_archives.items) |lazy_archive| {1253pub const ObjectGlobal = extern struct {
1036 const sym_name = wasm.stringSlice(sym_name_index);1254 /// `none` if this function has no symbol describing it.
1037 log.debug("Detected symbol '{s}' in archive '{'}', parsing objects..", .{1255 name: OptionalString,
1038 sym_name, lazy_archive.path,1256 flags: SymbolFlags,
1039 });1257 expr: Expr,
1040 const offset = lazy_archive.archive.toc.get(sym_name) orelse continue; // symbol does not exist in this archive1258 /// The object file whose global section contains this global.
10411259 object_index: ObjectIndex,
1042 // Symbol is found in unparsed object file within current archive.1260 offset: u32,
1043 // Parse object and and resolve symbols again before we check remaining1261 size: u32,
1044 // undefined symbols.
1045 const file_contents = lazy_archive.file_contents[offset.items[0]..];
1046 const object = lazy_archive.archive.parseObject(wasm, file_contents, lazy_archive.path) catch |err| {
1047 // TODO this fails to include information to identify which object failed
1048 return diags.failParse(lazy_archive.path, "failed to parse object in archive: {s}", .{@errorName(err)});
1049 };
1050 try wasm.objects.append(gpa, object);
1051 try wasm.resolveSymbolsInObject(@enumFromInt(wasm.objects.items.len - 1));
10521262
1053 // continue loop for any remaining undefined symbols that still exist1263 pub fn @"type"(og: *const ObjectGlobal) Type {
1054 // after resolving last object file1264 return og.flags.global_type.to();
1055 continue :undef_loop;
1056 }
1057 index += 1;
1058 }1265 }
1059}
10601266
1061/// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value.1267 pub const Type = struct {
1062fn writeI32Const(writer: anytype, val: u32) !void {1268 valtype: std.wasm.Valtype,
1063 try writer.writeByte(std.wasm.opcode(.i32_const));1269 mutable: bool,
1064 try leb.writeIleb128(writer, @as(i32, @bitCast(val)));1270 };
1065}
1066
1067fn setupInitMemoryFunction(wasm: *Wasm) !void {
1068 const comp = wasm.base.comp;
1069 const gpa = comp.gpa;
1070 const shared_memory = comp.config.shared_memory;
1071 const import_memory = comp.config.import_memory;
10721271
1073 // Passive segments are used to avoid memory being reinitialized on each1272 pub fn relocations(og: *const ObjectGlobal, wasm: *const Wasm) ObjectRelocation.IterableSlice {
1074 // thread's instantiation. These passive segments are initialized and1273 const global_section_index = og.object_index.ptr(wasm).global_section_index.?;
1075 // dropped in __wasm_init_memory, which is registered as the start function1274 const relocs = wasm.object_relocations_table.get(global_section_index) orelse return .empty;
1076 // We also initialize bss segments (using memory.fill) as part of this1275 return .init(relocs, og.offset, og.size, wasm);
1077 // function.
1078 if (!wasm.hasPassiveInitializationSegments()) {
1079 return;
1080 }1276 }
1081 const sym_loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_memory, .function);1277};
1082 wasm.symbolLocSymbol(sym_loc).mark();
1083
1084 const flag_address: u32 = if (shared_memory) address: {
1085 // when we have passive initialization segments and shared memory
1086 // `setupMemory` will create this symbol and set its virtual address.
1087 const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_init_memory_flag).?;
1088 break :address wasm.symbolLocSymbol(loc).virtual_address;
1089 } else 0;
1090
1091 var function_body = std.ArrayList(u8).init(gpa);
1092 defer function_body.deinit();
1093 const writer = function_body.writer();
1094
1095 // we have 0 locals
1096 try leb.writeUleb128(writer, @as(u32, 0));
1097
1098 if (shared_memory) {
1099 // destination blocks
1100 // based on values we jump to corresponding label
1101 try writer.writeByte(std.wasm.opcode(.block)); // $drop
1102 try writer.writeByte(std.wasm.block_empty); // block type
1103
1104 try writer.writeByte(std.wasm.opcode(.block)); // $wait
1105 try writer.writeByte(std.wasm.block_empty); // block type
1106
1107 try writer.writeByte(std.wasm.opcode(.block)); // $init
1108 try writer.writeByte(std.wasm.block_empty); // block type
1109
1110 // atomically check
1111 try writeI32Const(writer, flag_address);
1112 try writeI32Const(writer, 0);
1113 try writeI32Const(writer, 1);
1114 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
1115 try leb.writeUleb128(writer, std.wasm.atomicsOpcode(.i32_atomic_rmw_cmpxchg));
1116 try leb.writeUleb128(writer, @as(u32, 2)); // alignment
1117 try leb.writeUleb128(writer, @as(u32, 0)); // offset
1118
1119 // based on the value from the atomic check, jump to the label.
1120 try writer.writeByte(std.wasm.opcode(.br_table));
1121 try leb.writeUleb128(writer, @as(u32, 2)); // length of the table (we have 3 blocks but because of the mandatory default the length is 2).
1122 try leb.writeUleb128(writer, @as(u32, 0)); // $init
1123 try leb.writeUleb128(writer, @as(u32, 1)); // $wait
1124 try leb.writeUleb128(writer, @as(u32, 2)); // $drop
1125 try writer.writeByte(std.wasm.opcode(.end));
1126 }
1127
1128 for (wasm.data_segments.keys(), wasm.data_segments.values(), 0..) |key, value, segment_index_usize| {
1129 const segment_index: u32 = @intCast(segment_index_usize);
1130 const segment = wasm.segmentPtr(value);
1131 if (segment.needsPassiveInitialization(import_memory, key)) {
1132 // For passive BSS segments we can simple issue a memory.fill(0).
1133 // For non-BSS segments we do a memory.init. Both these
1134 // instructions take as their first argument the destination
1135 // address.
1136 try writeI32Const(writer, segment.offset);
1137
1138 if (shared_memory and std.mem.eql(u8, key, ".tdata")) {
1139 // When we initialize the TLS segment we also set the `__tls_base`
1140 // global. This allows the runtime to use this static copy of the
1141 // TLS data for the first/main thread.
1142 try writeI32Const(writer, segment.offset);
1143 try writer.writeByte(std.wasm.opcode(.global_set));
1144 const loc = wasm.globals.get(wasm.preloaded_strings.__tls_base).?;
1145 try leb.writeUleb128(writer, wasm.symbolLocSymbol(loc).index);
1146 }
11471278
1148 try writeI32Const(writer, 0);1279pub const RefType1 = enum(u1) {
1149 try writeI32Const(writer, segment.size);1280 funcref,
1150 try writer.writeByte(std.wasm.opcode(.misc_prefix));1281 externref,
1151 if (std.mem.eql(u8, key, ".bss")) {
1152 // fill bss segment with zeroes
1153 try leb.writeUleb128(writer, std.wasm.miscOpcode(.memory_fill));
1154 } else {
1155 // initialize the segment
1156 try leb.writeUleb128(writer, std.wasm.miscOpcode(.memory_init));
1157 try leb.writeUleb128(writer, segment_index);
1158 }
1159 try writer.writeByte(0); // memory index immediate
1160 }
1161 }
1162
1163 if (shared_memory) {
1164 // we set the init memory flag to value '2'
1165 try writeI32Const(writer, flag_address);
1166 try writeI32Const(writer, 2);
1167 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
1168 try leb.writeUleb128(writer, std.wasm.atomicsOpcode(.i32_atomic_store));
1169 try leb.writeUleb128(writer, @as(u32, 2)); // alignment
1170 try leb.writeUleb128(writer, @as(u32, 0)); // offset
1171
1172 // notify any waiters for segment initialization completion
1173 try writeI32Const(writer, flag_address);
1174 try writer.writeByte(std.wasm.opcode(.i32_const));
1175 try leb.writeIleb128(writer, @as(i32, -1)); // number of waiters
1176 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
1177 try leb.writeUleb128(writer, std.wasm.atomicsOpcode(.memory_atomic_notify));
1178 try leb.writeUleb128(writer, @as(u32, 2)); // alignment
1179 try leb.writeUleb128(writer, @as(u32, 0)); // offset
1180 try writer.writeByte(std.wasm.opcode(.drop));
1181
1182 // branch and drop segments
1183 try writer.writeByte(std.wasm.opcode(.br));
1184 try leb.writeUleb128(writer, @as(u32, 1));
1185
1186 // wait for thread to initialize memory segments
1187 try writer.writeByte(std.wasm.opcode(.end)); // end $wait
1188 try writeI32Const(writer, flag_address);
1189 try writeI32Const(writer, 1); // expected flag value
1190 try writer.writeByte(std.wasm.opcode(.i64_const));
1191 try leb.writeIleb128(writer, @as(i64, -1)); // timeout
1192 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
1193 try leb.writeUleb128(writer, std.wasm.atomicsOpcode(.memory_atomic_wait32));
1194 try leb.writeUleb128(writer, @as(u32, 2)); // alignment
1195 try leb.writeUleb128(writer, @as(u32, 0)); // offset
1196 try writer.writeByte(std.wasm.opcode(.drop));
1197
1198 try writer.writeByte(std.wasm.opcode(.end)); // end $drop
1199 }
1200
1201 for (wasm.data_segments.keys(), wasm.data_segments.values(), 0..) |name, value, segment_index_usize| {
1202 const segment_index: u32 = @intCast(segment_index_usize);
1203 const segment = wasm.segmentPtr(value);
1204 if (segment.needsPassiveInitialization(import_memory, name) and
1205 !std.mem.eql(u8, name, ".bss"))
1206 {
1207 // The TLS region should not be dropped since its is needed
1208 // during the initialization of each thread (__wasm_init_tls).
1209 if (shared_memory and std.mem.eql(u8, name, ".tdata")) {
1210 continue;
1211 }
12121282
1213 try writer.writeByte(std.wasm.opcode(.misc_prefix));1283 pub fn from(rt: std.wasm.RefType) RefType1 {
1214 try leb.writeUleb128(writer, std.wasm.miscOpcode(.data_drop));1284 return switch (rt) {
1215 try leb.writeUleb128(writer, segment_index);1285 .funcref => .funcref,
1216 }1286 .externref => .externref,
1287 };
1217 }1288 }
12181289
1219 // End of the function body1290 pub fn to(rt: RefType1) std.wasm.RefType {
1220 try writer.writeByte(std.wasm.opcode(.end));1291 return switch (rt) {
1292 .funcref => .funcref,
1293 .externref => .externref,
1294 };
1295 }
1296};
12211297
1222 try wasm.createSyntheticFunction(1298pub const TableImport = extern struct {
1223 wasm.preloaded_strings.__wasm_init_memory,1299 flags: SymbolFlags,
1224 std.wasm.Type{ .params = &.{}, .returns = &.{} },1300 module_name: String,
1225 &function_body,1301 /// May be different than the key which is a symbol name.
1226 );1302 name: String,
1227}1303 source_location: SourceLocation,
1304 resolution: Resolution,
1305 limits_min: u32,
1306 limits_max: u32,
1307
1308 /// Represents a synthetic table, or a table from an object.
1309 pub const Resolution = enum(u32) {
1310 unresolved,
1311 __indirect_function_table,
1312 // Next, index into `object_tables`.
1313 _,
12281314
1229/// Constructs a synthetic function that performs runtime relocations for1315 const first_object_table = @intFromEnum(Resolution.__indirect_function_table) + 1;
1230/// TLS symbols. This function is called by `__wasm_init_tls`.
1231fn setupTLSRelocationsFunction(wasm: *Wasm) !void {
1232 const comp = wasm.base.comp;
1233 const gpa = comp.gpa;
1234 const shared_memory = comp.config.shared_memory;
12351316
1236 // When we have TLS GOT entries and shared memory is enabled,1317 pub const Unpacked = union(enum) {
1237 // we must perform runtime relocations or else we don't create the function.1318 unresolved,
1238 if (!shared_memory or !wasm.requiresTLSReloc()) {1319 __indirect_function_table,
1239 return;1320 object_table: ObjectTableIndex,
1240 }1321 };
12411322
1242 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_apply_global_tls_relocs, .function);1323 pub fn unpack(r: Resolution) Unpacked {
1243 wasm.symbolLocSymbol(loc).mark();1324 return switch (r) {
1244 var function_body = std.ArrayList(u8).init(gpa);1325 .unresolved => .unresolved,
1245 defer function_body.deinit();1326 .__indirect_function_table => .__indirect_function_table,
1246 const writer = function_body.writer();1327 _ => .{ .object_table = @enumFromInt(@intFromEnum(r) - first_object_table) },
12471328 };
1248 // locals (we have none)1329 }
1249 try writer.writeByte(0);
1250 for (wasm.got_symbols.items, 0..) |got_loc, got_index| {
1251 const sym: *Symbol = wasm.symbolLocSymbol(got_loc);
1252 if (!sym.isTLS()) continue; // only relocate TLS symbols
1253 if (sym.tag == .data and sym.isDefined()) {
1254 // get __tls_base
1255 try writer.writeByte(std.wasm.opcode(.global_get));
1256 try leb.writeUleb128(writer, wasm.symbolLocSymbol(wasm.globals.get(wasm.preloaded_strings.__tls_base).?).index);
1257
1258 // add the virtual address of the symbol
1259 try writer.writeByte(std.wasm.opcode(.i32_const));
1260 try leb.writeUleb128(writer, sym.virtual_address);
1261 } else if (sym.tag == .function) {
1262 @panic("TODO: relocate GOT entry of function");
1263 } else continue;
1264
1265 try writer.writeByte(std.wasm.opcode(.i32_add));
1266 try writer.writeByte(std.wasm.opcode(.global_set));
1267 try leb.writeUleb128(writer, wasm.imported_globals_count + @as(u32, @intCast(wasm.wasm_globals.items.len + got_index)));
1268 }
1269 try writer.writeByte(std.wasm.opcode(.end));
1270
1271 try wasm.createSyntheticFunction(
1272 wasm.preloaded_strings.__wasm_apply_global_tls_relocs,
1273 std.wasm.Type{ .params = &.{}, .returns = &.{} },
1274 &function_body,
1275 );
1276}
12771330
1278fn validateFeatures(1331 fn pack(unpacked: Unpacked) Resolution {
1279 wasm: *const Wasm,1332 return switch (unpacked) {
1280 to_emit: *[@typeInfo(Feature.Tag).@"enum".fields.len]bool,1333 .unresolved => .unresolved,
1281 emit_features_count: *u32,1334 .__indirect_function_table => .__indirect_function_table,
1282) !void {1335 .object_table => |i| @enumFromInt(first_object_table + @intFromEnum(i)),
1283 const comp = wasm.base.comp;1336 };
1284 const diags = &wasm.base.comp.link_diags;
1285 const target = comp.root_mod.resolved_target.result;
1286 const shared_memory = comp.config.shared_memory;
1287 const cpu_features = target.cpu.features;
1288 const infer = cpu_features.isEmpty(); // when the user did not define any features, we infer them from linked objects.
1289 const known_features_count = @typeInfo(Feature.Tag).@"enum".fields.len;
1290
1291 var allowed = [_]bool{false} ** known_features_count;
1292 var used = [_]u17{0} ** known_features_count;
1293 var disallowed = [_]u17{0} ** known_features_count;
1294 var required = [_]u17{0} ** known_features_count;
1295
1296 // when false, we fail linking. We only verify this after a loop to catch all invalid features.
1297 var valid_feature_set = true;
1298 // will be set to true when there's any TLS segment found in any of the object files
1299 var has_tls = false;
1300
1301 // When the user has given an explicit list of features to enable,
1302 // we extract them and insert each into the 'allowed' list.
1303 if (!infer) {
1304 inline for (@typeInfo(std.Target.wasm.Feature).@"enum".fields) |feature_field| {
1305 if (cpu_features.isEnabled(feature_field.value)) {
1306 allowed[feature_field.value] = true;
1307 emit_features_count.* += 1;
1308 }
1309 }1337 }
1310 }
13111338
1312 // extract all the used, disallowed and required features from each1339 fn fromObjectTable(object_table: ObjectTableIndex) Resolution {
1313 // linked object file so we can test them.1340 return pack(.{ .object_table = object_table });
1314 for (wasm.objects.items, 0..) |*object, file_index| {
1315 for (object.features) |feature| {
1316 const value = (@as(u16, @intCast(file_index)) << 1) | 1;
1317 switch (feature.prefix) {
1318 .used => {
1319 used[@intFromEnum(feature.tag)] = value;
1320 },
1321 .disallowed => {
1322 disallowed[@intFromEnum(feature.tag)] = value;
1323 },
1324 .required => {
1325 required[@intFromEnum(feature.tag)] = value;
1326 used[@intFromEnum(feature.tag)] = value;
1327 },
1328 }
1329 }1341 }
13301342
1331 for (object.segment_info) |segment| {1343 pub fn refType(r: Resolution, wasm: *const Wasm) std.wasm.RefType {
1332 if (segment.isTLS()) {1344 return switch (unpack(r)) {
1333 has_tls = true;1345 .unresolved => unreachable,
1334 }1346 .__indirect_function_table => .funcref,
1347 .object_table => |i| i.ptr(wasm).flags.ref_type.to(),
1348 };
1335 }1349 }
1336 }
13371350
1338 // when we infer the features, we allow each feature found in the 'used' set1351 pub fn limits(r: Resolution, wasm: *const Wasm) std.wasm.Limits {
1339 // and insert it into the 'allowed' set. When features are not inferred,1352 return switch (unpack(r)) {
1340 // we validate that a used feature is allowed.1353 .unresolved => unreachable,
1341 for (used, 0..) |used_set, used_index| {1354 .__indirect_function_table => .{
1342 const is_enabled = @as(u1, @truncate(used_set)) != 0;1355 .flags = .{ .has_max = true, .is_shared = false },
1343 if (infer) {1356 .min = @intCast(wasm.flush_buffer.indirect_function_table.entries.len + 1),
1344 allowed[used_index] = is_enabled;1357 .max = @intCast(wasm.flush_buffer.indirect_function_table.entries.len + 1),
1345 emit_features_count.* += @intFromBool(is_enabled);1358 },
1346 } else if (is_enabled and !allowed[used_index]) {1359 .object_table => |i| i.ptr(wasm).limits(),
1347 diags.addParseError(1360 };
1348 wasm.objects.items[used_set >> 1].path,
1349 "feature '{}' not allowed, but used by linked object",
1350 .{@as(Feature.Tag, @enumFromInt(used_index))},
1351 );
1352 valid_feature_set = false;
1353 }1361 }
1354 }1362 };
13551363
1356 if (!valid_feature_set) {1364 /// Index into `object_table_imports`.
1357 return error.FlushFailure;1365 pub const Index = enum(u32) {
1358 }1366 _,
13591367
1360 if (shared_memory) {1368 pub fn key(index: Index, wasm: *const Wasm) *String {
1361 const disallowed_feature = disallowed[@intFromEnum(Feature.Tag.shared_mem)];1369 return &wasm.object_table_imports.keys()[@intFromEnum(index)];
1362 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1363 diags.addParseError(
1364 wasm.objects.items[disallowed_feature >> 1].path,
1365 "shared-memory is disallowed because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
1366 .{},
1367 );
1368 valid_feature_set = false;
1369 }1370 }
13701371
1371 for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| {1372 pub fn value(index: Index, wasm: *const Wasm) *TableImport {
1372 if (!allowed[@intFromEnum(feature)]) {1373 return &wasm.object_table_imports.values()[@intFromEnum(index)];
1373 var err = try diags.addErrorWithNotes(0);
1374 try err.addMsg("feature '{}' is not used but is required for shared-memory", .{feature});
1375 }
1376 }1374 }
1377 }
13781375
1379 if (has_tls) {1376 pub fn name(index: Index, wasm: *const Wasm) String {
1380 for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| {1377 return index.key(wasm).*;
1381 if (!allowed[@intFromEnum(feature)]) {
1382 var err = try diags.addErrorWithNotes(0);
1383 try err.addMsg("feature '{}' is not used but is required for thread-local storage", .{feature});
1384 }
1385 }1378 }
1386 }
1387 // For each linked object, validate the required and disallowed features
1388 for (wasm.objects.items) |*object| {
1389 var object_used_features = [_]bool{false} ** known_features_count;
1390 for (object.features) |feature| {
1391 if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set.
1392 // from here a feature is always used
1393 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
1394 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1395 var err = try diags.addErrorWithNotes(2);
1396 try err.addMsg("feature '{}' is disallowed, but used by linked object", .{feature.tag});
1397 try err.addNote("disallowed by '{'}'", .{wasm.objects.items[disallowed_feature >> 1].path});
1398 try err.addNote("used in '{'}'", .{object.path});
1399 valid_feature_set = false;
1400 }
14011379
1402 object_used_features[@intFromEnum(feature.tag)] = true;1380 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
1381 return index.value(wasm).module_name;
1403 }1382 }
1383 };
14041384
1405 // validate the linked object file has each required feature1385 pub fn limits(ti: *const TableImport) std.wasm.Limits {
1406 for (required, 0..) |required_feature, feature_index| {1386 return .{
1407 const is_required = @as(u1, @truncate(required_feature)) != 0;1387 .flags = .{
1408 if (is_required and !object_used_features[feature_index]) {1388 .has_max = ti.flags.limits_has_max,
1409 var err = try diags.addErrorWithNotes(2);1389 .is_shared = ti.flags.limits_is_shared,
1410 try err.addMsg("feature '{}' is required but not used in linked object", .{@as(Feature.Tag, @enumFromInt(feature_index))});1390 },
1411 try err.addNote("required by '{'}'", .{wasm.objects.items[required_feature >> 1].path});1391 .min = ti.limits_min,
1412 try err.addNote("missing in '{'}'", .{object.path});1392 .max = ti.limits_max,
1413 valid_feature_set = false;1393 };
1414 }
1415 }
1416 }1394 }
1395};
14171396
1418 if (!valid_feature_set) {1397pub const Table = extern struct {
1419 return error.FlushFailure;1398 module_name: OptionalString,
1420 }1399 name: OptionalString,
1400 flags: SymbolFlags,
1401 limits_min: u32,
1402 limits_max: u32,
14211403
1422 to_emit.* = allowed;1404 pub fn limits(t: *const Table) std.wasm.Limits {
1423}1405 return .{
1406 .flags = .{
1407 .has_max = t.flags.limits_has_max,
1408 .is_shared = t.flags.limits_is_shared,
1409 },
1410 .min = t.limits_min,
1411 .max = t.limits_max,
1412 };
1413 }
1414};
14241415
1425/// Creates synthetic linker-symbols, but only if they are being referenced from1416/// Uniquely identifies a section across all objects. By subtracting
1426/// any object file. For instance, the `__heap_base` symbol will only be created,1417/// `Object.local_section_index_base` from this one, the Object section index
1427/// if one or multiple undefined references exist. When none exist, the symbol will1418/// is obtained.
1428/// not be created, ensuring we don't unnecessarily emit unreferenced symbols.1419pub const ObjectSectionIndex = enum(u32) {
1429fn resolveLazySymbols(wasm: *Wasm) !void {1420 _,
1430 const comp = wasm.base.comp;1421};
1431 const gpa = comp.gpa;
1432 const shared_memory = comp.config.shared_memory;
14331422
1434 if (wasm.getExistingString("__heap_base")) |name_offset| {1423/// Index into `object_tables`.
1435 if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {1424pub const ObjectTableIndex = enum(u32) {
1436 const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data);1425 _,
1437 try wasm.discarded.putNoClobber(gpa, kv.value, loc);
1438 _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations.
1439 }
1440 }
14411426
1442 if (wasm.getExistingString("__heap_end")) |name_offset| {1427 pub fn ptr(index: ObjectTableIndex, wasm: *const Wasm) *Table {
1443 if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {1428 return &wasm.object_tables.items[@intFromEnum(index)];
1444 const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data);
1445 try wasm.discarded.putNoClobber(gpa, kv.value, loc);
1446 _ = wasm.resolved_symbols.swapRemove(loc);
1447 }
1448 }1429 }
14491430
1450 if (!shared_memory) {1431 pub fn chaseWeak(i: ObjectTableIndex, wasm: *const Wasm) ObjectTableIndex {
1451 if (wasm.getExistingString("__tls_base")) |name_offset| {1432 const table = ptr(i, wasm);
1452 if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {1433 if (table.flags.binding != .weak) return i;
1453 const loc = try wasm.createSyntheticSymbolOffset(name_offset, .global);1434 const name = table.name.unwrap().?;
1454 try wasm.discarded.putNoClobber(gpa, kv.value, loc);1435 const import = wasm.object_table_imports.getPtr(name).?;
1455 _ = wasm.resolved_symbols.swapRemove(kv.value);1436 assert(import.resolution != .unresolved); // otherwise it should resolve to this one.
1456 const symbol = wasm.symbolLocSymbol(loc);1437 return import.resolution.unpack().object_table;
1457 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
1458 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
1459 try wasm.wasm_globals.append(gpa, .{
1460 .global_type = .{ .valtype = .i32, .mutable = true },
1461 .init = .{ .i32_const = undefined },
1462 });
1463 }
1464 }
1465 }1438 }
1466}1439};
14671440
1468pub fn findGlobalSymbol(wasm: *const Wasm, name: []const u8) ?SymbolLoc {1441/// Index into `Wasm.object_globals`.
1469 const name_index = wasm.getExistingString(name) orelse return null;1442pub const ObjectGlobalIndex = enum(u32) {
1470 return wasm.globals.get(name_index);1443 _,
1471}
14721444
1473fn checkUndefinedSymbols(wasm: *const Wasm) !void {1445 pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *ObjectGlobal {
1474 const comp = wasm.base.comp;1446 return &wasm.object_globals.items[@intFromEnum(index)];
1475 const diags = &wasm.base.comp.link_diags;
1476 if (comp.config.output_mode == .Obj) return;
1477 if (wasm.import_symbols) return;
1478
1479 var found_undefined_symbols = false;
1480 for (wasm.undefs.values()) |undef| {
1481 const symbol = wasm.symbolLocSymbol(undef);
1482 if (symbol.tag == .data) {
1483 found_undefined_symbols = true;
1484 const symbol_name = wasm.symbolLocName(undef);
1485 switch (undef.file) {
1486 .zig_object => {
1487 // TODO: instead of saying the zig compilation unit, attach an actual source location
1488 // to this diagnostic
1489 diags.addError("unresolved symbol in Zig compilation unit: {s}", .{symbol_name});
1490 },
1491 .none => {
1492 diags.addError("internal linker bug: unresolved synthetic symbol: {s}", .{symbol_name});
1493 },
1494 _ => {
1495 const path = wasm.objects.items[@intFromEnum(undef.file)].path;
1496 diags.addParseError(path, "unresolved symbol: {s}", .{symbol_name});
1497 },
1498 }
1499 }
1500 }
1501 if (found_undefined_symbols) {
1502 return error.LinkFailure;
1503 }1447 }
1504}
1505
1506pub fn deinit(wasm: *Wasm) void {
1507 const gpa = wasm.base.comp.gpa;
1508 if (wasm.llvm_object) |llvm_object| llvm_object.deinit();
15091448
1510 for (wasm.func_types.items) |*func_type| {1449 pub fn name(index: ObjectGlobalIndex, wasm: *const Wasm) OptionalString {
1511 func_type.deinit(gpa);1450 return index.ptr(wasm).name;
1512 }
1513 for (wasm.segment_info.values()) |segment_info| {
1514 gpa.free(segment_info.name);
1515 }
1516 if (wasm.zig_object) |zig_obj| {
1517 zig_obj.deinit(wasm);
1518 }
1519 for (wasm.objects.items) |*object| {
1520 object.deinit(gpa);
1521 }1451 }
15221452
1523 for (wasm.lazy_archives.items) |*lazy_archive| lazy_archive.deinit(gpa);1453 pub fn chaseWeak(i: ObjectGlobalIndex, wasm: *const Wasm) ObjectGlobalIndex {
1524 wasm.lazy_archives.deinit(gpa);1454 const global = ptr(i, wasm);
15251455 if (global.flags.binding != .weak) return i;
1526 if (wasm.globals.get(wasm.preloaded_strings.__wasm_init_tls)) |loc| {1456 const import_name = global.name.unwrap().?;
1527 const atom = wasm.symbol_atom.get(loc).?;1457 const import = wasm.object_global_imports.getPtr(import_name).?;
1528 wasm.getAtomPtr(atom).deinit(gpa);1458 assert(import.resolution != .unresolved); // otherwise it should resolve to this one.
1459 return import.resolution.unpack(wasm).object_global;
1529 }1460 }
1461};
15301462
1531 wasm.synthetic_symbols.deinit(gpa);1463pub const ObjectMemory = extern struct {
1532 wasm.globals.deinit(gpa);1464 flags: SymbolFlags,
1533 wasm.resolved_symbols.deinit(gpa);1465 name: OptionalString,
1534 wasm.undefs.deinit(gpa);1466 limits_min: u32,
1535 wasm.discarded.deinit(gpa);1467 limits_max: u32,
1536 wasm.symbol_atom.deinit(gpa);
1537 wasm.atoms.deinit(gpa);
1538 wasm.managed_atoms.deinit(gpa);
1539 wasm.segments.deinit(gpa);
1540 wasm.data_segments.deinit(gpa);
1541 wasm.segment_info.deinit(gpa);
1542 wasm.objects.deinit(gpa);
15431468
1544 // free output sections1469 /// Index into `Wasm.object_memories`.
1545 wasm.imports.deinit(gpa);1470 pub const Index = enum(u32) {
1546 wasm.func_types.deinit(gpa);1471 _,
1547 wasm.functions.deinit(gpa);
1548 wasm.wasm_globals.deinit(gpa);
1549 wasm.function_table.deinit(gpa);
1550 wasm.tables.deinit(gpa);
1551 wasm.init_funcs.deinit(gpa);
1552 wasm.exports.deinit(gpa);
15531472
1554 wasm.string_bytes.deinit(gpa);1473 pub fn ptr(index: Index, wasm: *const Wasm) *ObjectMemory {
1555 wasm.string_table.deinit(gpa);1474 return &wasm.object_memories.items[@intFromEnum(index)];
1556 wasm.dump_argv_list.deinit(gpa);1475 }
1557}1476 };
15581477
1559pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {1478 pub fn limits(om: *const ObjectMemory) std.wasm.Limits {
1560 if (build_options.skip_non_native and builtin.object_format != .wasm) {1479 return .{
1561 @panic("Attempted to compile for object format that was disabled by build configuration");1480 .flags = .{
1481 .has_max = om.limits_has_max,
1482 .is_shared = om.limits_is_shared,
1483 },
1484 .min = om.limits_min,
1485 .max = om.limits_max,
1486 };
1562 }1487 }
1563 if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness);1488};
1564 try wasm.zig_object.?.updateFunc(wasm, pt, func_index, air, liveness);
1565}
15661489
1567// Generate code for the "Nav", storing it in memory to be later written to1490/// Index into `Wasm.object_functions`.
1568// the file on flush().1491pub const ObjectFunctionIndex = enum(u32) {
1569pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {1492 _,
1570 if (build_options.skip_non_native and builtin.object_format != .wasm) {
1571 @panic("Attempted to compile for object format that was disabled by build configuration");
1572 }
1573 if (wasm.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav);
1574 try wasm.zig_object.?.updateNav(wasm, pt, nav);
1575}
15761493
1577pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {1494 pub fn ptr(index: ObjectFunctionIndex, wasm: *const Wasm) *ObjectFunction {
1578 if (wasm.llvm_object) |_| return;1495 return &wasm.object_functions.items[@intFromEnum(index)];
1579 try wasm.zig_object.?.updateLineNumber(pt, ti_id);1496 }
1580}
15811497
1582/// From a given symbol location, returns its `wasm.GlobalType`.1498 pub fn toOptional(i: ObjectFunctionIndex) OptionalObjectFunctionIndex {
1583/// Asserts the Symbol represents a global.1499 const result: OptionalObjectFunctionIndex = @enumFromInt(@intFromEnum(i));
1584fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType {1500 assert(result != .none);
1585 const symbol = wasm.symbolLocSymbol(loc);1501 return result;
1586 assert(symbol.tag == .global);
1587 const is_undefined = symbol.isUndefined();
1588 switch (loc.file) {
1589 .zig_object => {
1590 const zo = wasm.zig_object.?;
1591 return if (is_undefined)
1592 zo.imports.get(loc.index).?.kind.global
1593 else
1594 zo.globals.items[symbol.index - zo.imported_globals_count].global_type;
1595 },
1596 .none => {
1597 return if (is_undefined)
1598 wasm.imports.get(loc).?.kind.global
1599 else
1600 wasm.wasm_globals.items[symbol.index].global_type;
1601 },
1602 _ => {
1603 const obj = &wasm.objects.items[@intFromEnum(loc.file)];
1604 return if (is_undefined)
1605 obj.findImport(obj.symtable[@intFromEnum(loc.index)]).kind.global
1606 else
1607 obj.globals[symbol.index - obj.imported_globals_count].global_type;
1608 },
1609 }1502 }
1610}
16111503
1612/// From a given symbol location, returns its `wasm.Type`.1504 pub fn chaseWeak(i: ObjectFunctionIndex, wasm: *const Wasm) ObjectFunctionIndex {
1613/// Asserts the Symbol represents a function.1505 const func = ptr(i, wasm);
1614fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {1506 if (func.flags.binding != .weak) return i;
1615 const symbol = wasm.symbolLocSymbol(loc);1507 const name = func.name.unwrap().?;
1616 assert(symbol.tag == .function);1508 const import = wasm.object_function_imports.getPtr(name).?;
1617 const is_undefined = symbol.isUndefined();1509 assert(import.resolution != .unresolved); // otherwise it should resolve to this one.
1618 switch (loc.file) {1510 return import.resolution.unpack(wasm).object_function;
1619 .zig_object => {
1620 const zo = wasm.zig_object.?;
1621 if (is_undefined) {
1622 const type_index = zo.imports.get(loc.index).?.kind.function;
1623 return zo.func_types.items[type_index];
1624 }
1625 const sym = zo.symbols.items[@intFromEnum(loc.index)];
1626 const type_index = zo.functions.items[sym.index].type_index;
1627 return zo.func_types.items[type_index];
1628 },
1629 .none => {
1630 if (is_undefined) {
1631 const type_index = wasm.imports.get(loc).?.kind.function;
1632 return wasm.func_types.items[type_index];
1633 }
1634 return wasm.func_types.items[
1635 wasm.functions.get(.{
1636 .file = .none,
1637 .index = symbol.index,
1638 }).?.func.type_index
1639 ];
1640 },
1641 _ => {
1642 const obj = &wasm.objects.items[@intFromEnum(loc.file)];
1643 if (is_undefined) {
1644 const type_index = obj.findImport(obj.symtable[@intFromEnum(loc.index)]).kind.function;
1645 return obj.func_types[type_index];
1646 }
1647 const sym = obj.symtable[@intFromEnum(loc.index)];
1648 const type_index = obj.functions[sym.index - obj.imported_functions_count].type_index;
1649 return obj.func_types[type_index];
1650 },
1651 }1511 }
1652}1512};
16531513
1654/// Returns the symbol index from a symbol of which its flag is set global,1514/// Index into `object_functions`, or null.
1655/// such as an exported or imported symbol.1515pub const OptionalObjectFunctionIndex = enum(u32) {
1656/// If the symbol does not yet exist, creates a new one symbol instead1516 none = std.math.maxInt(u32),
1657/// and then returns the index to it.1517 _,
1658pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !Symbol.Index {
1659 _ = lib_name;
1660 const name_index = try wasm.internString(name);
1661 return wasm.zig_object.?.getGlobalSymbol(wasm.base.comp.gpa, name_index);
1662}
16631518
1664/// For a given `Nav`, find the given symbol index's atom, and create a relocation for the type.1519 pub fn unwrap(i: OptionalObjectFunctionIndex) ?ObjectFunctionIndex {
1665/// Returns the given pointer address1520 if (i == .none) return null;
1666pub fn getNavVAddr(1521 return @enumFromInt(@intFromEnum(i));
1667 wasm: *Wasm,1522 }
1668 pt: Zcu.PerThread,1523};
1669 nav: InternPool.Nav.Index,
1670 reloc_info: link.File.RelocInfo,
1671) !u64 {
1672 return wasm.zig_object.?.getNavVAddr(wasm, pt, nav, reloc_info);
1673}
16741524
1675pub fn lowerUav(1525pub const ObjectDataSegment = extern struct {
1676 wasm: *Wasm,1526 /// `none` if segment info custom subsection is missing.
1677 pt: Zcu.PerThread,1527 name: OptionalString,
1678 uav: InternPool.Index,1528 flags: Flags,
1679 explicit_alignment: Alignment,1529 payload: DataPayload,
1680 src_loc: Zcu.LazySrcLoc,1530 offset: u32,
1681) !codegen.GenResult {1531 object_index: ObjectIndex,
1682 return wasm.zig_object.?.lowerUav(wasm, pt, uav, explicit_alignment, src_loc);1532
1683}1533 pub const Flags = packed struct(u32) {
1534 alive: bool = false,
1535 is_passive: bool = false,
1536 alignment: Alignment = .none,
1537 /// Signals that the segment contains only null terminated strings allowing
1538 /// the linker to perform merging.
1539 strings: bool = false,
1540 /// The segment contains thread-local data. This means that a unique copy
1541 /// of this segment will be created for each thread.
1542 tls: bool = false,
1543 /// If the object file is included in the final link, the segment should be
1544 /// retained in the final output regardless of whether it is used by the
1545 /// program.
1546 retain: bool = false,
1547
1548 _: u21 = 0,
1549 };
16841550
1685pub fn getUavVAddr(wasm: *Wasm, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {1551 /// Index into `Wasm.object_data_segments`.
1686 return wasm.zig_object.?.getUavVAddr(wasm, uav, reloc_info);1552 pub const Index = enum(u32) {
1687}1553 _,
16881554
1689pub fn deleteExport(1555 pub fn ptr(i: Index, wasm: *const Wasm) *ObjectDataSegment {
1690 wasm: *Wasm,1556 return &wasm.object_data_segments.items[@intFromEnum(i)];
1691 exported: Zcu.Exported,1557 }
1692 name: InternPool.NullTerminatedString,1558 };
1693) void {
1694 if (wasm.llvm_object) |_| return;
1695 return wasm.zig_object.?.deleteExport(wasm, exported, name);
1696}
16971559
1698pub fn updateExports(1560 pub fn relocations(ods: *const ObjectDataSegment, wasm: *const Wasm) ObjectRelocation.IterableSlice {
1699 wasm: *Wasm,1561 const data_section_index = ods.object_index.ptr(wasm).data_section_index.?;
1700 pt: Zcu.PerThread,1562 const relocs = wasm.object_relocations_table.get(data_section_index) orelse return .empty;
1701 exported: Zcu.Exported,1563 return .init(relocs, ods.offset, ods.payload.len, wasm);
1702 export_indices: []const u32,
1703) !void {
1704 if (build_options.skip_non_native and builtin.object_format != .wasm) {
1705 @panic("Attempted to compile for object format that was disabled by build configuration");
1706 }1564 }
1707 if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices);1565};
1708 return wasm.zig_object.?.updateExports(wasm, pt, exported, export_indices);
1709}
17101566
1711pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {1567/// A local or exported global const from an object file.
1712 if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);1568pub const ObjectData = extern struct {
1713 return wasm.zig_object.?.freeDecl(wasm, decl_index);1569 segment: ObjectDataSegment.Index,
1714}1570 /// Index into the object segment payload. Must be <= the segment's size.
1571 offset: u32,
1572 /// May be zero. `offset + size` must be <= the segment's size.
1573 size: u32,
1574 name: String,
1575 flags: SymbolFlags,
17151576
1716/// Assigns indexes to all indirect functions.1577 /// Index into `Wasm.object_datas`.
1717/// Starts at offset 1, where the value `0` represents an unresolved function pointer1578 pub const Index = enum(u32) {
1718/// or null-pointer1579 _,
1719fn mapFunctionTable(wasm: *Wasm) void {1580
1720 var it = wasm.function_table.iterator();1581 pub fn ptr(i: Index, wasm: *const Wasm) *ObjectData {
1721 var index: u32 = 1;1582 return &wasm.object_datas.items[@intFromEnum(i)];
1722 while (it.next()) |entry| {
1723 const symbol = wasm.symbolLocSymbol(entry.key_ptr.*);
1724 if (symbol.isAlive()) {
1725 entry.value_ptr.* = index;
1726 index += 1;
1727 } else {
1728 wasm.function_table.removeByPtr(entry.key_ptr);
1729 }1583 }
1730 }1584 };
1585};
17311586
1732 if (wasm.import_table or wasm.base.comp.config.output_mode == .Obj) {1587pub const ObjectDataImport = extern struct {
1733 const sym_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?;1588 resolution: Resolution,
1734 const import = wasm.imports.getPtr(sym_loc).?;1589 flags: SymbolFlags,
1735 import.kind.table.limits.min = index - 1; // we start at index 1.1590 source_location: SourceLocation,
1736 } else if (index > 1) {1591
1737 log.debug("Appending indirect function table", .{});1592 pub const Resolution = enum(u32) {
1738 const sym_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?;1593 unresolved,
1739 const symbol = wasm.symbolLocSymbol(sym_loc);1594 __zig_error_names,
1740 const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count];1595 __zig_error_name_table,
1741 table.limits = .{ .min = index, .max = index, .flags = 0x1 };1596 __heap_base,
1742 }1597 __heap_end,
1743}1598 /// Next, an `ObjectData.Index`.
1599 /// Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object.
1600 /// Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object.
1601 _,
17441602
1745/// From a given index, append the given `Atom` at the back of the linked list.1603 const first_object = @intFromEnum(Resolution.__heap_end) + 1;
1746/// Simply inserts it into the map of atoms when it doesn't exist yet.1604
1747pub fn appendAtomAtIndex(wasm: *Wasm, index: Segment.Index, atom_index: Atom.Index) !void {1605 pub const Unpacked = union(enum) {
1748 const gpa = wasm.base.comp.gpa;1606 unresolved,
1749 const atom = wasm.getAtomPtr(atom_index);1607 __zig_error_names,
1750 if (wasm.atoms.getPtr(index)) |last_index_ptr| {1608 __zig_error_name_table,
1751 atom.prev = last_index_ptr.*;1609 __heap_base,
1752 last_index_ptr.* = atom_index;1610 __heap_end,
1753 } else {1611 object: ObjectData.Index,
1754 try wasm.atoms.putNoClobber(gpa, index, atom_index);1612 uav_exe: UavsExeIndex,
1755 }1613 uav_obj: UavsObjIndex,
1756}1614 nav_exe: NavsExeIndex,
1615 nav_obj: NavsObjIndex,
1616 };
17571617
1758fn allocateAtoms(wasm: *Wasm) !void {1618 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
1759 // first sort the data segments1619 return switch (r) {
1760 try sortDataSegments(wasm);1620 .unresolved => .unresolved,
17611621 .__zig_error_names => .__zig_error_names,
1762 var it = wasm.atoms.iterator();1622 .__zig_error_name_table => .__zig_error_name_table,
1763 while (it.next()) |entry| {1623 .__heap_base => .__heap_base,
1764 const segment = wasm.segmentPtr(entry.key_ptr.*);1624 .__heap_end => .__heap_end,
1765 var atom_index = entry.value_ptr.*;1625 _ => {
1766 if (entry.key_ptr.toOptional() == wasm.code_section_index) {1626 const object_index = @intFromEnum(r) - first_object;
1767 // Code section is allocated upon writing as they are required to be ordered1627
1768 // to synchronise with the function section.1628 const uav_index = if (object_index < wasm.object_datas.items.len)
1769 continue;1629 return .{ .object = @enumFromInt(object_index) }
1770 }1630 else
1771 var offset: u32 = 0;1631 object_index - wasm.object_datas.items.len;
1772 while (true) {1632
1773 const atom = wasm.getAtomPtr(atom_index);1633 const comp = wasm.base.comp;
1774 const symbol_loc = atom.symbolLoc();1634 const is_obj = comp.config.output_mode == .Obj;
1775 // Ensure we get the original symbol, so we verify the correct symbol on whether1635 if (is_obj) {
1776 // it is dead or not and ensure an atom is removed when dead.1636 const nav_index = if (uav_index < wasm.uavs_obj.entries.len)
1777 // This is required as we may have parsed aliases into atoms.1637 return .{ .uav_obj = @enumFromInt(uav_index) }
1778 const sym = switch (symbol_loc.file) {1638 else
1779 .zig_object => wasm.zig_object.?.symbols.items[@intFromEnum(symbol_loc.index)],1639 uav_index - wasm.uavs_obj.entries.len;
1780 .none => wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)],1640
1781 _ => wasm.objects.items[@intFromEnum(symbol_loc.file)].symtable[@intFromEnum(symbol_loc.index)],1641 return .{ .nav_obj = @enumFromInt(nav_index) };
1642 } else {
1643 const nav_index = if (uav_index < wasm.uavs_exe.entries.len)
1644 return .{ .uav_exe = @enumFromInt(uav_index) }
1645 else
1646 uav_index - wasm.uavs_exe.entries.len;
1647
1648 return .{ .nav_exe = @enumFromInt(nav_index) };
1649 }
1650 },
1782 };1651 };
1652 }
17831653
1784 // Dead symbols must be unlinked from the linked-list to prevent them1654 pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution {
1785 // from being emit into the binary.1655 return switch (unpacked) {
1786 if (sym.isDead()) {1656 .unresolved => .unresolved,
1787 if (entry.value_ptr.* == atom_index and atom.prev != .null) {1657 .__zig_error_names => .__zig_error_names,
1788 // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update1658 .__zig_error_name_table => .__zig_error_name_table,
1789 // the entry to point it to the previous atom to ensure we do not start with a dead symbol that1659 .__heap_base => .__heap_base,
1790 // was removed and therefore do not emit any code at all.1660 .__heap_end => .__heap_end,
1791 entry.value_ptr.* = atom.prev;1661 .object => |i| @enumFromInt(first_object + @intFromEnum(i)),
1792 }1662 inline .uav_exe, .uav_obj => |i| @enumFromInt(first_object + wasm.object_datas.items.len + @intFromEnum(i)),
1793 if (atom.prev == .null) break;1663 .nav_exe => |i| @enumFromInt(first_object + wasm.object_datas.items.len + wasm.uavs_exe.entries.len + @intFromEnum(i)),
1794 atom_index = atom.prev;1664 .nav_obj => |i| @enumFromInt(first_object + wasm.object_datas.items.len + wasm.uavs_obj.entries.len + @intFromEnum(i)),
1795 atom.prev = .null;1665 };
1796 continue;
1797 }
1798 offset = @intCast(atom.alignment.forward(offset));
1799 atom.offset = offset;
1800 log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{
1801 wasm.symbolLocName(symbol_loc),
1802 offset,
1803 offset + atom.size,
1804 atom.size,
1805 });
1806 offset += atom.size;
1807 if (atom.prev == .null) break;
1808 atom_index = atom.prev;
1809 }1666 }
1810 segment.size = @intCast(segment.alignment.forward(offset));
1811 }
1812}
18131667
1814/// For each data symbol, sets the virtual address.1668 pub fn fromObjectDataIndex(wasm: *const Wasm, object_data_index: ObjectData.Index) Resolution {
1815fn allocateVirtualAddresses(wasm: *Wasm) void {1669 return pack(wasm, .{ .object = object_data_index });
1816 for (wasm.resolved_symbols.keys()) |loc| {1670 }
1817 const symbol = wasm.symbolLocSymbol(loc);
1818 if (symbol.tag != .data or symbol.isDead()) {
1819 // Only data symbols have virtual addresses.
1820 // Dead symbols do not get allocated, so we don't need to set their virtual address either.
1821 continue;
1822 }
1823 const atom_index = wasm.symbol_atom.get(loc) orelse {
1824 // synthetic symbol that does not contain an atom
1825 continue;
1826 };
18271671
1828 const atom = wasm.getAtom(atom_index);1672 pub fn objectDataSegment(r: Resolution, wasm: *const Wasm) ?ObjectDataSegment.Index {
1829 const merge_segment = wasm.base.comp.config.output_mode != .Obj;1673 return switch (unpack(r, wasm)) {
1830 const segment_info = switch (atom.file) {1674 .unresolved => unreachable,
1831 .zig_object => wasm.zig_object.?.segment_info.items,1675 .object => |i| i.ptr(wasm).segment,
1832 .none => wasm.segment_info.values(),1676 .__zig_error_names,
1833 _ => wasm.objects.items[@intFromEnum(atom.file)].segment_info,1677 .__zig_error_name_table,
1834 };1678 .__heap_base,
1835 const segment_name = segment_info[symbol.index].outputName(merge_segment);1679 .__heap_end,
1836 const segment_index = wasm.data_segments.get(segment_name).?;1680 .uav_exe,
1837 const segment = wasm.segmentPtr(segment_index);1681 .uav_obj,
18381682 .nav_exe,
1839 // TLS symbols have their virtual address set relative to their own TLS segment,1683 .nav_obj,
1840 // rather than the entire Data section.1684 => null,
1841 if (symbol.hasFlag(.WASM_SYM_TLS)) {1685 };
1842 symbol.virtual_address = atom.offset;
1843 } else {
1844 symbol.virtual_address = atom.offset + segment.offset;
1845 }1686 }
1846 }
1847}
18481687
1849fn sortDataSegments(wasm: *Wasm) !void {1688 pub fn dataLoc(r: Resolution, wasm: *const Wasm) DataLoc {
1850 const gpa = wasm.base.comp.gpa;1689 return switch (unpack(r, wasm)) {
1851 var new_mapping: std.StringArrayHashMapUnmanaged(Segment.Index) = .empty;1690 .unresolved => unreachable,
1852 try new_mapping.ensureUnusedCapacity(gpa, wasm.data_segments.count());1691 .object => |i| {
1853 errdefer new_mapping.deinit(gpa);1692 const ptr = i.ptr(wasm);
1693 return .{
1694 .segment = .fromObjectDataSegment(wasm, ptr.segment),
1695 .offset = ptr.offset,
1696 };
1697 },
1698 .__zig_error_names => .{ .segment = .__zig_error_names, .offset = 0 },
1699 .__zig_error_name_table => .{ .segment = .__zig_error_name_table, .offset = 0 },
1700 .__heap_base => .{ .segment = .__heap_base, .offset = 0 },
1701 .__heap_end => .{ .segment = .__heap_end, .offset = 0 },
1702 .uav_exe => @panic("TODO"),
1703 .uav_obj => @panic("TODO"),
1704 .nav_exe => @panic("TODO"),
1705 .nav_obj => @panic("TODO"),
1706 };
1707 }
1708 };
18541709
1855 const keys = try gpa.dupe([]const u8, wasm.data_segments.keys());1710 /// Points into `Wasm.object_data_imports`.
1856 defer gpa.free(keys);1711 pub const Index = enum(u32) {
1712 _,
18571713
1858 const SortContext = struct {1714 pub fn value(i: @This(), wasm: *const Wasm) *ObjectDataImport {
1859 fn sort(_: void, lhs: []const u8, rhs: []const u8) bool {1715 return &wasm.object_data_imports.values()[@intFromEnum(i)];
1860 return order(lhs) < order(rhs);
1861 }1716 }
18621717
1863 fn order(name: []const u8) u8 {1718 pub fn fromSymbolName(wasm: *const Wasm, name: String) ?Index {
1864 if (mem.startsWith(u8, name, ".rodata")) return 0;1719 return @enumFromInt(wasm.object_data_imports.getIndex(name) orelse return null);
1865 if (mem.startsWith(u8, name, ".data")) return 1;
1866 if (mem.startsWith(u8, name, ".text")) return 2;
1867 return 3;
1868 }1720 }
1869 };1721 };
1722};
18701723
1871 mem.sort([]const u8, keys, {}, SortContext.sort);1724pub const DataPayload = extern struct {
1872 for (keys) |key| {1725 off: Off,
1873 const segment_index = wasm.data_segments.get(key).?;1726 /// The size in bytes of the data representing the segment within the section.
1874 new_mapping.putAssumeCapacity(key, segment_index);1727 len: u32,
1875 }
1876 wasm.data_segments.deinit(gpa);
1877 wasm.data_segments = new_mapping;
1878}
18791728
1880/// Obtains all initfuncs from each object file, verifies its function signature,1729 pub const Off = enum(u32) {
1881/// and then appends it to our final `init_funcs` list.1730 /// The payload is all zeroes (bss section).
1882/// After all functions have been inserted, the functions will be ordered based1731 none = std.math.maxInt(u32),
1883/// on their priority.1732 /// Points into string_bytes. No corresponding string_table entry.
1884/// NOTE: This function must be called before we merged any other section.1733 _,
1885/// This is because all init funcs in the object files contain references to the
1886/// original functions and their types. We need to know the type to verify it doesn't
1887/// contain any parameters.
1888fn setupInitFunctions(wasm: *Wasm) !void {
1889 const gpa = wasm.base.comp.gpa;
1890 const diags = &wasm.base.comp.link_diags;
1891 // There's no constructors for Zig so we can simply search through linked object files only.
1892 for (wasm.objects.items, 0..) |*object, object_index| {
1893 try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len);
1894 for (object.init_funcs) |init_func| {
1895 const symbol = object.symtable[init_func.symbol_index];
1896 const ty: std.wasm.Type = if (symbol.isUndefined()) ty: {
1897 const imp: Import = object.findImport(symbol);
1898 break :ty object.func_types[imp.kind.function];
1899 } else ty: {
1900 const func_index = symbol.index - object.imported_functions_count;
1901 const func = object.functions[func_index];
1902 break :ty object.func_types[func.type_index];
1903 };
1904 if (ty.params.len != 0) {
1905 var err = try diags.addErrorWithNotes(0);
1906 try err.addMsg("constructor functions cannot take arguments: '{s}'", .{wasm.stringSlice(symbol.name)});
1907 }
1908 log.debug("appended init func '{s}'\n", .{wasm.stringSlice(symbol.name)});
1909 wasm.init_funcs.appendAssumeCapacity(.{
1910 .index = @enumFromInt(init_func.symbol_index),
1911 .file = @enumFromInt(object_index),
1912 .priority = init_func.priority,
1913 });
1914 try wasm.mark(.{
1915 .index = @enumFromInt(init_func.symbol_index),
1916 .file = @enumFromInt(object_index),
1917 });
1918 }
1919 }
19201734
1921 // sort the initfunctions based on their priority1735 pub fn unwrap(off: Off) ?u32 {
1922 mem.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan);1736 return if (off == .none) null else @intFromEnum(off);
1737 }
1738 };
19231739
1924 if (wasm.init_funcs.items.len > 0) {1740 pub fn slice(p: DataPayload, wasm: *const Wasm) []const u8 {
1925 const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_call_ctors).?;1741 return wasm.string_bytes.items[p.off.unwrap().?..][0..p.len];
1926 try wasm.mark(loc);
1927 }1742 }
1928}1743};
19291744
1930/// Creates a function body for the `__wasm_call_ctors` symbol.1745/// A reference to a local or exported global const.
1931/// Loops over all constructors found in `init_funcs` and calls them1746pub const DataSegmentId = enum(u32) {
1932/// respectively based on their priority which was sorted by `setupInitFunctions`.1747 __zig_error_names,
1933/// NOTE: This function must be called after we merged all sections to ensure the1748 __zig_error_name_table,
1934/// references to the function stored in the symbol have been finalized so we end1749 /// All name string bytes for all `@tagName` implementations, concatenated together.
1935/// up calling the resolved function.1750 __zig_tag_names,
1936fn initializeCallCtorsFunction(wasm: *Wasm) !void {1751 /// All tag name slices for all `@tagName` implementations, concatenated together.
1937 const gpa = wasm.base.comp.gpa;1752 __zig_tag_name_table,
1938 // No code to emit, so also no ctors to call1753 /// This and `__heap_end` are better retrieved via a global, but there is
1939 if (wasm.code_section_index == .none) {1754 /// some suboptimal code out there (wasi libc) that additionally needs them
1940 // Make sure to remove it from the resolved symbols so we do not emit1755 /// as data symbols.
1941 // it within any section. TODO: Remove this once we implement garbage collection.1756 __heap_base,
1942 const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_call_ctors).?;1757 __heap_end,
1943 assert(wasm.resolved_symbols.swapRemove(loc));1758 /// First, an `ObjectDataSegment.Index`.
1944 return;1759 /// Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object.
1945 }1760 /// Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object.
1761 _,
19461762
1947 var function_body = std.ArrayList(u8).init(gpa);1763 const first_object = @intFromEnum(DataSegmentId.__heap_end) + 1;
1948 defer function_body.deinit();
1949 const writer = function_body.writer();
19501764
1951 // Create the function body1765 pub const Category = enum {
1952 {1766 /// Thread-local variables.
1953 // Write locals count (we have none)1767 tls,
1954 try leb.writeUleb128(writer, @as(u32, 0));1768 /// Data that is not zero initialized and not threadlocal.
1769 data,
1770 /// Zero-initialized. Does not require corresponding bytes in the
1771 /// output file.
1772 zero,
1773 };
19551774
1956 // call constructors1775 pub const Unpacked = union(enum) {
1957 for (wasm.init_funcs.items) |init_func_loc| {1776 __zig_error_names,
1958 const symbol = init_func_loc.getSymbol(wasm);1777 __zig_error_name_table,
1959 const func = wasm.functions.values()[symbol.index - wasm.imported_functions_count].func;1778 __zig_tag_names,
1960 const ty = wasm.func_types.items[func.type_index];1779 __zig_tag_name_table,
1780 __heap_base,
1781 __heap_end,
1782 object: ObjectDataSegment.Index,
1783 uav_exe: UavsExeIndex,
1784 uav_obj: UavsObjIndex,
1785 nav_exe: NavsExeIndex,
1786 nav_obj: NavsObjIndex,
1787 };
19611788
1962 // Call function by its function index1789 pub fn pack(wasm: *const Wasm, unpacked: Unpacked) DataSegmentId {
1963 try writer.writeByte(std.wasm.opcode(.call));1790 return switch (unpacked) {
1964 try leb.writeUleb128(writer, symbol.index);1791 .__zig_error_names => .__zig_error_names,
1792 .__zig_error_name_table => .__zig_error_name_table,
1793 .__zig_tag_names => .__zig_tag_names,
1794 .__zig_tag_name_table => .__zig_tag_name_table,
1795 .__heap_base => .__heap_base,
1796 .__heap_end => .__heap_end,
1797 .object => |i| @enumFromInt(first_object + @intFromEnum(i)),
1798 inline .uav_exe, .uav_obj => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + @intFromEnum(i)),
1799 .nav_exe => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + wasm.uavs_exe.entries.len + @intFromEnum(i)),
1800 .nav_obj => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + wasm.uavs_obj.entries.len + @intFromEnum(i)),
1801 };
1802 }
19651803
1966 // drop all returned values from the stack as __wasm_call_ctors has no return value1804 pub fn unpack(id: DataSegmentId, wasm: *const Wasm) Unpacked {
1967 for (ty.returns) |_| {1805 return switch (id) {
1968 try writer.writeByte(std.wasm.opcode(.drop));1806 .__zig_error_names => .__zig_error_names,
1969 }1807 .__zig_error_name_table => .__zig_error_name_table,
1970 }1808 .__zig_tag_names => .__zig_tag_names,
1809 .__zig_tag_name_table => .__zig_tag_name_table,
1810 .__heap_base => .__heap_base,
1811 .__heap_end => .__heap_end,
1812 _ => {
1813 const object_index = @intFromEnum(id) - first_object;
19711814
1972 // End function body1815 const uav_index = if (object_index < wasm.object_data_segments.items.len)
1973 try writer.writeByte(std.wasm.opcode(.end));1816 return .{ .object = @enumFromInt(object_index) }
1974 }1817 else
1818 object_index - wasm.object_data_segments.items.len;
19751819
1976 try wasm.createSyntheticFunction(1820 const comp = wasm.base.comp;
1977 wasm.preloaded_strings.__wasm_call_ctors,1821 const is_obj = comp.config.output_mode == .Obj;
1978 std.wasm.Type{ .params = &.{}, .returns = &.{} },1822 if (is_obj) {
1979 &function_body,1823 const nav_index = if (uav_index < wasm.uavs_obj.entries.len)
1980 );1824 return .{ .uav_obj = @enumFromInt(uav_index) }
1981}1825 else
1826 uav_index - wasm.uavs_obj.entries.len;
19821827
1983fn createSyntheticFunction(1828 return .{ .nav_obj = @enumFromInt(nav_index) };
1984 wasm: *Wasm,1829 } else {
1985 symbol_name: String,1830 const nav_index = if (uav_index < wasm.uavs_exe.entries.len)
1986 func_ty: std.wasm.Type,1831 return .{ .uav_exe = @enumFromInt(uav_index) }
1987 function_body: *std.ArrayList(u8),1832 else
1988) !void {1833 uav_index - wasm.uavs_exe.entries.len;
1989 const gpa = wasm.base.comp.gpa;1834
1990 const loc = wasm.globals.get(symbol_name).?;1835 return .{ .nav_exe = @enumFromInt(nav_index) };
1991 const symbol = wasm.symbolLocSymbol(loc);1836 }
1992 if (symbol.isDead()) {1837 },
1993 return;1838 };
1994 }1839 }
1995 const ty_index = try wasm.putOrGetFuncType(func_ty);
1996 // create function with above type
1997 const func_index = wasm.imported_functions_count + @as(u32, @intCast(wasm.functions.count()));
1998 try wasm.functions.putNoClobber(
1999 gpa,
2000 .{ .file = .none, .index = func_index },
2001 .{ .func = .{ .type_index = ty_index }, .sym_index = loc.index },
2002 );
2003 symbol.index = func_index;
2004
2005 // create the atom that will be output into the final binary
2006 const atom_index = try wasm.createAtom(loc.index, .none);
2007 const atom = wasm.getAtomPtr(atom_index);
2008 atom.size = @intCast(function_body.items.len);
2009 atom.code = function_body.moveToUnmanaged();
2010 try wasm.appendAtomAtIndex(wasm.code_section_index.unwrap().?, atom_index);
2011}
20121840
2013/// Unlike `createSyntheticFunction` this function is to be called by1841 pub fn fromNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) DataSegmentId {
2014/// the codegeneration backend. This will not allocate the created Atom yet.1842 const comp = wasm.base.comp;
2015/// Returns the index of the symbol.1843 const is_obj = comp.config.output_mode == .Obj;
2016pub fn createFunction(1844 return pack(wasm, if (is_obj) .{
2017 wasm: *Wasm,1845 .nav_obj = @enumFromInt(wasm.navs_obj.getIndex(nav_index).?),
2018 symbol_name: []const u8,1846 } else .{
2019 func_ty: std.wasm.Type,1847 .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(nav_index).?),
2020 function_body: *std.ArrayList(u8),1848 });
2021 relocations: *std.ArrayList(Relocation),1849 }
2022) !Symbol.Index {
2023 return wasm.zig_object.?.createFunction(wasm, symbol_name, func_ty, function_body, relocations);
2024}
20251850
2026/// If required, sets the function index in the `start` section.1851 pub fn fromObjectDataSegment(wasm: *const Wasm, object_data_segment: ObjectDataSegment.Index) DataSegmentId {
2027fn setupStartSection(wasm: *Wasm) !void {1852 return pack(wasm, .{ .object = object_data_segment });
2028 if (wasm.globals.get(wasm.preloaded_strings.__wasm_init_memory)) |loc| {
2029 wasm.entry = wasm.symbolLocSymbol(loc).index;
2030 }1853 }
2031}
20321854
2033fn initializeTLSFunction(wasm: *Wasm) !void {1855 pub fn category(id: DataSegmentId, wasm: *const Wasm) Category {
2034 const comp = wasm.base.comp;1856 return switch (unpack(id, wasm)) {
2035 const gpa = comp.gpa;1857 .__zig_error_names,
2036 const shared_memory = comp.config.shared_memory;1858 .__zig_error_name_table,
1859 .__zig_tag_names,
1860 .__zig_tag_name_table,
1861 .__heap_base,
1862 .__heap_end,
1863 => .data,
20371864
2038 if (!shared_memory) return;1865 .object => |i| {
1866 const ptr = i.ptr(wasm);
1867 if (ptr.flags.tls) return .tls;
1868 if (wasm.isBss(ptr.name)) return .zero;
1869 return .data;
1870 },
1871 inline .uav_exe, .uav_obj => |i| if (i.value(wasm).code.off == .none) .zero else .data,
1872 inline .nav_exe, .nav_obj => |i| {
1873 const zcu = wasm.base.comp.zcu.?;
1874 const ip = &zcu.intern_pool;
1875 const nav = ip.getNav(i.key(wasm).*);
1876 if (nav.isThreadlocal(ip)) return .tls;
1877 const code = i.value(wasm).code;
1878 return if (code.off == .none) .zero else .data;
1879 },
1880 };
1881 }
20391882
2040 // ensure function is marked as we must emit it1883 pub fn isTls(id: DataSegmentId, wasm: *const Wasm) bool {
2041 wasm.symbolLocSymbol(wasm.globals.get(wasm.preloaded_strings.__wasm_init_tls).?).mark();1884 return switch (unpack(id, wasm)) {
1885 .__zig_error_names,
1886 .__zig_error_name_table,
1887 .__zig_tag_names,
1888 .__zig_tag_name_table,
1889 .__heap_base,
1890 .__heap_end,
1891 => false,
1892
1893 .object => |i| i.ptr(wasm).flags.tls,
1894 .uav_exe, .uav_obj => false,
1895 inline .nav_exe, .nav_obj => |i| {
1896 const zcu = wasm.base.comp.zcu.?;
1897 const ip = &zcu.intern_pool;
1898 const nav = ip.getNav(i.key(wasm).*);
1899 return nav.isThreadlocal(ip);
1900 },
1901 };
1902 }
20421903
2043 var function_body = std.ArrayList(u8).init(gpa);1904 pub fn isBss(id: DataSegmentId, wasm: *const Wasm) bool {
2044 defer function_body.deinit();1905 return id.category(wasm) == .zero;
2045 const writer = function_body.writer();1906 }
1907
1908 pub fn name(id: DataSegmentId, wasm: *const Wasm) []const u8 {
1909 return switch (unpack(id, wasm)) {
1910 .__zig_error_names,
1911 .__zig_error_name_table,
1912 .__zig_tag_names,
1913 .__zig_tag_name_table,
1914 .uav_exe,
1915 .uav_obj,
1916 .__heap_base,
1917 .__heap_end,
1918 => ".data",
1919
1920 .object => |i| i.ptr(wasm).name.unwrap().?.slice(wasm),
1921 inline .nav_exe, .nav_obj => |i| {
1922 const zcu = wasm.base.comp.zcu.?;
1923 const ip = &zcu.intern_pool;
1924 const nav = ip.getNav(i.key(wasm).*);
1925 return nav.getLinkSection().toSlice(ip) orelse switch (category(id, wasm)) {
1926 .tls => ".tdata",
1927 .data => ".data",
1928 .zero => ".bss",
1929 };
1930 },
1931 };
1932 }
20461933
2047 // locals1934 pub fn alignment(id: DataSegmentId, wasm: *const Wasm) Alignment {
2048 try writer.writeByte(0);1935 return switch (unpack(id, wasm)) {
1936 .__zig_error_names, .__zig_tag_names => .@"1",
1937 .__zig_error_name_table, .__zig_tag_name_table, .__heap_base, .__heap_end => wasm.pointerAlignment(),
1938 .object => |i| i.ptr(wasm).flags.alignment,
1939 inline .uav_exe, .uav_obj => |i| {
1940 const zcu = wasm.base.comp.zcu.?;
1941 const ip = &zcu.intern_pool;
1942 const ip_index = i.key(wasm).*;
1943 if (wasm.overaligned_uavs.get(ip_index)) |a| return a;
1944 const ty: Zcu.Type = .fromInterned(ip.typeOf(ip_index));
1945 const result = ty.abiAlignment(zcu);
1946 assert(result != .none);
1947 return result;
1948 },
1949 inline .nav_exe, .nav_obj => |i| {
1950 const zcu = wasm.base.comp.zcu.?;
1951 const ip = &zcu.intern_pool;
1952 const nav = ip.getNav(i.key(wasm).*);
1953 const explicit = nav.getAlignment();
1954 if (explicit != .none) return explicit;
1955 const ty: Zcu.Type = .fromInterned(nav.typeOf(ip));
1956 const result = ty.abiAlignment(zcu);
1957 assert(result != .none);
1958 return result;
1959 },
1960 };
1961 }
20491962
2050 // If there's a TLS segment, initialize it during runtime using the bulk-memory feature1963 pub fn refCount(id: DataSegmentId, wasm: *const Wasm) u32 {
2051 if (wasm.data_segments.getIndex(".tdata")) |data_index| {1964 return switch (unpack(id, wasm)) {
2052 const segment_index = wasm.data_segments.entries.items(.value)[data_index];1965 .__zig_error_names => @intCast(wasm.error_name_offs.items.len),
2053 const segment = wasm.segmentPtr(segment_index);1966 .__zig_error_name_table => wasm.error_name_table_ref_count,
1967 .__zig_tag_names => @intCast(wasm.tag_name_offs.items.len),
1968 .__zig_tag_name_table => wasm.tag_name_table_ref_count,
1969 .object, .uav_obj, .nav_obj, .__heap_base, .__heap_end => 0,
1970 inline .uav_exe, .nav_exe => |i| i.value(wasm).count,
1971 };
1972 }
20541973
2055 const param_local: u32 = 0;1974 pub fn isPassive(id: DataSegmentId, wasm: *const Wasm) bool {
1975 const comp = wasm.base.comp;
1976 if (comp.config.import_memory) return true;
1977 return switch (unpack(id, wasm)) {
1978 .__zig_error_names,
1979 .__zig_error_name_table,
1980 .__zig_tag_names,
1981 .__zig_tag_name_table,
1982 .__heap_base,
1983 .__heap_end,
1984 => false,
1985
1986 .object => |i| i.ptr(wasm).flags.is_passive,
1987 .uav_exe, .uav_obj, .nav_exe, .nav_obj => false,
1988 };
1989 }
20561990
2057 try writer.writeByte(std.wasm.opcode(.local_get));1991 pub fn isEmpty(id: DataSegmentId, wasm: *const Wasm) bool {
2058 try leb.writeUleb128(writer, param_local);1992 return switch (unpack(id, wasm)) {
1993 .__zig_error_names,
1994 .__zig_error_name_table,
1995 .__zig_tag_names,
1996 .__zig_tag_name_table,
1997 .__heap_base,
1998 .__heap_end,
1999 => false,
20592000
2060 const tls_base_loc = wasm.globals.get(wasm.preloaded_strings.__tls_base).?;2001 .object => |i| i.ptr(wasm).payload.off == .none,
2061 try writer.writeByte(std.wasm.opcode(.global_set));2002 inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.off == .none,
2062 try leb.writeUleb128(writer, wasm.symbolLocSymbol(tls_base_loc).index);2003 };
2004 }
20632005
2064 // load stack values for the bulk-memory operation2006 pub fn size(id: DataSegmentId, wasm: *const Wasm) u32 {
2065 {2007 return switch (unpack(id, wasm)) {
2066 try writer.writeByte(std.wasm.opcode(.local_get));2008 .__zig_error_names => @intCast(wasm.error_name_bytes.items.len),
2067 try leb.writeUleb128(writer, param_local);2009 .__zig_error_name_table => {
2010 const comp = wasm.base.comp;
2011 const zcu = comp.zcu.?;
2012 const errors_len = wasm.error_name_offs.items.len;
2013 const elem_size = Zcu.Type.slice_const_u8_sentinel_0.abiSize(zcu);
2014 return @intCast(errors_len * elem_size);
2015 },
2016 .__zig_tag_names => @intCast(wasm.tag_name_bytes.items.len),
2017 .__zig_tag_name_table => {
2018 const comp = wasm.base.comp;
2019 const zcu = comp.zcu.?;
2020 const table_len = wasm.tag_name_offs.items.len;
2021 const elem_size = Zcu.Type.slice_const_u8_sentinel_0.abiSize(zcu);
2022 return @intCast(table_len * elem_size);
2023 },
2024 .__heap_base, .__heap_end => wasm.pointerSize(),
2025 .object => |i| i.ptr(wasm).payload.len,
2026 inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.len,
2027 };
2028 }
2029};
20682030
2069 try writer.writeByte(std.wasm.opcode(.i32_const));2031pub const DataLoc = struct {
2070 try leb.writeUleb128(writer, @as(u32, 0)); //segment offset2032 segment: Wasm.DataSegmentId,
2033 offset: u32,
20712034
2072 try writer.writeByte(std.wasm.opcode(.i32_const));2035 pub fn fromObjectDataIndex(wasm: *const Wasm, i: Wasm.ObjectData.Index) DataLoc {
2073 try leb.writeUleb128(writer, @as(u32, segment.size)); //segment offset2036 const ptr = i.ptr(wasm);
2074 }2037 return .{
2038 .segment = .fromObjectDataSegment(wasm, ptr.segment),
2039 .offset = ptr.offset,
2040 };
2041 }
20752042
2076 // perform the bulk-memory operation to initialize the data segment2043 pub fn fromDataImportId(wasm: *const Wasm, id: Wasm.DataImportId) DataLoc {
2077 try writer.writeByte(std.wasm.opcode(.misc_prefix));2044 return switch (id.unpack(wasm)) {
2078 try leb.writeUleb128(writer, std.wasm.miscOpcode(.memory_init));2045 .object_data_import => |i| .fromObjectDataImportIndex(wasm, i),
2079 // segment immediate2046 .zcu_import => |i| .fromZcuImport(wasm, i),
2080 try leb.writeUleb128(writer, @as(u32, @intCast(data_index)));2047 };
2081 // memory index immediate (always 0)
2082 try leb.writeUleb128(writer, @as(u32, 0));
2083 }2048 }
20842049
2085 // If we have to perform any TLS relocations, call the corresponding function2050 pub fn fromObjectDataImportIndex(wasm: *const Wasm, i: Wasm.ObjectDataImport.Index) DataLoc {
2086 // which performs all runtime TLS relocations. This is a synthetic function,2051 return i.value(wasm).resolution.dataLoc(wasm);
2087 // generated by the linker.
2088 if (wasm.globals.get(wasm.preloaded_strings.__wasm_apply_global_tls_relocs)) |loc| {
2089 try writer.writeByte(std.wasm.opcode(.call));
2090 try leb.writeUleb128(writer, wasm.symbolLocSymbol(loc).index);
2091 wasm.symbolLocSymbol(loc).mark();
2092 }2052 }
20932053
2094 try writer.writeByte(std.wasm.opcode(.end));2054 pub fn fromZcuImport(wasm: *const Wasm, zcu_import: ZcuImportIndex) DataLoc {
2055 const nav_index = zcu_import.ptr(wasm).*;
2056 return .{
2057 .segment = .fromNav(wasm, nav_index),
2058 .offset = 0,
2059 };
2060 }
2061};
20952062
2096 try wasm.createSyntheticFunction(2063/// Index into `Wasm.uavs`.
2097 wasm.preloaded_strings.__wasm_init_tls,2064pub const UavIndex = enum(u32) {
2098 std.wasm.Type{ .params = &.{.i32}, .returns = &.{} },2065 _,
2099 &function_body,2066};
2100 );
2101}
21022067
2103fn setupImports(wasm: *Wasm) !void {2068pub const CustomSegment = extern struct {
2104 const gpa = wasm.base.comp.gpa;2069 payload: Payload,
2105 log.debug("Merging imports", .{});2070 flags: SymbolFlags,
2106 for (wasm.resolved_symbols.keys()) |symbol_loc| {2071 section_name: String,
2107 const object_id = symbol_loc.file.unwrap() orelse {
2108 // Synthetic symbols will already exist in the `import` section
2109 continue;
2110 };
21112072
2112 const symbol = wasm.symbolLocSymbol(symbol_loc);2073 pub const Payload = DataPayload;
2113 if (symbol.isDead()) continue;2074};
2114 if (!symbol.requiresImport()) continue;2075
2115 if (symbol.name == wasm.preloaded_strings.__indirect_function_table) continue;2076/// An index into string_bytes where a wasm expression is found.
2077pub const Expr = enum(u32) {
2078 _,
21162079
2117 log.debug("Symbol '{s}' will be imported from the host", .{wasm.stringSlice(symbol.name)});2080 pub const end = @intFromEnum(std.wasm.Opcode.end);
2118 const import = objectImport(wasm, object_id, symbol_loc.index);
21192081
2120 // We copy the import to a new import to ensure the names contain references2082 pub fn slice(index: Expr, wasm: *const Wasm) [:end]const u8 {
2121 // to the internal string table, rather than of the object file.2083 const start_slice = wasm.string_bytes.items[@intFromEnum(index)..];
2122 const new_imp: Import = .{2084 const end_pos = Object.exprEndPos(start_slice, 0) catch |err| switch (err) {
2123 .module_name = import.module_name,2085 error.InvalidInitOpcode => unreachable,
2124 .name = import.name,
2125 .kind = import.kind,
2126 };2086 };
2127 // TODO: De-duplicate imports when they contain the same names and type2087 return start_slice[0..end_pos :end];
2128 try wasm.imports.putNoClobber(gpa, symbol_loc, new_imp);
2129 }
2130
2131 // Assign all indexes of the imports to their representing symbols
2132 var function_index: u32 = 0;
2133 var global_index: u32 = 0;
2134 var table_index: u32 = 0;
2135 var it = wasm.imports.iterator();
2136 while (it.next()) |entry| {
2137 const symbol = wasm.symbolLocSymbol(entry.key_ptr.*);
2138 const import: Import = entry.value_ptr.*;
2139 switch (import.kind) {
2140 .function => {
2141 symbol.index = function_index;
2142 function_index += 1;
2143 },
2144 .global => {
2145 symbol.index = global_index;
2146 global_index += 1;
2147 },
2148 .table => {
2149 symbol.index = table_index;
2150 table_index += 1;
2151 },
2152 else => unreachable,
2153 }
2154 }2088 }
2155 wasm.imported_functions_count = function_index;2089};
2156 wasm.imported_globals_count = global_index;
2157 wasm.imported_tables_count = table_index;
21582090
2159 log.debug("Merged ({d}) functions, ({d}) globals, and ({d}) tables into import section", .{2091pub const FunctionType = extern struct {
2160 function_index,2092 params: ValtypeList,
2161 global_index,2093 returns: ValtypeList,
2162 table_index,
2163 });
2164}
21652094
2166/// Takes the global, function and table section from each linked object file2095 /// Index into func_types
2167/// and merges it into a single section for each.2096 pub const Index = enum(u32) {
2168fn mergeSections(wasm: *Wasm) !void {2097 _,
2169 const gpa = wasm.base.comp.gpa;
21702098
2171 var removed_duplicates = std.ArrayList(SymbolLoc).init(gpa);2099 pub fn ptr(i: Index, wasm: *const Wasm) *FunctionType {
2172 defer removed_duplicates.deinit();2100 return &wasm.func_types.keys()[@intFromEnum(i)];
2101 }
21732102
2174 for (wasm.resolved_symbols.keys()) |sym_loc| {2103 pub fn fmt(i: Index, wasm: *const Wasm) Formatter {
2175 const object_id = sym_loc.file.unwrap() orelse {2104 return i.ptr(wasm).fmt(wasm);
2176 // Synthetic symbols already live in the corresponding sections.2105 }
2177 continue;2106 };
2178 };
21792107
2180 const symbol = objectSymbol(wasm, object_id, sym_loc.index);2108 pub const format = @compileError("can't format without *Wasm reference");
2181 if (symbol.isDead() or symbol.isUndefined()) {2109
2182 // Skip undefined symbols as they go in the `import` section2110 pub fn eql(a: FunctionType, b: FunctionType) bool {
2183 continue;2111 return a.params == b.params and a.returns == b.returns;
2184 }2112 }
21852113
2186 switch (symbol.tag) {2114 pub fn fmt(ft: FunctionType, wasm: *const Wasm) Formatter {
2187 .function => {2115 return .{ .wasm = wasm, .ft = ft };
2188 const gop = try wasm.functions.getOrPut(2116 }
2189 gpa,2117
2190 .{ .file = sym_loc.file, .index = symbol.index },2118 const Formatter = struct {
2191 );2119 wasm: *const Wasm,
2192 if (gop.found_existing) {2120 ft: FunctionType,
2193 // We found an alias to the same function, discard this symbol in favor of2121
2194 // the original symbol and point the discard function to it. This ensures2122 pub fn format(
2195 // we only emit a single function, instead of duplicates.2123 self: Formatter,
2196 // we favor keeping the global over a local.2124 comptime format_string: []const u8,
2197 const original_loc: SymbolLoc = .{ .file = gop.key_ptr.file, .index = gop.value_ptr.sym_index };2125 options: std.fmt.FormatOptions,
2198 const original_sym = wasm.symbolLocSymbol(original_loc);2126 writer: anytype,
2199 if (original_sym.isLocal() and symbol.isGlobal()) {2127 ) !void {
2200 original_sym.unmark();2128 if (format_string.len != 0) std.fmt.invalidFmtError(format_string, self);
2201 try wasm.discarded.put(gpa, original_loc, sym_loc);2129 _ = options;
2202 try removed_duplicates.append(original_loc);2130 const params = self.ft.params.slice(self.wasm);
2203 } else {2131 const returns = self.ft.returns.slice(self.wasm);
2204 symbol.unmark();2132
2205 try wasm.discarded.putNoClobber(gpa, sym_loc, original_loc);2133 try writer.writeByte('(');
2206 try removed_duplicates.append(sym_loc);2134 for (params, 0..) |param, i| {
2207 continue;2135 try writer.print("{s}", .{@tagName(param)});
2136 if (i + 1 != params.len) {
2137 try writer.writeAll(", ");
2138 }
2139 }
2140 try writer.writeAll(") -> ");
2141 if (returns.len == 0) {
2142 try writer.writeAll("nil");
2143 } else {
2144 for (returns, 0..) |return_ty, i| {
2145 try writer.print("{s}", .{@tagName(return_ty)});
2146 if (i + 1 != returns.len) {
2147 try writer.writeAll(", ");
2208 }2148 }
2209 }2149 }
2210 gop.value_ptr.* = .{2150 }
2211 .func = objectFunction(wasm, object_id, sym_loc.index),
2212 .sym_index = sym_loc.index,
2213 };
2214 symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count;
2215 },
2216 .global => {
2217 const index = symbol.index - objectImportedFunctions(wasm, object_id);
2218 const original_global = objectGlobals(wasm, object_id)[index];
2219 symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
2220 try wasm.wasm_globals.append(gpa, original_global);
2221 },
2222 .table => {
2223 const index = symbol.index - objectImportedFunctions(wasm, object_id);
2224 // assert it's a regular relocatable object file as `ZigObject` will never
2225 // contain a table.
2226 const original_table = wasm.objectById(object_id).?.tables[index];
2227 symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count;
2228 try wasm.tables.append(gpa, original_table);
2229 },
2230 .dead, .undefined => unreachable,
2231 else => {},
2232 }2151 }
2233 }2152 };
2153};
22342154
2235 // For any removed duplicates, remove them from the resolved symbols list2155/// Represents a function entry, holding the index to its type
2236 for (removed_duplicates.items) |sym_loc| {2156pub const Func = extern struct {
2237 assert(wasm.resolved_symbols.swapRemove(sym_loc));2157 type_index: FunctionType.Index,
2238 gc_log.debug("Removed duplicate for function '{s}'", .{wasm.symbolLocName(sym_loc)});2158};
2239 }
22402159
2241 log.debug("Merged ({d}) functions", .{wasm.functions.count()});2160/// Type reflection is used on the field names to autopopulate each field
2242 log.debug("Merged ({d}) globals", .{wasm.wasm_globals.items.len});2161/// during initialization.
2243 log.debug("Merged ({d}) tables", .{wasm.tables.items.len});2162const PreloadedStrings = struct {
2244}2163 __heap_base: String,
2164 __heap_end: String,
2165 __indirect_function_table: String,
2166 __linear_memory: String,
2167 __stack_pointer: String,
2168 __tls_align: String,
2169 __tls_base: String,
2170 __tls_size: String,
2171 __wasm_apply_global_tls_relocs: String,
2172 __wasm_call_ctors: String,
2173 __wasm_init_memory: String,
2174 __wasm_init_memory_flag: String,
2175 __wasm_init_tls: String,
2176 __zig_error_names: String,
2177 __zig_error_name_table: String,
2178 __zig_errors_len: String,
2179 _initialize: String,
2180 _start: String,
2181 memory: String,
2182};
22452183
2246/// Merges function types of all object files into the final2184/// Index into string_bytes
2247/// 'types' section, while assigning the type index to the representing2185pub const String = enum(u32) {
2248/// section (import, export, function).2186 _,
2249fn mergeTypes(wasm: *Wasm) !void {2187
2250 const gpa = wasm.base.comp.gpa;2188 const Table = std.HashMapUnmanaged(String, void, TableContext, std.hash_map.default_max_load_percentage);
2251 // A map to track which functions have already had their2189
2252 // type inserted. If we do this for the same function multiple times,2190 const TableContext = struct {
2253 // it will be overwritten with the incorrect type.2191 bytes: []const u8,
2254 var dirty = std.AutoHashMap(u32, void).init(gpa);
2255 try dirty.ensureUnusedCapacity(@as(u32, @intCast(wasm.functions.count())));
2256 defer dirty.deinit();
2257
2258 for (wasm.resolved_symbols.keys()) |sym_loc| {
2259 const object_id = sym_loc.file.unwrap() orelse {
2260 // zig code-generated symbols are already present in final type section
2261 continue;
2262 };
22632192
2264 const symbol = objectSymbol(wasm, object_id, sym_loc.index);2193 pub fn eql(_: @This(), a: String, b: String) bool {
2265 if (symbol.tag != .function or symbol.isDead()) {2194 return a == b;
2266 // Only functions have types. Only retrieve the type of referenced functions.
2267 continue;
2268 }2195 }
22692196
2270 if (symbol.isUndefined()) {2197 pub fn hash(ctx: @This(), key: String) u64 {
2271 log.debug("Adding type from extern function '{s}'", .{wasm.symbolLocName(sym_loc)});2198 return std.hash_map.hashString(mem.sliceTo(ctx.bytes[@intFromEnum(key)..], 0));
2272 const import: *Import = wasm.imports.getPtr(sym_loc) orelse continue;
2273 const original_type = objectFuncTypes(wasm, object_id)[import.kind.function];
2274 import.kind.function = try wasm.putOrGetFuncType(original_type);
2275 } else if (!dirty.contains(symbol.index)) {
2276 log.debug("Adding type from function '{s}'", .{wasm.symbolLocName(sym_loc)});
2277 const func = &wasm.functions.values()[symbol.index - wasm.imported_functions_count].func;
2278 func.type_index = try wasm.putOrGetFuncType(objectFuncTypes(wasm, object_id)[func.type_index]);
2279 dirty.putAssumeCapacityNoClobber(symbol.index, {});
2280 }2199 }
2281 }2200 };
2282 log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len});
2283}
22842201
2285fn checkExportNames(wasm: *Wasm) !void {2202 const TableIndexAdapter = struct {
2286 const force_exp_names = wasm.export_symbol_names;2203 bytes: []const u8,
2287 const diags = &wasm.base.comp.link_diags;
2288 if (force_exp_names.len > 0) {
2289 var failed_exports = false;
2290
2291 for (force_exp_names) |exp_name| {
2292 const exp_name_interned = try wasm.internString(exp_name);
2293 const loc = wasm.globals.get(exp_name_interned) orelse {
2294 var err = try diags.addErrorWithNotes(0);
2295 try err.addMsg("could not export '{s}', symbol not found", .{exp_name});
2296 failed_exports = true;
2297 continue;
2298 };
22992204
2300 const symbol = wasm.symbolLocSymbol(loc);2205 pub fn eql(ctx: @This(), a: []const u8, b: String) bool {
2301 symbol.setFlag(.WASM_SYM_EXPORTED);2206 return mem.eql(u8, a, mem.sliceTo(ctx.bytes[@intFromEnum(b)..], 0));
2302 }2207 }
23032208
2304 if (failed_exports) {2209 pub fn hash(_: @This(), adapted_key: []const u8) u64 {
2305 return error.FlushFailure;2210 assert(mem.indexOfScalar(u8, adapted_key, 0) == null);
2211 return std.hash_map.hashString(adapted_key);
2306 }2212 }
2213 };
2214
2215 pub fn slice(index: String, wasm: *const Wasm) [:0]const u8 {
2216 const start_slice = wasm.string_bytes.items[@intFromEnum(index)..];
2217 return start_slice[0..mem.indexOfScalar(u8, start_slice, 0).? :0];
2307 }2218 }
2308}
23092219
2310fn setupExports(wasm: *Wasm) !void {2220 pub fn toOptional(i: String) OptionalString {
2311 const comp = wasm.base.comp;2221 const result: OptionalString = @enumFromInt(@intFromEnum(i));
2312 const gpa = comp.gpa;2222 assert(result != .none);
2313 if (comp.config.output_mode == .Obj) return;2223 return result;
2314 log.debug("Building exports from symbols", .{});
2315
2316 for (wasm.resolved_symbols.keys()) |sym_loc| {
2317 const symbol = wasm.symbolLocSymbol(sym_loc);
2318 if (!symbol.isExported(comp.config.rdynamic)) continue;
2319
2320 const exp: Export = if (symbol.tag == .data) exp: {
2321 const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len));
2322 try wasm.wasm_globals.append(gpa, .{
2323 .global_type = .{ .valtype = .i32, .mutable = false },
2324 .init = .{ .i32_const = @as(i32, @intCast(symbol.virtual_address)) },
2325 });
2326 break :exp .{
2327 .name = symbol.name,
2328 .kind = .global,
2329 .index = global_index,
2330 };
2331 } else .{
2332 .name = symbol.name,
2333 .kind = symbol.tag.externalType(),
2334 .index = symbol.index,
2335 };
2336 log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{
2337 wasm.stringSlice(symbol.name),
2338 wasm.stringSlice(exp.name),
2339 exp.index,
2340 });
2341 try wasm.exports.append(gpa, exp);
2342 }2224 }
2225};
23432226
2344 log.debug("Completed building exports. Total count: ({d})", .{wasm.exports.items.len});2227pub const OptionalString = enum(u32) {
2345}2228 none = std.math.maxInt(u32),
2229 _,
23462230
2347fn setupStart(wasm: *Wasm) !void {2231 pub fn unwrap(i: OptionalString) ?String {
2348 const comp = wasm.base.comp;2232 if (i == .none) return null;
2349 const diags = &wasm.base.comp.link_diags;2233 return @enumFromInt(@intFromEnum(i));
2350 // do not export entry point if user set none or no default was set.2234 }
2351 const entry_name = wasm.entry_name.unwrap() orelse return;2235
23522236 pub fn slice(index: OptionalString, wasm: *const Wasm) ?[:0]const u8 {
2353 const symbol_loc = wasm.globals.get(entry_name) orelse {2237 return (index.unwrap() orelse return null).slice(wasm);
2354 var err = try diags.addErrorWithNotes(1);2238 }
2355 try err.addMsg("entry symbol '{s}' missing", .{wasm.stringSlice(entry_name)});2239};
2356 try err.addNote("'-fno-entry' suppresses this error", .{});
2357 return error.LinkFailure;
2358 };
23592240
2360 const symbol = wasm.symbolLocSymbol(symbol_loc);2241/// Stored identically to `String`. The bytes are reinterpreted as
2361 if (symbol.tag != .function)2242/// `std.wasm.Valtype` elements.
2362 return diags.fail("entry symbol '{s}' is not a function", .{wasm.stringSlice(entry_name)});2243pub const ValtypeList = enum(u32) {
2244 _,
23632245
2364 // Ensure the symbol is exported so host environment can access it2246 pub fn fromString(s: String) ValtypeList {
2365 if (comp.config.output_mode != .Obj) {2247 return @enumFromInt(@intFromEnum(s));
2366 symbol.setFlag(.WASM_SYM_EXPORTED);
2367 }2248 }
2368}
23692249
2370/// Sets up the memory section of the wasm module, as well as the stack.2250 pub fn slice(index: ValtypeList, wasm: *const Wasm) []const std.wasm.Valtype {
2371fn setupMemory(wasm: *Wasm) !void {2251 return @ptrCast(String.slice(@enumFromInt(@intFromEnum(index)), wasm));
2372 const comp = wasm.base.comp;2252 }
2373 const diags = &wasm.base.comp.link_diags;2253};
2374 const shared_memory = comp.config.shared_memory;
2375 log.debug("Setting up memory layout", .{});
2376 const page_size = std.wasm.page_size; // 64kb
2377 const stack_alignment: Alignment = .@"16"; // wasm's stack alignment as specified by tool-convention
2378 const heap_alignment: Alignment = .@"16"; // wasm's heap alignment as specified by tool-convention
2379
2380 // Always place the stack at the start by default
2381 // unless the user specified the global-base flag
2382 var place_stack_first = true;
2383 var memory_ptr: u64 = if (wasm.global_base) |base| blk: {
2384 place_stack_first = false;
2385 break :blk base;
2386 } else 0;
23872254
2388 const is_obj = comp.config.output_mode == .Obj;2255/// Index into `Wasm.imports`.
2256pub const ZcuImportIndex = enum(u32) {
2257 _,
23892258
2390 const stack_ptr = if (wasm.globals.get(wasm.preloaded_strings.__stack_pointer)) |loc| index: {2259 pub fn ptr(index: ZcuImportIndex, wasm: *const Wasm) *InternPool.Nav.Index {
2391 const sym = wasm.symbolLocSymbol(loc);2260 return &wasm.imports.keys()[@intFromEnum(index)];
2392 break :index sym.index - wasm.imported_globals_count;2261 }
2393 } else null;
23942262
2395 if (place_stack_first and !is_obj) {2263 pub fn importName(index: ZcuImportIndex, wasm: *const Wasm) String {
2396 memory_ptr = stack_alignment.forward(memory_ptr);2264 const zcu = wasm.base.comp.zcu.?;
2397 memory_ptr += wasm.base.stack_size;2265 const ip = &zcu.intern_pool;
2398 // We always put the stack pointer global at index 02266 const nav_index = index.ptr(wasm).*;
2399 if (stack_ptr) |index| {2267 const ext = ip.getNav(nav_index).getResolvedExtern(ip).?;
2400 wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));2268 const name_slice = ext.name.toSlice(ip);
2401 }2269 return wasm.getExistingString(name_slice).?;
2402 }2270 }
24032271
2404 var offset: u32 = @as(u32, @intCast(memory_ptr));2272 pub fn moduleName(index: ZcuImportIndex, wasm: *const Wasm) OptionalString {
2405 var data_seg_it = wasm.data_segments.iterator();2273 const zcu = wasm.base.comp.zcu.?;
2406 while (data_seg_it.next()) |entry| {2274 const ip = &zcu.intern_pool;
2407 const segment = wasm.segmentPtr(entry.value_ptr.*);2275 const nav_index = index.ptr(wasm).*;
2408 memory_ptr = segment.alignment.forward(memory_ptr);2276 const ext = ip.getNav(nav_index).getResolvedExtern(ip).?;
2277 const lib_name = ext.lib_name.toSlice(ip) orelse return .none;
2278 return wasm.getExistingString(lib_name).?.toOptional();
2279 }
24092280
2410 // set TLS-related symbols2281 pub fn functionType(index: ZcuImportIndex, wasm: *Wasm) FunctionType.Index {
2411 if (mem.eql(u8, entry.key_ptr.*, ".tdata")) {2282 const comp = wasm.base.comp;
2412 if (wasm.globals.get(wasm.preloaded_strings.__tls_size)) |loc| {2283 const target = &comp.root_mod.resolved_target.result;
2413 const sym = wasm.symbolLocSymbol(loc);2284 const zcu = comp.zcu.?;
2414 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.size);2285 const ip = &zcu.intern_pool;
2415 }2286 const nav_index = index.ptr(wasm).*;
2416 if (wasm.globals.get(wasm.preloaded_strings.__tls_align)) |loc| {2287 const ext = ip.getNav(nav_index).getResolvedExtern(ip).?;
2417 const sym = wasm.symbolLocSymbol(loc);2288 const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?;
2418 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.alignment.toByteUnits().?);2289 return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?;
2419 }2290 }
2420 if (wasm.globals.get(wasm.preloaded_strings.__tls_base)) |loc| {
2421 const sym = wasm.symbolLocSymbol(loc);
2422 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (shared_memory)
2423 @as(i32, 0)
2424 else
2425 @as(i32, @intCast(memory_ptr));
2426 }
2427 }
24282291
2429 memory_ptr += segment.size;2292 pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) ObjectGlobal.Type {
2430 segment.offset = offset;2293 _ = index;
2431 offset += segment.size;2294 _ = wasm;
2295 unreachable; // Zig has no way to create Wasm globals yet.
2432 }2296 }
2297};
24332298
2434 // create the memory init flag which is used by the init memory function2299/// 0. Index into `Wasm.object_function_imports`.
2435 if (shared_memory and wasm.hasPassiveInitializationSegments()) {2300/// 1. Index into `Wasm.imports`.
2436 // align to pointer size2301pub const FunctionImportId = enum(u32) {
2437 memory_ptr = mem.alignForward(u64, memory_ptr, 4);2302 _,
2438 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_memory_flag, .data);2303
2439 const sym = wasm.symbolLocSymbol(loc);2304 pub const Unpacked = union(enum) {
2440 sym.mark();2305 object_function_import: FunctionImport.Index,
2441 sym.virtual_address = @as(u32, @intCast(memory_ptr));2306 zcu_import: ZcuImportIndex,
2442 memory_ptr += 4;2307 };
2308
2309 pub fn pack(unpacked: Unpacked, wasm: *const Wasm) FunctionImportId {
2310 return switch (unpacked) {
2311 .object_function_import => |i| @enumFromInt(@intFromEnum(i)),
2312 .zcu_import => |i| @enumFromInt(@intFromEnum(i) + wasm.object_function_imports.entries.len),
2313 };
2443 }2314 }
24442315
2445 if (!place_stack_first and !is_obj) {2316 pub fn unpack(id: FunctionImportId, wasm: *const Wasm) Unpacked {
2446 memory_ptr = stack_alignment.forward(memory_ptr);2317 const i = @intFromEnum(id);
2447 memory_ptr += wasm.base.stack_size;2318 if (i < wasm.object_function_imports.entries.len) return .{ .object_function_import = @enumFromInt(i) };
2448 if (stack_ptr) |index| {2319 const zcu_import_i = i - wasm.object_function_imports.entries.len;
2449 wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));2320 return .{ .zcu_import = @enumFromInt(zcu_import_i) };
2450 }
2451 }2321 }
24522322
2453 // One of the linked object files has a reference to the __heap_base symbol.2323 pub fn fromObject(function_import_index: FunctionImport.Index, wasm: *const Wasm) FunctionImportId {
2454 // We must set its virtual address so it can be used in relocations.2324 return pack(.{ .object_function_import = function_import_index }, wasm);
2455 if (wasm.globals.get(wasm.preloaded_strings.__heap_base)) |loc| {
2456 const symbol = wasm.symbolLocSymbol(loc);
2457 symbol.virtual_address = @intCast(heap_alignment.forward(memory_ptr));
2458 }2325 }
24592326
2460 // Setup the max amount of pages2327 pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) FunctionImportId {
2461 // For now we only support wasm32 by setting the maximum allowed memory size 2^32-12328 return pack(.{ .zcu_import = zcu_import }, wasm);
2462 const max_memory_allowed: u64 = (1 << 32) - 1;2329 }
24632330
2464 if (wasm.initial_memory) |initial_memory| {2331 /// This function is allowed O(N) lookup because it is only called during
2465 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {2332 /// diagnostic generation.
2466 var err = try diags.addErrorWithNotes(0);2333 pub fn sourceLocation(id: FunctionImportId, wasm: *const Wasm) SourceLocation {
2467 try err.addMsg("Initial memory must be {d}-byte aligned", .{page_size});2334 switch (id.unpack(wasm)) {
2468 }2335 .object_function_import => |obj_func_index| {
2469 if (memory_ptr > initial_memory) {2336 // TODO binary search
2470 var err = try diags.addErrorWithNotes(0);2337 for (wasm.objects.items, 0..) |o, i| {
2471 try err.addMsg("Initial memory too small, must be at least {d} bytes", .{memory_ptr});2338 if (o.function_imports.off <= @intFromEnum(obj_func_index) and
2472 }2339 o.function_imports.off + o.function_imports.len > @intFromEnum(obj_func_index))
2473 if (initial_memory > max_memory_allowed) {2340 {
2474 var err = try diags.addErrorWithNotes(0);2341 return .pack(.{ .object_index = @enumFromInt(i) }, wasm);
2475 try err.addMsg("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});2342 }
2343 } else unreachable;
2344 },
2345 .zcu_import => return .zig_object_nofile, // TODO give a better source location
2476 }2346 }
2477 memory_ptr = initial_memory;
2478 }2347 }
2479 memory_ptr = mem.alignForward(u64, memory_ptr, std.wasm.page_size);
2480 // In case we do not import memory, but define it ourselves,
2481 // set the minimum amount of pages on the memory section.
2482 wasm.memories.limits.min = @as(u32, @intCast(memory_ptr / page_size));
2483 log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});
24842348
2485 if (wasm.globals.get(wasm.preloaded_strings.__heap_end)) |loc| {2349 pub fn importName(id: FunctionImportId, wasm: *const Wasm) String {
2486 const symbol = wasm.symbolLocSymbol(loc);2350 return switch (unpack(id, wasm)) {
2487 symbol.virtual_address = @as(u32, @intCast(memory_ptr));2351 inline .object_function_import, .zcu_import => |i| i.importName(wasm),
2352 };
2488 }2353 }
24892354
2490 if (wasm.max_memory) |max_memory| {2355 pub fn moduleName(id: FunctionImportId, wasm: *const Wasm) OptionalString {
2491 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {2356 return switch (unpack(id, wasm)) {
2492 var err = try diags.addErrorWithNotes(0);2357 inline .object_function_import, .zcu_import => |i| i.moduleName(wasm),
2493 try err.addMsg("Maximum memory must be {d}-byte aligned", .{page_size});2358 };
2494 }2359 }
2495 if (memory_ptr > max_memory) {2360
2496 var err = try diags.addErrorWithNotes(0);2361 pub fn functionType(id: FunctionImportId, wasm: *Wasm) FunctionType.Index {
2497 try err.addMsg("Maximum memory too small, must be at least {d} bytes", .{memory_ptr});2362 return switch (unpack(id, wasm)) {
2498 }2363 inline .object_function_import, .zcu_import => |i| i.functionType(wasm),
2499 if (max_memory > max_memory_allowed) {2364 };
2500 var err = try diags.addErrorWithNotes(0);2365 }
2501 try err.addMsg("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed});2366
2502 }2367 /// Asserts not emitting an object, and `Wasm.import_symbols` is false.
2503 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));2368 pub fn undefinedAllowed(id: FunctionImportId, wasm: *const Wasm) bool {
2504 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);2369 assert(!wasm.import_symbols);
2505 if (shared_memory) {2370 assert(wasm.base.comp.config.output_mode != .Obj);
2506 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_IS_SHARED);2371 return switch (unpack(id, wasm)) {
2372 .object_function_import => |i| {
2373 const import = i.value(wasm);
2374 return import.flags.binding == .strong and import.module_name != .none;
2375 },
2376 .zcu_import => |i| {
2377 const zcu = wasm.base.comp.zcu.?;
2378 const ip = &zcu.intern_pool;
2379 const ext = ip.getNav(i.ptr(wasm).*).getResolvedExtern(ip).?;
2380 return !ext.is_weak_linkage and ext.lib_name != .none;
2381 },
2382 };
2383 }
2384};
2385
2386/// 0. Index into `object_global_imports`.
2387/// 1. Index into `imports`.
2388pub const GlobalImportId = enum(u32) {
2389 _,
2390
2391 pub const Unpacked = union(enum) {
2392 object_global_import: GlobalImport.Index,
2393 zcu_import: ZcuImportIndex,
2394 };
2395
2396 pub fn pack(unpacked: Unpacked, wasm: *const Wasm) GlobalImportId {
2397 return switch (unpacked) {
2398 .object_global_import => |i| @enumFromInt(@intFromEnum(i)),
2399 .zcu_import => |i| @enumFromInt(@intFromEnum(i) + wasm.object_global_imports.entries.len),
2400 };
2401 }
2402
2403 pub fn unpack(id: GlobalImportId, wasm: *const Wasm) Unpacked {
2404 const i = @intFromEnum(id);
2405 if (i < wasm.object_global_imports.entries.len) return .{ .object_global_import = @enumFromInt(i) };
2406 const zcu_import_i = i - wasm.object_global_imports.entries.len;
2407 return .{ .zcu_import = @enumFromInt(zcu_import_i) };
2408 }
2409
2410 pub fn fromObject(object_global_import: GlobalImport.Index, wasm: *const Wasm) GlobalImportId {
2411 return pack(.{ .object_global_import = object_global_import }, wasm);
2412 }
2413
2414 /// This function is allowed O(N) lookup because it is only called during
2415 /// diagnostic generation.
2416 pub fn sourceLocation(id: GlobalImportId, wasm: *const Wasm) SourceLocation {
2417 switch (id.unpack(wasm)) {
2418 .object_global_import => |obj_global_index| {
2419 // TODO binary search
2420 for (wasm.objects.items, 0..) |o, i| {
2421 if (o.global_imports.off <= @intFromEnum(obj_global_index) and
2422 o.global_imports.off + o.global_imports.len > @intFromEnum(obj_global_index))
2423 {
2424 return .pack(.{ .object_index = @enumFromInt(i) }, wasm);
2425 }
2426 } else unreachable;
2427 },
2428 .zcu_import => return .zig_object_nofile, // TODO give a better source location
2507 }2429 }
2508 log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max});
2509 }2430 }
2510}
25112431
2512/// From a given object's index and the index of the segment, returns the corresponding2432 pub fn importName(id: GlobalImportId, wasm: *const Wasm) String {
2513/// index of the segment within the final data section. When the segment does not yet2433 return switch (unpack(id, wasm)) {
2514/// exist, a new one will be initialized and appended. The new index will be returned in that case.2434 inline .object_global_import, .zcu_import => |i| i.importName(wasm),
2515pub fn getMatchingSegment(wasm: *Wasm, object_id: ObjectId, symbol_index: Symbol.Index) !Segment.Index {2435 };
2516 const comp = wasm.base.comp;2436 }
2517 const gpa = comp.gpa;
2518 const diags = &wasm.base.comp.link_diags;
2519 const symbol = objectSymbols(wasm, object_id)[@intFromEnum(symbol_index)];
2520 const index: Segment.Index = @enumFromInt(wasm.segments.items.len);
2521 const shared_memory = comp.config.shared_memory;
25222437
2523 switch (symbol.tag) {2438 pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) OptionalString {
2524 .data => {2439 return switch (unpack(id, wasm)) {
2525 const segment_info = objectSegmentInfo(wasm, object_id)[symbol.index];2440 inline .object_global_import, .zcu_import => |i| i.moduleName(wasm),
2526 const merge_segment = comp.config.output_mode != .Obj;2441 };
2527 const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment));2442 }
2528 if (!result.found_existing) {2443
2529 result.value_ptr.* = index;2444 pub fn globalType(id: GlobalImportId, wasm: *Wasm) ObjectGlobal.Type {
2530 var flags: u32 = 0;2445 return switch (unpack(id, wasm)) {
2531 if (shared_memory) {2446 inline .object_global_import, .zcu_import => |i| i.globalType(wasm),
2532 flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);2447 };
2533 }2448 }
2534 try wasm.segments.append(gpa, .{2449};
2535 .alignment = .@"1",2450
2536 .size = 0,2451/// 0. Index into `Wasm.object_data_imports`.
2537 .offset = 0,2452/// 1. Index into `Wasm.imports`.
2538 .flags = flags,2453pub const DataImportId = enum(u32) {
2539 });2454 _,
2540 try wasm.segment_info.putNoClobber(gpa, index, .{2455
2541 .name = try gpa.dupe(u8, segment_info.name),2456 pub const Unpacked = union(enum) {
2542 .alignment = segment_info.alignment,2457 object_data_import: ObjectDataImport.Index,
2543 .flags = segment_info.flags,2458 zcu_import: ZcuImportIndex,
2544 });2459 };
2545 return index;2460
2546 } else return result.value_ptr.*;2461 pub fn pack(unpacked: Unpacked, wasm: *const Wasm) DataImportId {
2547 },2462 return switch (unpacked) {
2548 .function => return wasm.code_section_index.unwrap() orelse blk: {2463 .object_data_import => |i| @enumFromInt(@intFromEnum(i)),
2549 wasm.code_section_index = index.toOptional();2464 .zcu_import => |i| @enumFromInt(@intFromEnum(i) + wasm.object_data_imports.entries.len),
2550 try wasm.appendDummySegment();2465 };
2551 break :blk index;2466 }
2552 },2467
2553 .section => {2468 pub fn unpack(id: DataImportId, wasm: *const Wasm) Unpacked {
2554 const section_name = wasm.objectSymbol(object_id, symbol_index).name;2469 const i = @intFromEnum(id);
25552470 if (i < wasm.object_data_imports.entries.len) return .{ .object_data_import = @enumFromInt(i) };
2556 inline for (@typeInfo(CustomSections).@"struct".fields) |field| {2471 const zcu_import_i = i - wasm.object_data_imports.entries.len;
2557 if (@field(wasm.custom_sections, field.name).name == section_name) {2472 return .{ .zcu_import = @enumFromInt(zcu_import_i) };
2558 const field_ptr = &@field(wasm.custom_sections, field.name).index;2473 }
2559 return field_ptr.unwrap() orelse {2474
2560 field_ptr.* = index.toOptional();2475 pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) DataImportId {
2561 try wasm.appendDummySegment();2476 return pack(.{ .zcu_import = zcu_import }, wasm);
2562 return index;2477 }
2563 };2478
2564 }2479 pub fn fromObject(object_data_import: ObjectDataImport.Index, wasm: *const Wasm) DataImportId {
2565 } else {2480 return pack(.{ .object_data_import = object_data_import }, wasm);
2566 return diags.failParse(objectPath(wasm, object_id), "unknown section: {s}", .{2481 }
2567 wasm.stringSlice(section_name),2482
2568 });2483 pub fn sourceLocation(id: DataImportId, wasm: *const Wasm) SourceLocation {
2569 }2484 switch (id.unpack(wasm)) {
2485 .object_data_import => |obj_data_index| {
2486 // TODO binary search
2487 for (wasm.objects.items, 0..) |o, i| {
2488 if (o.data_imports.off <= @intFromEnum(obj_data_index) and
2489 o.data_imports.off + o.data_imports.len > @intFromEnum(obj_data_index))
2490 {
2491 return .pack(.{ .object_index = @enumFromInt(i) }, wasm);
2492 }
2493 } else unreachable;
2494 },
2495 .zcu_import => return .zig_object_nofile, // TODO give a better source location
2496 }
2497 }
2498};
2499
2500/// Index into `Wasm.symbol_table`.
2501pub const SymbolTableIndex = enum(u32) {
2502 _,
2503
2504 pub fn key(i: @This(), wasm: *const Wasm) *String {
2505 return &wasm.symbol_table.keys()[@intFromEnum(i)];
2506 }
2507};
2508
2509pub const OutReloc = struct {
2510 tag: Object.RelocationType,
2511 offset: u32,
2512 pointee: Pointee,
2513 addend: i32,
2514
2515 pub const Pointee = union {
2516 symbol_index: SymbolTableIndex,
2517 type_index: FunctionType.Index,
2518 };
2519
2520 pub const Slice = extern struct {
2521 /// Index into `out_relocs`.
2522 off: u32,
2523 len: u32,
2524
2525 pub fn slice(s: Slice, wasm: *const Wasm) []OutReloc {
2526 return wasm.relocations.items[s.off..][0..s.len];
2527 }
2528 };
2529};
2530
2531pub const ObjectRelocation = struct {
2532 tag: Tag,
2533 /// Offset of the value to rewrite relative to the relevant section's contents.
2534 /// When `offset` is zero, its position is immediately after the id and size of the section.
2535 offset: u32,
2536 pointee: Pointee,
2537 /// Populated only for `memory_addr_*`, `function_offset_i32` and `section_offset_i32`.
2538 addend: i32,
2539
2540 pub const Tag = enum(u8) {
2541 // These use `Pointee.function`.
2542 function_index_i32,
2543 function_index_leb,
2544 function_offset_i32,
2545 function_offset_i64,
2546 table_index_i32,
2547 table_index_i64,
2548 table_index_rel_sleb,
2549 table_index_rel_sleb64,
2550 table_index_sleb,
2551 table_index_sleb64,
2552 // These use `Pointee.symbol_name`.
2553 function_import_index_i32,
2554 function_import_index_leb,
2555 function_import_offset_i32,
2556 function_import_offset_i64,
2557 table_import_index_i32,
2558 table_import_index_i64,
2559 table_import_index_rel_sleb,
2560 table_import_index_rel_sleb64,
2561 table_import_index_sleb,
2562 table_import_index_sleb64,
2563 // These use `Pointee.global`.
2564 global_index_i32,
2565 global_index_leb,
2566 // These use `Pointee.symbol_name`.
2567 global_import_index_i32,
2568 global_import_index_leb,
2569 // These use `Pointee.data`.
2570 memory_addr_i32,
2571 memory_addr_i64,
2572 memory_addr_leb,
2573 memory_addr_leb64,
2574 memory_addr_locrel_i32,
2575 memory_addr_rel_sleb,
2576 memory_addr_rel_sleb64,
2577 memory_addr_sleb,
2578 memory_addr_sleb64,
2579 memory_addr_tls_sleb,
2580 memory_addr_tls_sleb64,
2581 // These use `Pointee.symbol_name`.
2582 memory_addr_import_i32,
2583 memory_addr_import_i64,
2584 memory_addr_import_leb,
2585 memory_addr_import_leb64,
2586 memory_addr_import_locrel_i32,
2587 memory_addr_import_rel_sleb,
2588 memory_addr_import_rel_sleb64,
2589 memory_addr_import_sleb,
2590 memory_addr_import_sleb64,
2591 memory_addr_import_tls_sleb,
2592 memory_addr_import_tls_sleb64,
2593 /// Uses `Pointee.section`.
2594 section_offset_i32,
2595 /// Uses `Pointee.table`.
2596 table_number_leb,
2597 /// Uses `Pointee.symbol_name`.
2598 table_import_number_leb,
2599 /// Uses `Pointee.type_index`.
2600 type_index_leb,
2601
2602 pub fn fromType(t: Object.RelocationType) Tag {
2603 return switch (t) {
2604 .event_index_leb => unreachable,
2605 .function_index_i32 => .function_index_i32,
2606 .function_index_leb => .function_index_leb,
2607 .function_offset_i32 => .function_offset_i32,
2608 .function_offset_i64 => .function_offset_i64,
2609 .global_index_i32 => .global_index_i32,
2610 .global_index_leb => .global_index_leb,
2611 .memory_addr_i32 => .memory_addr_i32,
2612 .memory_addr_i64 => .memory_addr_i64,
2613 .memory_addr_leb => .memory_addr_leb,
2614 .memory_addr_leb64 => .memory_addr_leb64,
2615 .memory_addr_locrel_i32 => .memory_addr_locrel_i32,
2616 .memory_addr_rel_sleb => .memory_addr_rel_sleb,
2617 .memory_addr_rel_sleb64 => .memory_addr_rel_sleb64,
2618 .memory_addr_sleb => .memory_addr_sleb,
2619 .memory_addr_sleb64 => .memory_addr_sleb64,
2620 .memory_addr_tls_sleb => .memory_addr_tls_sleb,
2621 .memory_addr_tls_sleb64 => .memory_addr_tls_sleb64,
2622 .section_offset_i32 => .section_offset_i32,
2623 .table_index_i32 => .table_index_i32,
2624 .table_index_i64 => .table_index_i64,
2625 .table_index_rel_sleb => .table_index_rel_sleb,
2626 .table_index_rel_sleb64 => .table_index_rel_sleb64,
2627 .table_index_sleb => .table_index_sleb,
2628 .table_index_sleb64 => .table_index_sleb64,
2629 .table_number_leb => .table_number_leb,
2630 .type_index_leb => .type_index_leb,
2631 };
2632 }
2633
2634 pub fn fromTypeImport(t: Object.RelocationType) Tag {
2635 return switch (t) {
2636 .event_index_leb => unreachable,
2637 .function_index_i32 => .function_import_index_i32,
2638 .function_index_leb => .function_import_index_leb,
2639 .function_offset_i32 => .function_import_offset_i32,
2640 .function_offset_i64 => .function_import_offset_i64,
2641 .global_index_i32 => .global_import_index_i32,
2642 .global_index_leb => .global_import_index_leb,
2643 .memory_addr_i32 => .memory_addr_import_i32,
2644 .memory_addr_i64 => .memory_addr_import_i64,
2645 .memory_addr_leb => .memory_addr_import_leb,
2646 .memory_addr_leb64 => .memory_addr_import_leb64,
2647 .memory_addr_locrel_i32 => .memory_addr_import_locrel_i32,
2648 .memory_addr_rel_sleb => .memory_addr_import_rel_sleb,
2649 .memory_addr_rel_sleb64 => .memory_addr_import_rel_sleb64,
2650 .memory_addr_sleb => .memory_addr_import_sleb,
2651 .memory_addr_sleb64 => .memory_addr_import_sleb64,
2652 .memory_addr_tls_sleb => .memory_addr_import_tls_sleb,
2653 .memory_addr_tls_sleb64 => .memory_addr_import_tls_sleb64,
2654 .section_offset_i32 => unreachable,
2655 .table_index_i32 => .table_import_index_i32,
2656 .table_index_i64 => .table_import_index_i64,
2657 .table_index_rel_sleb => .table_import_index_rel_sleb,
2658 .table_index_rel_sleb64 => .table_import_index_rel_sleb64,
2659 .table_index_sleb => .table_import_index_sleb,
2660 .table_index_sleb64 => .table_import_index_sleb64,
2661 .table_number_leb => .table_import_number_leb,
2662 .type_index_leb => unreachable,
2663 };
2664 }
2665 };
2666
2667 pub const Pointee = union {
2668 symbol_name: String,
2669 data: ObjectData.Index,
2670 type_index: FunctionType.Index,
2671 section: ObjectSectionIndex,
2672 function: ObjectFunctionIndex,
2673 global: ObjectGlobalIndex,
2674 table: ObjectTableIndex,
2675 };
2676
2677 pub const Slice = extern struct {
2678 /// Index into `relocations`.
2679 off: u32,
2680 len: u32,
2681
2682 const empty: Slice = .{ .off = 0, .len = 0 };
2683
2684 pub fn tags(s: Slice, wasm: *const Wasm) []const ObjectRelocation.Tag {
2685 return wasm.object_relocations.items(.tag)[s.off..][0..s.len];
2686 }
2687
2688 pub fn offsets(s: Slice, wasm: *const Wasm) []const u32 {
2689 return wasm.object_relocations.items(.offset)[s.off..][0..s.len];
2690 }
2691
2692 pub fn pointees(s: Slice, wasm: *const Wasm) []const Pointee {
2693 return wasm.object_relocations.items(.pointee)[s.off..][0..s.len];
2694 }
2695
2696 pub fn addends(s: Slice, wasm: *const Wasm) []const i32 {
2697 return wasm.object_relocations.items(.addend)[s.off..][0..s.len];
2698 }
2699 };
2700
2701 pub const IterableSlice = struct {
2702 slice: Slice,
2703 /// Offset at which point to stop iterating.
2704 end: u32,
2705
2706 const empty: IterableSlice = .{ .slice = .empty, .end = 0 };
2707
2708 fn init(relocs: Slice, offset: u32, size: u32, wasm: *const Wasm) IterableSlice {
2709 const offsets = relocs.offsets(wasm);
2710 const start = std.sort.lowerBound(u32, offsets, offset, order);
2711 return .{
2712 .slice = .{
2713 .off = @intCast(relocs.off + start),
2714 .len = @intCast(relocs.len - start),
2715 },
2716 .end = offset + size,
2717 };
2718 }
2719
2720 fn order(lhs: u32, rhs: u32) std.math.Order {
2721 return std.math.order(lhs, rhs);
2722 }
2723 };
2724};
2725
2726pub const MemoryImport = extern struct {
2727 module_name: String,
2728 limits_min: u32,
2729 limits_max: u32,
2730 source_location: SourceLocation,
2731 limits_has_max: bool,
2732 limits_is_shared: bool,
2733 padding: [2]u8 = .{ 0, 0 },
2734
2735 pub fn limits(mi: *const MemoryImport) std.wasm.Limits {
2736 return .{
2737 .flags = .{
2738 .has_max = mi.limits_has_max,
2739 .is_shared = mi.limits_is_shared,
2740 },
2741 .min = mi.limits_min,
2742 .max = mi.limits_max,
2743 };
2744 }
2745};
2746
2747pub const Alignment = InternPool.Alignment;
2748
2749pub const InitFunc = extern struct {
2750 priority: u32,
2751 function_index: ObjectFunctionIndex,
2752
2753 pub fn lessThan(ctx: void, lhs: InitFunc, rhs: InitFunc) bool {
2754 _ = ctx;
2755 if (lhs.priority == rhs.priority) {
2756 return @intFromEnum(lhs.function_index) < @intFromEnum(rhs.function_index);
2757 } else {
2758 return lhs.priority < rhs.priority;
2759 }
2760 }
2761};
2762
2763pub const Comdat = struct {
2764 name: String,
2765 /// Must be zero, no flags are currently defined by the tool-convention.
2766 flags: u32,
2767 symbols: Comdat.Symbol.Slice,
2768
2769 pub const Symbol = struct {
2770 kind: Comdat.Symbol.Type,
2771 /// Index of the data segment/function/global/event/table within a WASM module.
2772 /// The object must not be an import.
2773 index: u32,
2774
2775 pub const Slice = struct {
2776 /// Index into Wasm object_comdat_symbols
2777 off: u32,
2778 len: u32,
2779 };
2780
2781 pub const Type = enum(u8) {
2782 data = 0,
2783 function = 1,
2784 global = 2,
2785 event = 3,
2786 table = 4,
2787 section = 5,
2788 };
2789 };
2790};
2791
2792/// Stored as a u8 so it can reuse the string table mechanism.
2793pub const Feature = packed struct(u8) {
2794 prefix: Prefix,
2795 /// Type of the feature, must be unique in the sequence of features.
2796 tag: Tag,
2797
2798 pub const sentinel: Feature = @bitCast(@as(u8, 0));
2799
2800 /// Stored identically to `String`. The bytes are reinterpreted as `Feature`
2801 /// elements. Elements must be sorted before string-interning.
2802 pub const Set = enum(u32) {
2803 _,
2804
2805 pub fn fromString(s: String) Set {
2806 return @enumFromInt(@intFromEnum(s));
2807 }
2808
2809 pub fn string(s: Set) String {
2810 return @enumFromInt(@intFromEnum(s));
2811 }
2812
2813 pub fn slice(s: Set, wasm: *const Wasm) [:sentinel]const Feature {
2814 return @ptrCast(string(s).slice(wasm));
2815 }
2816 };
2817
2818 /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem.
2819 /// Additionally the name uses convention matching the wasm binary format.
2820 pub const Tag = enum(u6) {
2821 atomics,
2822 @"bulk-memory",
2823 @"exception-handling",
2824 @"extended-const",
2825 @"half-precision",
2826 multimemory,
2827 multivalue,
2828 @"mutable-globals",
2829 @"nontrapping-fptoint",
2830 @"reference-types",
2831 @"relaxed-simd",
2832 @"sign-ext",
2833 simd128,
2834 @"tail-call",
2835 @"shared-mem",
2836
2837 pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag {
2838 return @enumFromInt(@intFromEnum(feature));
2839 }
2840
2841 pub fn toCpuFeature(tag: Tag) ?std.Target.wasm.Feature {
2842 return if (@intFromEnum(tag) < @typeInfo(std.Target.wasm.Feature).@"enum".fields.len)
2843 @enumFromInt(@intFromEnum(tag))
2844 else
2845 null;
2846 }
2847
2848 pub const format = @compileError("use @tagName instead");
2849 };
2850
2851 /// Provides information about the usage of the feature.
2852 pub const Prefix = enum(u2) {
2853 /// Reserved so that a 0-byte Feature is invalid and therefore can be a sentinel.
2854 invalid,
2855 /// Object uses this feature, and the link fails if feature is not in
2856 /// the allowed set.
2857 @"+",
2858 /// Object does not use this feature, and the link fails if this
2859 /// feature is in the allowed set.
2860 @"-",
2861 /// Object uses this feature, and the link fails if this feature is not
2862 /// in the allowed set, or if any object does not use this feature.
2863 @"=",
2864 };
2865
2866 pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
2867 _ = opt;
2868 _ = fmt;
2869 try writer.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) });
2870 }
2871
2872 pub fn lessThan(_: void, a: Feature, b: Feature) bool {
2873 assert(a != b);
2874 const a_int: u8 = @bitCast(a);
2875 const b_int: u8 = @bitCast(b);
2876 return a_int < b_int;
2877 }
2878};
2879
2880pub fn open(
2881 arena: Allocator,
2882 comp: *Compilation,
2883 emit: Path,
2884 options: link.File.OpenOptions,
2885) !*Wasm {
2886 // TODO: restore saved linker state, don't truncate the file, and
2887 // participate in incremental compilation.
2888 return createEmpty(arena, comp, emit, options);
2889}
2890
2891pub fn createEmpty(
2892 arena: Allocator,
2893 comp: *Compilation,
2894 emit: Path,
2895 options: link.File.OpenOptions,
2896) !*Wasm {
2897 const target = comp.root_mod.resolved_target.result;
2898 assert(target.ofmt == .wasm);
2899
2900 const use_lld = build_options.have_llvm and comp.config.use_lld;
2901 const use_llvm = comp.config.use_llvm;
2902 const output_mode = comp.config.output_mode;
2903 const wasi_exec_model = comp.config.wasi_exec_model;
2904
2905 // If using LLD to link, this code should produce an object file so that it
2906 // can be passed to LLD.
2907 // If using LLVM to generate the object file for the zig compilation unit,
2908 // we need a place to put the object file so that it can be subsequently
2909 // handled.
2910 const zcu_object_sub_path = if (!use_lld and !use_llvm)
2911 null
2912 else
2913 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
2914
2915 const wasm = try arena.create(Wasm);
2916 wasm.* = .{
2917 .base = .{
2918 .tag = .wasm,
2919 .comp = comp,
2920 .emit = emit,
2921 .zcu_object_sub_path = zcu_object_sub_path,
2922 // Garbage collection is so crucial to WebAssembly that we design
2923 // the linker around the assumption that it will be on in the vast
2924 // majority of cases, and therefore express "no garbage collection"
2925 // in terms of setting the no_strip and must_link flags on all
2926 // symbols.
2927 .gc_sections = options.gc_sections orelse (output_mode != .Obj),
2928 .print_gc_sections = options.print_gc_sections,
2929 .stack_size = options.stack_size orelse switch (target.os.tag) {
2930 .freestanding => 1 * 1024 * 1024, // 1 MiB
2931 else => 16 * 1024 * 1024, // 16 MiB
2932 },
2933 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
2934 .file = null,
2935 .disable_lld_caching = options.disable_lld_caching,
2936 .build_id = options.build_id,
2937 },
2938 .name = undefined,
2939 .string_table = .empty,
2940 .string_bytes = .empty,
2941 .import_table = options.import_table,
2942 .export_table = options.export_table,
2943 .import_symbols = options.import_symbols,
2944 .export_symbol_names = options.export_symbol_names,
2945 .global_base = options.global_base,
2946 .initial_memory = options.initial_memory,
2947 .max_memory = options.max_memory,
2948
2949 .entry_name = undefined,
2950 .dump_argv_list = .empty,
2951 .object_host_name = .none,
2952 .preloaded_strings = undefined,
2953 };
2954 if (use_llvm and comp.config.have_zcu) {
2955 wasm.llvm_object = try LlvmObject.create(arena, comp);
2956 }
2957 errdefer wasm.base.destroy();
2958
2959 if (options.object_host_name) |name| wasm.object_host_name = (try wasm.internString(name)).toOptional();
2960
2961 inline for (@typeInfo(PreloadedStrings).@"struct".fields) |field| {
2962 @field(wasm.preloaded_strings, field.name) = try wasm.internString(field.name);
2963 }
2964
2965 wasm.entry_name = switch (options.entry) {
2966 .disabled => .none,
2967 .default => if (output_mode != .Exe) .none else defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(),
2968 .enabled => defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(),
2969 .named => |name| (try wasm.internString(name)).toOptional(),
2970 };
2971
2972 if (use_lld and (use_llvm or !comp.config.have_zcu)) {
2973 // LLVM emits the object file (if any); LLD links it into the final product.
2974 return wasm;
2975 }
2976
2977 // What path should this Wasm linker code output to?
2978 // If using LLD to link, this code should produce an object file so that it
2979 // can be passed to LLD.
2980 const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path;
2981
2982 wasm.base.file = try emit.root_dir.handle.createFile(sub_path, .{
2983 .truncate = true,
2984 .read = true,
2985 .mode = if (fs.has_executable_bit)
2986 if (target.os.tag == .wasi and output_mode == .Exe)
2987 fs.File.default_mode | 0b001_000_000
2988 else
2989 fs.File.default_mode
2990 else
2991 0,
2992 });
2993 wasm.name = sub_path;
2994
2995 return wasm;
2996}
2997
2998fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void {
2999 const diags = &wasm.base.comp.link_diags;
3000 const obj = link.openObject(path, false, false) catch |err| {
3001 switch (diags.failParse(path, "failed to open object: {s}", .{@errorName(err)})) {
3002 error.LinkFailure => return,
3003 }
3004 };
3005 wasm.parseObject(obj) catch |err| {
3006 switch (diags.failParse(path, "failed to parse object: {s}", .{@errorName(err)})) {
3007 error.LinkFailure => return,
3008 }
3009 };
3010}
3011
3012fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {
3013 log.debug("parseObject {}", .{obj.path});
3014 const gpa = wasm.base.comp.gpa;
3015 const gc_sections = wasm.base.gc_sections;
3016
3017 defer obj.file.close();
3018
3019 try wasm.objects.ensureUnusedCapacity(gpa, 1);
3020 const stat = try obj.file.stat();
3021 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
3022
3023 const file_contents = try gpa.alloc(u8, size);
3024 defer gpa.free(file_contents);
3025
3026 const n = try obj.file.preadAll(file_contents, 0);
3027 if (n != file_contents.len) return error.UnexpectedEndOfFile;
3028
3029 var ss: Object.ScratchSpace = .{};
3030 defer ss.deinit(gpa);
3031
3032 const object = try Object.parse(wasm, file_contents, obj.path, null, wasm.object_host_name, &ss, obj.must_link, gc_sections);
3033 wasm.objects.appendAssumeCapacity(object);
3034}
3035
3036fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {
3037 log.debug("parseArchive {}", .{obj.path});
3038 const gpa = wasm.base.comp.gpa;
3039 const gc_sections = wasm.base.gc_sections;
3040
3041 defer obj.file.close();
3042
3043 const stat = try obj.file.stat();
3044 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
3045
3046 const file_contents = try gpa.alloc(u8, size);
3047 defer gpa.free(file_contents);
3048
3049 const n = try obj.file.preadAll(file_contents, 0);
3050 if (n != file_contents.len) return error.UnexpectedEndOfFile;
3051
3052 var archive = try Archive.parse(gpa, file_contents);
3053 defer archive.deinit(gpa);
3054
3055 // In this case we must force link all embedded object files within the archive
3056 // We loop over all symbols, and then group them by offset as the offset
3057 // notates where the object file starts.
3058 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
3059 defer offsets.deinit();
3060 for (archive.toc.values()) |symbol_offsets| {
3061 for (symbol_offsets.items) |sym_offset| {
3062 try offsets.put(sym_offset, {});
3063 }
3064 }
3065
3066 var ss: Object.ScratchSpace = .{};
3067 defer ss.deinit(gpa);
3068
3069 try wasm.objects.ensureUnusedCapacity(gpa, offsets.count());
3070 for (offsets.keys()) |file_offset| {
3071 const object = try archive.parseObject(wasm, file_contents, file_offset, obj.path, wasm.object_host_name, &ss, obj.must_link, gc_sections);
3072 wasm.objects.appendAssumeCapacity(object);
3073 }
3074}
3075
3076pub fn deinit(wasm: *Wasm) void {
3077 const gpa = wasm.base.comp.gpa;
3078 if (wasm.llvm_object) |llvm_object| llvm_object.deinit();
3079
3080 wasm.navs_exe.deinit(gpa);
3081 wasm.navs_obj.deinit(gpa);
3082 wasm.uavs_exe.deinit(gpa);
3083 wasm.uavs_obj.deinit(gpa);
3084 wasm.overaligned_uavs.deinit(gpa);
3085 wasm.zcu_funcs.deinit(gpa);
3086 wasm.nav_exports.deinit(gpa);
3087 wasm.uav_exports.deinit(gpa);
3088 wasm.imports.deinit(gpa);
3089
3090 wasm.flush_buffer.deinit(gpa);
3091
3092 wasm.mir_instructions.deinit(gpa);
3093 wasm.mir_extra.deinit(gpa);
3094 wasm.all_zcu_locals.deinit(gpa);
3095
3096 if (wasm.dwarf) |*dwarf| dwarf.deinit();
3097
3098 wasm.object_function_imports.deinit(gpa);
3099 wasm.object_functions.deinit(gpa);
3100 wasm.object_global_imports.deinit(gpa);
3101 wasm.object_globals.deinit(gpa);
3102 wasm.object_table_imports.deinit(gpa);
3103 wasm.object_tables.deinit(gpa);
3104 wasm.object_memory_imports.deinit(gpa);
3105 wasm.object_memories.deinit(gpa);
3106 wasm.object_relocations.deinit(gpa);
3107 wasm.object_data_imports.deinit(gpa);
3108 wasm.object_data_segments.deinit(gpa);
3109 wasm.object_datas.deinit(gpa);
3110 wasm.object_custom_segments.deinit(gpa);
3111 wasm.object_init_funcs.deinit(gpa);
3112 wasm.object_comdats.deinit(gpa);
3113 wasm.object_relocations_table.deinit(gpa);
3114 wasm.object_comdat_symbols.deinit(gpa);
3115 wasm.objects.deinit(gpa);
3116
3117 wasm.func_types.deinit(gpa);
3118 wasm.function_exports.deinit(gpa);
3119 wasm.hidden_function_exports.deinit(gpa);
3120 wasm.function_imports.deinit(gpa);
3121 wasm.functions.deinit(gpa);
3122 wasm.globals.deinit(gpa);
3123 wasm.global_exports.deinit(gpa);
3124 wasm.global_imports.deinit(gpa);
3125 wasm.table_imports.deinit(gpa);
3126 wasm.tables.deinit(gpa);
3127 wasm.data_imports.deinit(gpa);
3128 wasm.data_segments.deinit(gpa);
3129 wasm.symbol_table.deinit(gpa);
3130 wasm.out_relocs.deinit(gpa);
3131 wasm.uav_fixups.deinit(gpa);
3132 wasm.nav_fixups.deinit(gpa);
3133 wasm.func_table_fixups.deinit(gpa);
3134
3135 wasm.zcu_indirect_function_set.deinit(gpa);
3136 wasm.object_indirect_function_import_set.deinit(gpa);
3137 wasm.object_indirect_function_set.deinit(gpa);
3138
3139 wasm.string_bytes.deinit(gpa);
3140 wasm.string_table.deinit(gpa);
3141 wasm.dump_argv_list.deinit(gpa);
3142
3143 wasm.params_scratch.deinit(gpa);
3144 wasm.returns_scratch.deinit(gpa);
3145
3146 wasm.error_name_bytes.deinit(gpa);
3147 wasm.error_name_offs.deinit(gpa);
3148 wasm.tag_name_bytes.deinit(gpa);
3149 wasm.tag_name_offs.deinit(gpa);
3150
3151 wasm.missing_exports.deinit(gpa);
3152}
3153
3154pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
3155 if (build_options.skip_non_native and builtin.object_format != .wasm) {
3156 @panic("Attempted to compile for object format that was disabled by build configuration");
3157 }
3158 if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness);
3159
3160 dev.check(.wasm_backend);
3161
3162 const zcu = pt.zcu;
3163 const gpa = zcu.gpa;
3164 try wasm.functions.ensureUnusedCapacity(gpa, 1);
3165 try wasm.zcu_funcs.ensureUnusedCapacity(gpa, 1);
3166
3167 const ip = &zcu.intern_pool;
3168 const owner_nav = zcu.funcInfo(func_index).owner_nav;
3169 log.debug("updateFunc {}", .{ip.getNav(owner_nav).fqn.fmt(ip)});
3170
3171 const zds: ZcuDataStarts = .init(wasm);
3172
3173 // This converts AIR to MIR but does not yet lower to wasm code.
3174 // That lowering happens during `flush`, after garbage collection, which
3175 // can affect function and global indexes, which affects the LEB integer
3176 // encoding, which affects the output binary size.
3177 const function = try CodeGen.function(wasm, pt, func_index, air, liveness);
3178 wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = function });
3179 wasm.functions.putAssumeCapacity(.pack(wasm, .{ .zcu_func = @enumFromInt(wasm.zcu_funcs.entries.len - 1) }), {});
3180
3181 try zds.finish(wasm, pt);
3182}
3183
3184// Generate code for the "Nav", storing it in memory to be later written to
3185// the file on flush().
3186pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
3187 if (build_options.skip_non_native and builtin.object_format != .wasm) {
3188 @panic("Attempted to compile for object format that was disabled by build configuration");
3189 }
3190 if (wasm.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav_index);
3191 const zcu = pt.zcu;
3192 const ip = &zcu.intern_pool;
3193 const nav = ip.getNav(nav_index);
3194 const comp = wasm.base.comp;
3195 const gpa = comp.gpa;
3196 const is_obj = comp.config.output_mode == .Obj;
3197 const target = &comp.root_mod.resolved_target.result;
3198
3199 const nav_init, const chased_nav_index = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
3200 .func => return, // global const which is a function alias
3201 .@"extern" => |ext| {
3202 if (is_obj) {
3203 assert(!wasm.navs_obj.contains(ext.owner_nav));
3204 } else {
3205 assert(!wasm.navs_exe.contains(ext.owner_nav));
3206 }
3207 const name = try wasm.internString(ext.name.toSlice(ip));
3208 if (ext.lib_name.toSlice(ip)) |ext_name| _ = try wasm.internString(ext_name);
3209 try wasm.imports.ensureUnusedCapacity(gpa, 1);
3210 try wasm.function_imports.ensureUnusedCapacity(gpa, 1);
3211 try wasm.data_imports.ensureUnusedCapacity(gpa, 1);
3212 const zcu_import = wasm.addZcuImportReserved(ext.owner_nav);
3213 if (ip.isFunctionType(nav.typeOf(ip))) {
3214 wasm.function_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm));
3215 // Ensure there is a corresponding function type table entry.
3216 const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?;
3217 _ = try internFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target);
3218 } else {
3219 wasm.data_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm));
3220 }
3221 return;
3222 },
3223 .variable => |variable| .{ variable.init, variable.owner_nav },
3224 else => .{ nav.status.fully_resolved.val, nav_index },
3225 };
3226 //log.debug("updateNav {} {d}", .{ nav.fqn.fmt(ip), chased_nav_index });
3227 assert(!wasm.imports.contains(chased_nav_index));
3228
3229 if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {
3230 if (is_obj) {
3231 assert(!wasm.navs_obj.contains(chased_nav_index));
3232 } else {
3233 assert(!wasm.navs_exe.contains(chased_nav_index));
3234 }
3235 return;
3236 }
3237
3238 if (is_obj) {
3239 const zcu_data_starts: ZcuDataStarts = .initObj(wasm);
3240 const navs_i = try refNavObj(wasm, chased_nav_index);
3241 const zcu_data = try lowerZcuData(wasm, pt, nav_init);
3242 navs_i.value(wasm).* = zcu_data;
3243 try zcu_data_starts.finishObj(wasm, pt);
3244 } else {
3245 const zcu_data_starts: ZcuDataStarts = .initExe(wasm);
3246 const navs_i = try refNavExe(wasm, chased_nav_index);
3247 const zcu_data = try lowerZcuData(wasm, pt, nav_init);
3248 navs_i.value(wasm).code = zcu_data.code;
3249 try zcu_data_starts.finishExe(wasm, pt);
3250 }
3251}
3252
3253pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
3254 const comp = wasm.base.comp;
3255 const diags = &comp.link_diags;
3256 if (wasm.dwarf) |*dw| {
3257 dw.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) {
3258 error.Overflow => return error.Overflow,
3259 error.OutOfMemory => return error.OutOfMemory,
3260 else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}),
3261 };
3262 }
3263}
3264
3265pub fn deleteExport(
3266 wasm: *Wasm,
3267 exported: Zcu.Exported,
3268 name: InternPool.NullTerminatedString,
3269) void {
3270 if (wasm.llvm_object != null) return;
3271
3272 const zcu = wasm.base.comp.zcu.?;
3273 const ip = &zcu.intern_pool;
3274 const name_slice = name.toSlice(ip);
3275 const export_name = wasm.getExistingString(name_slice).?;
3276 switch (exported) {
3277 .nav => |nav_index| {
3278 log.debug("deleteExport '{s}' nav={d}", .{ name_slice, @intFromEnum(nav_index) });
3279 assert(wasm.nav_exports.swapRemove(.{ .nav_index = nav_index, .name = export_name }));
2570 },3280 },
2571 else => unreachable,3281 .uav => |uav_index| assert(wasm.uav_exports.swapRemove(.{ .uav_index = uav_index, .name = export_name })),
2572 }3282 }
2573}3283}
25743284
2575/// Appends a new segment with default field values3285pub fn updateExports(
2576fn appendDummySegment(wasm: *Wasm) !void {3286 wasm: *Wasm,
2577 const gpa = wasm.base.comp.gpa;3287 pt: Zcu.PerThread,
2578 try wasm.segments.append(gpa, .{3288 exported: Zcu.Exported,
2579 .alignment = .@"1",3289 export_indices: []const Zcu.Export.Index,
2580 .size = 0,3290) !void {
2581 .offset = 0,3291 if (build_options.skip_non_native and builtin.object_format != .wasm) {
2582 .flags = 0,3292 @panic("Attempted to compile for object format that was disabled by build configuration");
2583 });3293 }
3294 if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices);
3295
3296 const zcu = pt.zcu;
3297 const gpa = zcu.gpa;
3298 const ip = &zcu.intern_pool;
3299 for (export_indices) |export_idx| {
3300 const exp = export_idx.ptr(zcu);
3301 const name_slice = exp.opts.name.toSlice(ip);
3302 const name = try wasm.internString(name_slice);
3303 switch (exported) {
3304 .nav => |nav_index| {
3305 log.debug("updateExports '{s}' nav={d}", .{ name_slice, @intFromEnum(nav_index) });
3306 try wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx);
3307 },
3308 .uav => |uav_index| try wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx),
3309 }
3310 }
2584}3311}
25853312
2586pub fn loadInput(wasm: *Wasm, input: link.Input) !void {3313pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
...@@ -2596,7 +3323,9 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {...@@ -2596,7 +3323,9 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
2596 .res => unreachable,3323 .res => unreachable,
2597 .dso_exact => unreachable,3324 .dso_exact => unreachable,
2598 .dso => unreachable,3325 .dso => unreachable,
2599 .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)),3326 .object, .archive => |obj| {
3327 try argv.append(gpa, try obj.path.toString(comp.arena));
3328 },
2600 }3329 }
2601 }3330 }
26023331
...@@ -2612,791 +3341,472 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {...@@ -2612,791 +3341,472 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
2612pub fn flush(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {3341pub fn flush(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
2613 const comp = wasm.base.comp;3342 const comp = wasm.base.comp;
2614 const use_lld = build_options.have_llvm and comp.config.use_lld;3343 const use_lld = build_options.have_llvm and comp.config.use_lld;
3344 const diags = &comp.link_diags;
26153345
2616 if (use_lld) {3346 if (use_lld) {
2617 return wasm.linkWithLLD(arena, tid, prog_node);3347 return wasm.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) {
3348 error.OutOfMemory => return error.OutOfMemory,
3349 error.LinkFailure => return error.LinkFailure,
3350 else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}),
3351 };
2618 }3352 }
2619 return wasm.flushModule(arena, tid, prog_node);3353 return wasm.flushModule(arena, tid, prog_node);
2620}3354}
26213355
2622pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {3356pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!void {
2623 const tracy = trace(@src());3357 const tracy = trace(@src());
2624 defer tracy.end();3358 defer tracy.end();
26253359
2626 const comp = wasm.base.comp;3360 const sub_prog_node = prog_node.start("Wasm Prelink", 0);
2627 const diags = &comp.link_diags;
2628 if (wasm.llvm_object) |llvm_object| {
2629 try wasm.base.emitLlvmObject(arena, llvm_object, prog_node);
2630 const use_lld = build_options.have_llvm and comp.config.use_lld;
2631 if (use_lld) return;
2632 }
2633
2634 if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items);
2635
2636 const sub_prog_node = prog_node.start("Wasm Flush", 0);
2637 defer sub_prog_node.end();3361 defer sub_prog_node.end();
26383362
2639 const module_obj_path: ?Path = if (wasm.base.zcu_object_sub_path) |path| .{
2640 .root_dir = wasm.base.emit.root_dir,
2641 .sub_path = if (fs.path.dirname(wasm.base.emit.sub_path)) |dirname|
2642 try fs.path.join(arena, &.{ dirname, path })
2643 else
2644 path,
2645 } else null;
2646
2647 if (wasm.zig_object) |zig_object| try zig_object.flushModule(wasm, tid);
2648
2649 if (module_obj_path) |path| openParseObjectReportingFailure(wasm, path);
2650
2651 if (wasm.zig_object != null) {
2652 try wasm.resolveSymbolsInObject(.zig_object);
2653 }
2654 if (diags.hasErrors()) return error.FlushFailure;
2655 for (0..wasm.objects.items.len) |object_index| {
2656 try wasm.resolveSymbolsInObject(@enumFromInt(object_index));
2657 }
2658 if (diags.hasErrors()) return error.FlushFailure;
2659
2660 var emit_features_count: u32 = 0;
2661 var enabled_features: [@typeInfo(Feature.Tag).@"enum".fields.len]bool = undefined;
2662 try wasm.validateFeatures(&enabled_features, &emit_features_count);
2663 try wasm.resolveSymbolsInArchives();
2664 if (diags.hasErrors()) return error.FlushFailure;
2665 try wasm.resolveLazySymbols();
2666 try wasm.checkUndefinedSymbols();
2667 try wasm.checkExportNames();
2668
2669 try wasm.setupInitFunctions();
2670 if (diags.hasErrors()) return error.FlushFailure;
2671 try wasm.setupStart();
2672
2673 try wasm.markReferences();
2674 try wasm.setupImports();
2675 try wasm.mergeSections();
2676 try wasm.mergeTypes();
2677 try wasm.allocateAtoms();
2678 try wasm.setupMemory();
2679 if (diags.hasErrors()) return error.FlushFailure;
2680 wasm.allocateVirtualAddresses();
2681 wasm.mapFunctionTable();
2682 try wasm.initializeCallCtorsFunction();
2683 try wasm.setupInitMemoryFunction();
2684 try wasm.setupTLSRelocationsFunction();
2685 try wasm.initializeTLSFunction();
2686 try wasm.setupStartSection();
2687 try wasm.setupExports();
2688 try wasm.writeToFile(enabled_features, emit_features_count, arena);
2689 if (diags.hasErrors()) return error.FlushFailure;
2690}
2691
2692/// Writes the WebAssembly in-memory module to the file
2693fn writeToFile(
2694 wasm: *Wasm,
2695 enabled_features: [@typeInfo(Feature.Tag).@"enum".fields.len]bool,
2696 feature_count: u32,
2697 arena: Allocator,
2698) !void {
2699 const comp = wasm.base.comp;3363 const comp = wasm.base.comp;
2700 const diags = &comp.link_diags;
2701 const gpa = comp.gpa;3364 const gpa = comp.gpa;
2702 const use_llvm = comp.config.use_llvm;3365 const rdynamic = comp.config.rdynamic;
2703 const use_lld = build_options.have_llvm and comp.config.use_lld;3366 const is_obj = comp.config.output_mode == .Obj;
2704 const shared_memory = comp.config.shared_memory;
2705 const import_memory = comp.config.import_memory;
2706 const export_memory = comp.config.export_memory;
27073367
2708 // Size of each section header3368 assert(wasm.missing_exports.entries.len == 0);
2709 const header_size = 5 + 1;3369 for (wasm.export_symbol_names) |exp_name| {
2710 // The amount of sections that will be written3370 const exp_name_interned = try wasm.internString(exp_name);
2711 var section_count: u32 = 0;3371 if (wasm.object_function_imports.getPtr(exp_name_interned)) |import| {
2712 // Index of the code section. Used to tell relocation table where the section lives.3372 if (import.resolution != .unresolved) {
2713 var code_section_index: ?u32 = null;3373 import.flags.exported = true;
2714 // Index of the data section. Used to tell relocation table where the section lives.3374 continue;
2715 var data_section_index: ?u32 = null;
2716 const is_obj = comp.config.output_mode == .Obj or (!use_llvm and use_lld);
2717
2718 var binary_bytes = std.ArrayList(u8).init(gpa);
2719 defer binary_bytes.deinit();
2720 const binary_writer = binary_bytes.writer();
2721
2722 // We write the magic bytes at the end so they will only be written
2723 // if everything succeeded as expected. So populate with 0's for now.
2724 try binary_writer.writeAll(&[_]u8{0} ** 8);
2725 // (Re)set file pointer to 0
2726 try wasm.base.file.?.setEndPos(0);
2727 try wasm.base.file.?.seekTo(0);
2728
2729 // Type section
2730 if (wasm.func_types.items.len != 0) {
2731 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2732 log.debug("Writing type section. Count: ({d})", .{wasm.func_types.items.len});
2733 for (wasm.func_types.items) |func_type| {
2734 try leb.writeUleb128(binary_writer, std.wasm.function_type);
2735 try leb.writeUleb128(binary_writer, @as(u32, @intCast(func_type.params.len)));
2736 for (func_type.params) |param_ty| {
2737 try leb.writeUleb128(binary_writer, std.wasm.valtype(param_ty));
2738 }3375 }
2739 try leb.writeUleb128(binary_writer, @as(u32, @intCast(func_type.returns.len)));3376 }
2740 for (func_type.returns) |ret_ty| {3377 if (wasm.object_global_imports.getPtr(exp_name_interned)) |import| {
2741 try leb.writeUleb128(binary_writer, std.wasm.valtype(ret_ty));3378 if (import.resolution != .unresolved) {
3379 import.flags.exported = true;
3380 continue;
2742 }3381 }
2743 }3382 }
27443383 if (wasm.object_table_imports.getPtr(exp_name_interned)) |import| {
2745 try writeVecSectionHeader(3384 if (import.resolution != .unresolved) {
2746 binary_bytes.items,3385 import.flags.exported = true;
2747 header_offset,3386 continue;
2748 .type,3387 }
2749 @intCast(binary_bytes.items.len - header_offset - header_size),3388 }
2750 @intCast(wasm.func_types.items.len),3389 try wasm.missing_exports.put(gpa, exp_name_interned, {});
2751 );
2752 section_count += 1;
2753 }3390 }
27543391
2755 // Import section3392 if (wasm.entry_name.unwrap()) |entry_name| {
2756 if (wasm.imports.count() != 0 or import_memory) {3393 if (wasm.object_function_imports.getPtr(entry_name)) |import| {
2757 const header_offset = try reserveVecSectionHeader(&binary_bytes);3394 if (import.resolution != .unresolved) {
27583395 import.flags.exported = true;
2759 var it = wasm.imports.iterator();3396 wasm.entry_resolution = import.resolution;
2760 while (it.next()) |entry| {3397 }
2761 assert(wasm.symbolLocSymbol(entry.key_ptr.*).isUndefined());
2762 const import = entry.value_ptr.*;
2763 try wasm.emitImport(binary_writer, import);
2764 }3398 }
3399 }
27653400
2766 if (import_memory) {3401 if (comp.zcu != null) {
2767 const mem_imp: Import = .{3402 // Zig always depends on a stack pointer global.
2768 .module_name = wasm.host_name,3403 // If emitting an object, it's an import. Otherwise, the linker synthesizes it.
2769 .name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory,3404 if (is_obj) {
2770 .kind = .{ .memory = wasm.memories.limits },3405 @panic("TODO");
2771 };3406 } else {
2772 try wasm.emitImport(binary_writer, mem_imp);3407 try wasm.globals.put(gpa, .__stack_pointer, {});
3408 assert(wasm.globals.entries.len - 1 == @intFromEnum(GlobalIndex.stack_pointer));
2773 }3409 }
2774
2775 try writeVecSectionHeader(
2776 binary_bytes.items,
2777 header_offset,
2778 .import,
2779 @intCast(binary_bytes.items.len - header_offset - header_size),
2780 @intCast(wasm.imports.count() + @intFromBool(import_memory)),
2781 );
2782 section_count += 1;
2783 }3410 }
27843411
2785 // Function section3412 // These loops do both recursive marking of alive symbols well as checking for undefined symbols.
2786 if (wasm.functions.count() != 0) {3413 // At the end, output functions and globals will be populated.
2787 const header_offset = try reserveVecSectionHeader(&binary_bytes);3414 for (wasm.object_function_imports.keys(), wasm.object_function_imports.values(), 0..) |name, *import, i| {
2788 for (wasm.functions.values()) |function| {3415 if (import.flags.isIncluded(rdynamic)) {
2789 try leb.writeUleb128(binary_writer, function.func.type_index);3416 try markFunctionImport(wasm, name, import, @enumFromInt(i));
2790 }3417 }
2791
2792 try writeVecSectionHeader(
2793 binary_bytes.items,
2794 header_offset,
2795 .function,
2796 @intCast(binary_bytes.items.len - header_offset - header_size),
2797 @intCast(wasm.functions.count()),
2798 );
2799 section_count += 1;
2800 }3418 }
28013419 // Also treat init functions as roots.
2802 // Table section3420 for (wasm.object_init_funcs.items) |init_func| {
2803 if (wasm.tables.items.len > 0) {3421 const func = init_func.function_index.ptr(wasm);
2804 const header_offset = try reserveVecSectionHeader(&binary_bytes);3422 if (func.object_index.ptr(wasm).is_included) {
28053423 try markFunction(wasm, init_func.function_index, false);
2806 for (wasm.tables.items) |table| {
2807 try leb.writeUleb128(binary_writer, std.wasm.reftype(table.reftype));
2808 try emitLimits(binary_writer, table.limits);
2809 }3424 }
2810
2811 try writeVecSectionHeader(
2812 binary_bytes.items,
2813 header_offset,
2814 .table,
2815 @intCast(binary_bytes.items.len - header_offset - header_size),
2816 @intCast(wasm.tables.items.len),
2817 );
2818 section_count += 1;
2819 }3425 }
3426 wasm.functions_end_prelink = @intCast(wasm.functions.entries.len);
28203427
2821 // Memory section3428 for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| {
2822 if (!import_memory) {3429 if (import.flags.isIncluded(rdynamic)) {
2823 const header_offset = try reserveVecSectionHeader(&binary_bytes);3430 try markGlobalImport(wasm, name, import, @enumFromInt(i));
28243431 }
2825 try emitLimits(binary_writer, wasm.memories.limits);
2826 try writeVecSectionHeader(
2827 binary_bytes.items,
2828 header_offset,
2829 .memory,
2830 @intCast(binary_bytes.items.len - header_offset - header_size),
2831 1, // wasm currently only supports 1 linear memory segment
2832 );
2833 section_count += 1;
2834 }3432 }
3433 wasm.globals_end_prelink = @intCast(wasm.globals.entries.len);
3434 wasm.global_exports_len = @intCast(wasm.global_exports.items.len);
28353435
2836 // Global section (used to emit stack pointer)3436 for (wasm.object_table_imports.keys(), wasm.object_table_imports.values(), 0..) |name, *import, i| {
2837 if (wasm.wasm_globals.items.len > 0) {3437 if (import.flags.isIncluded(rdynamic)) {
2838 const header_offset = try reserveVecSectionHeader(&binary_bytes);3438 try markTableImport(wasm, name, import, @enumFromInt(i));
2839
2840 for (wasm.wasm_globals.items) |global| {
2841 try binary_writer.writeByte(std.wasm.valtype(global.global_type.valtype));
2842 try binary_writer.writeByte(@intFromBool(global.global_type.mutable));
2843 try emitInit(binary_writer, global.init);
2844 }3439 }
2845
2846 try writeVecSectionHeader(
2847 binary_bytes.items,
2848 header_offset,
2849 .global,
2850 @intCast(binary_bytes.items.len - header_offset - header_size),
2851 @intCast(wasm.wasm_globals.items.len),
2852 );
2853 section_count += 1;
2854 }3440 }
28553441
2856 // Export section3442 for (wasm.object_data_imports.keys(), wasm.object_data_imports.values(), 0..) |name, *import, i| {
2857 if (wasm.exports.items.len != 0 or export_memory) {3443 if (import.flags.isIncluded(rdynamic)) {
2858 const header_offset = try reserveVecSectionHeader(&binary_bytes);3444 try markDataImport(wasm, name, import, @enumFromInt(i));
2859
2860 for (wasm.exports.items) |exp| {
2861 const name = wasm.stringSlice(exp.name);
2862 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
2863 try binary_writer.writeAll(name);
2864 try leb.writeUleb128(binary_writer, @intFromEnum(exp.kind));
2865 try leb.writeUleb128(binary_writer, exp.index);
2866 }3445 }
2867
2868 if (export_memory) {
2869 try leb.writeUleb128(binary_writer, @as(u32, @intCast("memory".len)));
2870 try binary_writer.writeAll("memory");
2871 try binary_writer.writeByte(std.wasm.externalKind(.memory));
2872 try leb.writeUleb128(binary_writer, @as(u32, 0));
2873 }
2874
2875 try writeVecSectionHeader(
2876 binary_bytes.items,
2877 header_offset,
2878 .@"export",
2879 @intCast(binary_bytes.items.len - header_offset - header_size),
2880 @intCast(wasm.exports.items.len + @intFromBool(export_memory)),
2881 );
2882 section_count += 1;
2883 }3446 }
28843447
2885 if (wasm.entry) |entry_index| {3448 // This is a wild ass guess at how to merge memories, haven't checked yet
2886 const header_offset = try reserveVecSectionHeader(&binary_bytes);3449 // what the proper way to do this is.
2887 try writeVecSectionHeader(3450 for (wasm.object_memory_imports.values()) |*memory_import| {
2888 binary_bytes.items,3451 wasm.memories.limits.min = @min(wasm.memories.limits.min, memory_import.limits_min);
2889 header_offset,3452 wasm.memories.limits.max = @max(wasm.memories.limits.max, memory_import.limits_max);
2890 .start,3453 wasm.memories.limits.flags.has_max = wasm.memories.limits.flags.has_max or memory_import.limits_has_max;
2891 @intCast(binary_bytes.items.len - header_offset - header_size),
2892 entry_index,
2893 );
2894 }3454 }
28953455
2896 // element section (function table)3456 wasm.function_imports_len_prelink = @intCast(wasm.function_imports.entries.len);
2897 if (wasm.function_table.count() > 0) {3457 wasm.data_imports_len_prelink = @intCast(wasm.data_imports.entries.len);
2898 const header_offset = try reserveVecSectionHeader(&binary_bytes);3458}
2899
2900 const table_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?;
2901 const table_sym = wasm.symbolLocSymbol(table_loc);
2902
2903 const flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually
2904 try leb.writeUleb128(binary_writer, flags);
2905 if (flags == 0x02) {
2906 try leb.writeUleb128(binary_writer, table_sym.index);
2907 }
2908 try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid
2909 if (flags == 0x02) {
2910 try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref
2911 }
2912 try leb.writeUleb128(binary_writer, @as(u32, @intCast(wasm.function_table.count())));
2913 var symbol_it = wasm.function_table.keyIterator();
2914 while (symbol_it.next()) |symbol_loc_ptr| {
2915 const sym = wasm.symbolLocSymbol(symbol_loc_ptr.*);
2916 std.debug.assert(sym.isAlive());
2917 std.debug.assert(sym.index < wasm.functions.count() + wasm.imported_functions_count);
2918 try leb.writeUleb128(binary_writer, sym.index);
2919 }
2920
2921 try writeVecSectionHeader(
2922 binary_bytes.items,
2923 header_offset,
2924 .element,
2925 @intCast(binary_bytes.items.len - header_offset - header_size),
2926 1,
2927 );
2928 section_count += 1;
2929 }
2930
2931 // When the shared-memory option is enabled, we *must* emit the 'data count' section.
2932 const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and !import_memory);
2933 if (data_segments_count != 0 and shared_memory) {
2934 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2935 try writeVecSectionHeader(
2936 binary_bytes.items,
2937 header_offset,
2938 .data_count,
2939 @intCast(binary_bytes.items.len - header_offset - header_size),
2940 @intCast(data_segments_count),
2941 );
2942 }
29433459
2944 // Code section3460pub fn markFunctionImport(
2945 if (wasm.code_section_index != .none) {3461 wasm: *Wasm,
2946 const header_offset = try reserveVecSectionHeader(&binary_bytes);3462 name: String,
2947 const start_offset = binary_bytes.items.len - 5; // minus 5 so start offset is 5 to include entry count3463 import: *FunctionImport,
3464 func_index: FunctionImport.Index,
3465) link.File.FlushError!void {
3466 if (import.flags.alive) return;
3467 import.flags.alive = true;
29483468
2949 var func_it = wasm.functions.iterator();3469 const comp = wasm.base.comp;
2950 while (func_it.next()) |entry| {3470 const gpa = comp.gpa;
2951 const sym_loc: SymbolLoc = .{ .index = entry.value_ptr.sym_index, .file = entry.key_ptr.file };
2952 const atom_index = wasm.symbol_atom.get(sym_loc).?;
2953 const atom = wasm.getAtomPtr(atom_index);
29543471
2955 if (!is_obj) {3472 try wasm.functions.ensureUnusedCapacity(gpa, 1);
2956 atom.resolveRelocs(wasm);3473
2957 }3474 if (import.resolution == .unresolved) {
2958 atom.offset = @intCast(binary_bytes.items.len - start_offset);3475 if (name == wasm.preloaded_strings.__wasm_init_memory) {
2959 try leb.writeUleb128(binary_writer, atom.size);3476 try wasm.resolveFunctionSynthetic(import, .__wasm_init_memory, &.{}, &.{});
2960 try binary_writer.writeAll(atom.code.items);3477 } else if (name == wasm.preloaded_strings.__wasm_apply_global_tls_relocs) {
3478 try wasm.resolveFunctionSynthetic(import, .__wasm_apply_global_tls_relocs, &.{}, &.{});
3479 } else if (name == wasm.preloaded_strings.__wasm_call_ctors) {
3480 try wasm.resolveFunctionSynthetic(import, .__wasm_call_ctors, &.{}, &.{});
3481 } else if (name == wasm.preloaded_strings.__wasm_init_tls) {
3482 try wasm.resolveFunctionSynthetic(import, .__wasm_init_tls, &.{.i32}, &.{});
3483 } else {
3484 try wasm.function_imports.put(gpa, name, .fromObject(func_index, wasm));
2961 }3485 }
3486 } else {
3487 try markFunction(wasm, import.resolution.unpack(wasm).object_function, import.flags.exported);
3488 }
3489}
29623490
2963 try writeVecSectionHeader(3491/// Recursively mark alive everything referenced by the function.
2964 binary_bytes.items,3492fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex, override_export: bool) link.File.FlushError!void {
2965 header_offset,3493 const comp = wasm.base.comp;
2966 .code,3494 const gpa = comp.gpa;
2967 @intCast(binary_bytes.items.len - header_offset - header_size),3495 const gop = try wasm.functions.getOrPut(gpa, .fromObjectFunction(wasm, i));
2968 @intCast(wasm.functions.count()),3496 if (gop.found_existing) return;
2969 );
2970 code_section_index = section_count;
2971 section_count += 1;
2972 }
2973
2974 // Data section
2975 if (data_segments_count != 0) {
2976 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2977
2978 var it = wasm.data_segments.iterator();
2979 var segment_count: u32 = 0;
2980 while (it.next()) |entry| {
2981 // do not output 'bss' section unless we import memory and therefore
2982 // want to guarantee the data is zero initialized
2983 if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;
2984 const segment_index = entry.value_ptr.*;
2985 const segment = wasm.segmentPtr(segment_index);
2986 if (segment.size == 0) continue; // do not emit empty segments
2987 segment_count += 1;
2988 var atom_index = wasm.atoms.get(segment_index).?;
2989
2990 try leb.writeUleb128(binary_writer, segment.flags);
2991 if (segment.flags & @intFromEnum(Wasm.Segment.Flag.WASM_DATA_SEGMENT_HAS_MEMINDEX) != 0) {
2992 try leb.writeUleb128(binary_writer, @as(u32, 0)); // memory is always index 0 as we only have 1 memory entry
2993 }
2994 // when a segment is passive, it's initialized during runtime.
2995 if (!segment.isPassive()) {
2996 try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(segment.offset)) });
2997 }
2998 // offset into data section
2999 try leb.writeUleb128(binary_writer, segment.size);
3000
3001 // fill in the offset table and the data segments
3002 var current_offset: u32 = 0;
3003 while (true) {
3004 const atom = wasm.getAtomPtr(atom_index);
3005 if (!is_obj) {
3006 atom.resolveRelocs(wasm);
3007 }
30083497
3009 // Pad with zeroes to ensure all segments are aligned3498 const rdynamic = comp.config.rdynamic;
3010 if (current_offset != atom.offset) {3499 const is_obj = comp.config.output_mode == .Obj;
3011 const diff = atom.offset - current_offset;3500 const function = i.ptr(wasm);
3012 try binary_writer.writeByteNTimes(0, diff);3501 markObject(wasm, function.object_index);
3013 current_offset += diff;
3014 }
3015 assert(current_offset == atom.offset);
3016 assert(atom.code.items.len == atom.size);
3017 try binary_writer.writeAll(atom.code.items);
30183502
3019 current_offset += atom.size;3503 if (!is_obj and (override_export or function.flags.isExported(rdynamic))) {
3020 if (atom.prev != .null) {3504 const symbol_name = function.name.unwrap().?;
3021 atom_index = atom.prev;3505 if (!override_export and function.flags.visibility_hidden) {
3022 } else {3506 try wasm.hidden_function_exports.put(gpa, symbol_name, @enumFromInt(gop.index));
3023 // also pad with zeroes when last atom to ensure3507 } else {
3024 // segments are aligned.3508 try wasm.function_exports.put(gpa, symbol_name, @enumFromInt(gop.index));
3025 if (current_offset != segment.size) {
3026 try binary_writer.writeByteNTimes(0, segment.size - current_offset);
3027 current_offset += segment.size - current_offset;
3028 }
3029 break;
3030 }
3031 }
3032 assert(current_offset == segment.size);
3033 }3509 }
3034
3035 try writeVecSectionHeader(
3036 binary_bytes.items,
3037 header_offset,
3038 .data,
3039 @intCast(binary_bytes.items.len - header_offset - header_size),
3040 @intCast(segment_count),
3041 );
3042 data_section_index = section_count;
3043 section_count += 1;
3044 }3510 }
30453511
3046 if (is_obj) {3512 try wasm.markRelocations(function.relocations(wasm));
3047 // relocations need to point to the index of a symbol in the final symbol table. To save memory,3513}
3048 // we never store all symbols in a single table, but store a location reference instead.
3049 // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections.
3050 var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena);
3051 try wasm.emitLinkSection(&binary_bytes, &symbol_table);
3052 if (code_section_index) |code_index| {
3053 try wasm.emitCodeRelocations(&binary_bytes, code_index, symbol_table);
3054 }
3055 if (data_section_index) |data_index| {
3056 try wasm.emitDataRelocations(&binary_bytes, data_index, symbol_table);
3057 }
3058 } else if (comp.config.debug_format != .strip) {
3059 try wasm.emitNameSection(&binary_bytes, arena);
3060 }
3061
3062 if (comp.config.debug_format != .strip) {
3063 // The build id must be computed on the main sections only,
3064 // so we have to do it now, before the debug sections.
3065 switch (wasm.base.build_id) {
3066 .none => {},
3067 .fast => {
3068 var id: [16]u8 = undefined;
3069 std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{});
3070 var uuid: [36]u8 = undefined;
3071 _ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{
3072 std.fmt.fmtSliceHexLower(id[0..4]),
3073 std.fmt.fmtSliceHexLower(id[4..6]),
3074 std.fmt.fmtSliceHexLower(id[6..8]),
3075 std.fmt.fmtSliceHexLower(id[8..10]),
3076 std.fmt.fmtSliceHexLower(id[10..]),
3077 });
3078 try emitBuildIdSection(&binary_bytes, &uuid);
3079 },
3080 .hexstring => |hs| {
3081 var buffer: [32 * 2]u8 = undefined;
3082 const str = std.fmt.bufPrint(&buffer, "{s}", .{
3083 std.fmt.fmtSliceHexLower(hs.toSlice()),
3084 }) catch unreachable;
3085 try emitBuildIdSection(&binary_bytes, str);
3086 },
3087 else => |mode| {
3088 var err = try diags.addErrorWithNotes(0);
3089 try err.addMsg("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)});
3090 },
3091 }
30923514
3093 var debug_bytes = std.ArrayList(u8).init(gpa);3515fn markObject(wasm: *Wasm, i: ObjectIndex) void {
3094 defer debug_bytes.deinit();3516 i.ptr(wasm).is_included = true;
3517}
30953518
3096 inline for (@typeInfo(CustomSections).@"struct".fields) |field| {3519/// Recursively mark alive everything referenced by the global.
3097 if (@field(wasm.custom_sections, field.name).index.unwrap()) |index| {3520fn markGlobalImport(
3098 var atom = wasm.getAtomPtr(wasm.atoms.get(index).?);3521 wasm: *Wasm,
3099 while (true) {3522 name: String,
3100 atom.resolveRelocs(wasm);3523 import: *GlobalImport,
3101 try debug_bytes.appendSlice(atom.code.items);3524 global_index: GlobalImport.Index,
3102 if (atom.prev == .null) break;3525) link.File.FlushError!void {
3103 atom = wasm.getAtomPtr(atom.prev);3526 if (import.flags.alive) return;
3104 }3527 import.flags.alive = true;
3105 try emitDebugSection(&binary_bytes, debug_bytes.items, field.name);
3106 debug_bytes.clearRetainingCapacity();
3107 }
3108 }
31093528
3110 try emitProducerSection(&binary_bytes);3529 const comp = wasm.base.comp;
3111 if (feature_count > 0) {3530 const gpa = comp.gpa;
3112 try emitFeaturesSection(&binary_bytes, &enabled_features, feature_count);
3113 }
3114 }
31153531
3116 // Only when writing all sections executed properly we write the magic3532 try wasm.globals.ensureUnusedCapacity(gpa, 1);
3117 // bytes. This allows us to easily detect what went wrong while generating3533
3118 // the final binary.3534 if (import.resolution == .unresolved) {
3119 {3535 if (name == wasm.preloaded_strings.__heap_base) {
3120 const src = std.wasm.magic ++ std.wasm.version;3536 import.resolution = .__heap_base;
3121 binary_bytes.items[0..src.len].* = src;3537 wasm.globals.putAssumeCapacity(.__heap_base, {});
3538 } else if (name == wasm.preloaded_strings.__heap_end) {
3539 import.resolution = .__heap_end;
3540 wasm.globals.putAssumeCapacity(.__heap_end, {});
3541 } else if (name == wasm.preloaded_strings.__stack_pointer) {
3542 import.resolution = .__stack_pointer;
3543 wasm.globals.putAssumeCapacity(.__stack_pointer, {});
3544 } else if (name == wasm.preloaded_strings.__tls_align) {
3545 import.resolution = .__tls_align;
3546 wasm.globals.putAssumeCapacity(.__tls_align, {});
3547 } else if (name == wasm.preloaded_strings.__tls_base) {
3548 import.resolution = .__tls_base;
3549 wasm.globals.putAssumeCapacity(.__tls_base, {});
3550 } else if (name == wasm.preloaded_strings.__tls_size) {
3551 import.resolution = .__tls_size;
3552 wasm.globals.putAssumeCapacity(.__tls_size, {});
3553 } else {
3554 try wasm.global_imports.put(gpa, name, .fromObject(global_index, wasm));
3555 }
3556 } else {
3557 try markGlobal(wasm, import.resolution.unpack(wasm).object_global, import.flags.exported);
3122 }3558 }
3123
3124 // finally, write the entire binary into the file.
3125 var iovec = [_]std.posix.iovec_const{.{
3126 .base = binary_bytes.items.ptr,
3127 .len = binary_bytes.items.len,
3128 }};
3129 try wasm.base.file.?.writevAll(&iovec);
3130}
3131
3132fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: []const u8) !void {
3133 if (data.len == 0) return;
3134 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3135 const writer = binary_bytes.writer();
3136 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
3137 try writer.writeAll(name);
3138
3139 const start = binary_bytes.items.len - header_offset;
3140 log.debug("Emit debug section: '{s}' start=0x{x:0>8} end=0x{x:0>8}", .{ name, start, start + data.len });
3141 try writer.writeAll(data);
3142
3143 try writeCustomSectionHeader(
3144 binary_bytes.items,
3145 header_offset,
3146 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3147 );
3148}3559}
31493560
3150fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void {3561fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex, override_export: bool) link.File.FlushError!void {
3151 const header_offset = try reserveCustomSectionHeader(binary_bytes);3562 const comp = wasm.base.comp;
3563 const gpa = comp.gpa;
3564 const gop = try wasm.globals.getOrPut(gpa, .fromObjectGlobal(wasm, i));
3565 if (gop.found_existing) return;
31523566
3153 const writer = binary_bytes.writer();3567 const rdynamic = comp.config.rdynamic;
3154 const producers = "producers";3568 const is_obj = comp.config.output_mode == .Obj;
3155 try leb.writeUleb128(writer, @as(u32, @intCast(producers.len)));3569 const global = i.ptr(wasm);
3156 try writer.writeAll(producers);
31573570
3158 try leb.writeUleb128(writer, @as(u32, 2)); // 2 fields: Language + processed-by3571 if (!is_obj and (override_export or global.flags.isExported(rdynamic))) try wasm.global_exports.append(gpa, .{
3572 .name = global.name.unwrap().?,
3573 .global_index = @enumFromInt(gop.index),
3574 });
31593575
3160 // used for the Zig version3576 try wasm.markRelocations(global.relocations(wasm));
3161 var version_buf: [100]u8 = undefined;3577}
3162 const version = try std.fmt.bufPrint(&version_buf, "{}", .{build_options.semver});
31633578
3164 // language field3579fn markTableImport(
3165 {3580 wasm: *Wasm,
3166 const language = "language";3581 name: String,
3167 try leb.writeUleb128(writer, @as(u32, @intCast(language.len)));3582 import: *TableImport,
3168 try writer.writeAll(language);3583 table_index: TableImport.Index,
3584) link.File.FlushError!void {
3585 if (import.flags.alive) return;
3586 import.flags.alive = true;
31693587
3170 // field_value_count (TODO: Parse object files for producer sections to detect their language)3588 const comp = wasm.base.comp;
3171 try leb.writeUleb128(writer, @as(u32, 1));3589 const gpa = comp.gpa;
31723590
3173 // versioned name3591 try wasm.tables.ensureUnusedCapacity(gpa, 1);
3174 {
3175 try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig"
3176 try writer.writeAll("Zig");
31773592
3178 try leb.writeUleb128(writer, @as(u32, @intCast(version.len)));3593 if (import.resolution == .unresolved) {
3179 try writer.writeAll(version);3594 if (name == wasm.preloaded_strings.__indirect_function_table) {
3595 import.resolution = .__indirect_function_table;
3596 wasm.tables.putAssumeCapacity(.__indirect_function_table, {});
3597 } else {
3598 try wasm.table_imports.put(gpa, name, table_index);
3180 }3599 }
3600 } else {
3601 wasm.tables.putAssumeCapacity(import.resolution, {});
3602 // Tables have no relocations.
3181 }3603 }
3604}
31823605
3183 // processed-by field3606fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.File.FlushError!void {
3184 {3607 const comp = wasm.base.comp;
3185 const processed_by = "processed-by";3608 const segment = segment_index.ptr(wasm);
3186 try leb.writeUleb128(writer, @as(u32, @intCast(processed_by.len)));3609 if (segment.flags.alive) return;
3187 try writer.writeAll(processed_by);3610 segment.flags.alive = true;
3188
3189 // field_value_count (TODO: Parse object files for producer sections to detect other used tools)
3190 try leb.writeUleb128(writer, @as(u32, 1));
3191
3192 // versioned name
3193 {
3194 try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig"
3195 try writer.writeAll("Zig");
31963611
3197 try leb.writeUleb128(writer, @as(u32, @intCast(version.len)));3612 wasm.any_passive_inits = wasm.any_passive_inits or segment.flags.is_passive or
3198 try writer.writeAll(version);3613 (comp.config.import_memory and !wasm.isBss(segment.name));
3199 }
3200 }
32013614
3202 try writeCustomSectionHeader(3615 try wasm.data_segments.put(comp.gpa, .pack(wasm, .{ .object = segment_index }), {});
3203 binary_bytes.items,3616 try wasm.markRelocations(segment.relocations(wasm));
3204 header_offset,
3205 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3206 );
3207}3617}
32083618
3209fn emitBuildIdSection(binary_bytes: *std.ArrayList(u8), build_id: []const u8) !void {3619pub fn markDataImport(
3210 const header_offset = try reserveCustomSectionHeader(binary_bytes);3620 wasm: *Wasm,
32113621 name: String,
3212 const writer = binary_bytes.writer();3622 import: *ObjectDataImport,
3213 const hdr_build_id = "build_id";3623 data_index: ObjectDataImport.Index,
3214 try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len)));3624) link.File.FlushError!void {
3215 try writer.writeAll(hdr_build_id);3625 if (import.flags.alive) return;
3626 import.flags.alive = true;
32163627
3217 try leb.writeUleb128(writer, @as(u32, 1));3628 const comp = wasm.base.comp;
3218 try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len)));3629 const gpa = comp.gpa;
3219 try writer.writeAll(build_id);
32203630
3221 try writeCustomSectionHeader(3631 if (import.resolution == .unresolved) {
3222 binary_bytes.items,3632 if (name == wasm.preloaded_strings.__heap_base) {
3223 header_offset,3633 import.resolution = .__heap_base;
3224 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),3634 wasm.data_segments.putAssumeCapacity(.__heap_base, {});
3225 );3635 } else if (name == wasm.preloaded_strings.__heap_end) {
3636 import.resolution = .__heap_end;
3637 wasm.data_segments.putAssumeCapacity(.__heap_end, {});
3638 } else {
3639 try wasm.data_imports.put(gpa, name, .fromObject(data_index, wasm));
3640 }
3641 } else if (import.resolution.objectDataSegment(wasm)) |segment_index| {
3642 try markDataSegment(wasm, segment_index);
3643 }
3226}3644}
32273645
3228fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void {3646fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.File.FlushError!void {
3229 const header_offset = try reserveCustomSectionHeader(binary_bytes);3647 const gpa = wasm.base.comp.gpa;
3648 for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, pointee, offset| {
3649 if (offset >= relocs.end) break;
3650 switch (tag) {
3651 .function_import_index_leb,
3652 .function_import_index_i32,
3653 .function_import_offset_i32,
3654 .function_import_offset_i64,
3655 => {
3656 const name = pointee.symbol_name;
3657 const i: FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(name).?);
3658 try markFunctionImport(wasm, name, i.value(wasm), i);
3659 },
3660 .table_import_index_sleb,
3661 .table_import_index_i32,
3662 .table_import_index_sleb64,
3663 .table_import_index_i64,
3664 .table_import_index_rel_sleb,
3665 .table_import_index_rel_sleb64,
3666 => {
3667 const name = pointee.symbol_name;
3668 try wasm.object_indirect_function_import_set.put(gpa, name, {});
3669 const i: FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(name).?);
3670 try markFunctionImport(wasm, name, i.value(wasm), i);
3671 },
3672 .global_import_index_leb, .global_import_index_i32 => {
3673 const name = pointee.symbol_name;
3674 const i: GlobalImport.Index = @enumFromInt(wasm.object_global_imports.getIndex(name).?);
3675 try markGlobalImport(wasm, name, i.value(wasm), i);
3676 },
3677 .table_import_number_leb => {
3678 const name = pointee.symbol_name;
3679 const i: TableImport.Index = @enumFromInt(wasm.object_table_imports.getIndex(name).?);
3680 try markTableImport(wasm, name, i.value(wasm), i);
3681 },
3682 .memory_addr_import_leb,
3683 .memory_addr_import_sleb,
3684 .memory_addr_import_i32,
3685 .memory_addr_import_rel_sleb,
3686 .memory_addr_import_leb64,
3687 .memory_addr_import_sleb64,
3688 .memory_addr_import_i64,
3689 .memory_addr_import_rel_sleb64,
3690 .memory_addr_import_tls_sleb,
3691 .memory_addr_import_locrel_i32,
3692 .memory_addr_import_tls_sleb64,
3693 => {
3694 const name = pointee.symbol_name;
3695 const i = ObjectDataImport.Index.fromSymbolName(wasm, name).?;
3696 try markDataImport(wasm, name, i.value(wasm), i);
3697 },
3698
3699 .function_index_leb,
3700 .function_index_i32,
3701 .function_offset_i32,
3702 .function_offset_i64,
3703 => try markFunction(wasm, pointee.function.chaseWeak(wasm), false),
3704 .table_index_sleb,
3705 .table_index_i32,
3706 .table_index_sleb64,
3707 .table_index_i64,
3708 .table_index_rel_sleb,
3709 .table_index_rel_sleb64,
3710 => {
3711 const function = pointee.function;
3712 try wasm.object_indirect_function_set.put(gpa, function, {});
3713 try markFunction(wasm, function.chaseWeak(wasm), false);
3714 },
3715 .global_index_leb,
3716 .global_index_i32,
3717 => try markGlobal(wasm, pointee.global.chaseWeak(wasm), false),
3718 .table_number_leb,
3719 => try markTable(wasm, pointee.table.chaseWeak(wasm)),
3720
3721 .section_offset_i32 => {
3722 log.warn("TODO: ensure section {d} is included in output", .{pointee.section});
3723 },
32303724
3231 const writer = binary_bytes.writer();3725 .memory_addr_leb,
3232 const target_features = "target_features";3726 .memory_addr_sleb,
3233 try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len)));3727 .memory_addr_i32,
3234 try writer.writeAll(target_features);3728 .memory_addr_rel_sleb,
3729 .memory_addr_leb64,
3730 .memory_addr_sleb64,
3731 .memory_addr_i64,
3732 .memory_addr_rel_sleb64,
3733 .memory_addr_tls_sleb,
3734 .memory_addr_locrel_i32,
3735 .memory_addr_tls_sleb64,
3736 => try markDataSegment(wasm, pointee.data.ptr(wasm).segment),
32353737
3236 try leb.writeUleb128(writer, features_count);3738 .type_index_leb => continue,
3237 for (enabled_features, 0..) |enabled, feature_index| {
3238 if (enabled) {
3239 const feature: Feature = .{ .prefix = .used, .tag = @as(Feature.Tag, @enumFromInt(feature_index)) };
3240 try leb.writeUleb128(writer, @intFromEnum(feature.prefix));
3241 var buf: [100]u8 = undefined;
3242 const string = try std.fmt.bufPrint(&buf, "{}", .{feature.tag});
3243 try leb.writeUleb128(writer, @as(u32, @intCast(string.len)));
3244 try writer.writeAll(string);
3245 }3739 }
3246 }3740 }
3741}
32473742
3248 try writeCustomSectionHeader(3743fn markTable(wasm: *Wasm, i: ObjectTableIndex) link.File.FlushError!void {
3249 binary_bytes.items,3744 try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(i), {});
3250 header_offset,
3251 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3252 );
3253}3745}
32543746
3255fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void {3747pub fn flushModule(
3748 wasm: *Wasm,
3749 arena: Allocator,
3750 tid: Zcu.PerThread.Id,
3751 prog_node: std.Progress.Node,
3752) link.File.FlushError!void {
3753 // The goal is to never use this because it's only needed if we need to
3754 // write to InternPool, but flushModule is too late to be writing to the
3755 // InternPool.
3756 _ = tid;
3256 const comp = wasm.base.comp;3757 const comp = wasm.base.comp;
3257 const import_memory = comp.config.import_memory;3758 const use_lld = build_options.have_llvm and comp.config.use_lld;
3258 const Name = struct {3759 const diags = &comp.link_diags;
3259 index: u32,3760 const gpa = comp.gpa;
3260 name: []const u8,
3261
3262 fn lessThan(context: void, lhs: @This(), rhs: @This()) bool {
3263 _ = context;
3264 return lhs.index < rhs.index;
3265 }
3266 };
32673761
3268 // we must de-duplicate symbols that point to the same function3762 if (wasm.llvm_object) |llvm_object| {
3269 var funcs = std.AutoArrayHashMap(u32, Name).init(arena);3763 try wasm.base.emitLlvmObject(arena, llvm_object, prog_node);
3270 try funcs.ensureUnusedCapacity(wasm.functions.count() + wasm.imported_functions_count);3764 if (use_lld) return;
3271 var globals = try std.ArrayList(Name).initCapacity(arena, wasm.wasm_globals.items.len + wasm.imported_globals_count);
3272 var segments = try std.ArrayList(Name).initCapacity(arena, wasm.data_segments.count());
3273
3274 for (wasm.resolved_symbols.keys()) |sym_loc| {
3275 const symbol = wasm.symbolLocSymbol(sym_loc).*;
3276 if (symbol.isDead()) {
3277 continue;
3278 }
3279 const name = wasm.symbolLocName(sym_loc);
3280 switch (symbol.tag) {
3281 .function => {
3282 const gop = funcs.getOrPutAssumeCapacity(symbol.index);
3283 if (!gop.found_existing) {
3284 gop.value_ptr.* = .{ .index = symbol.index, .name = name };
3285 }
3286 },
3287 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = name }),
3288 else => {},
3289 }
3290 }
3291 // data segments are already 'ordered'
3292 var data_segment_index: u32 = 0;
3293 for (wasm.data_segments.keys()) |key| {
3294 // bss section is not emitted when this condition holds true, so we also
3295 // do not output a name for it.
3296 if (!import_memory and std.mem.eql(u8, key, ".bss")) continue;
3297 segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key });
3298 data_segment_index += 1;
3299 }3765 }
33003766
3301 mem.sort(Name, funcs.values(), {}, Name.lessThan);3767 if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items);
3302 mem.sort(Name, globals.items, {}, Name.lessThan);
3303
3304 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3305 const writer = binary_bytes.writer();
3306 try leb.writeUleb128(writer, @as(u32, @intCast("name".len)));
3307 try writer.writeAll("name");
33083768
3309 try wasm.emitNameSubsection(.function, funcs.values(), writer);3769 if (wasm.base.zcu_object_sub_path) |path| {
3310 try wasm.emitNameSubsection(.global, globals.items, writer);3770 const module_obj_path: Path = .{
3311 try wasm.emitNameSubsection(.data_segment, segments.items, writer);3771 .root_dir = wasm.base.emit.root_dir,
3772 .sub_path = if (fs.path.dirname(wasm.base.emit.sub_path)) |dirname|
3773 try fs.path.join(arena, &.{ dirname, path })
3774 else
3775 path,
3776 };
3777 openParseObjectReportingFailure(wasm, module_obj_path);
3778 try prelink(wasm, prog_node);
3779 }
33123780
3313 try writeCustomSectionHeader(3781 const tracy = trace(@src());
3314 binary_bytes.items,3782 defer tracy.end();
3315 header_offset,
3316 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3317 );
3318}
33193783
3320fn emitNameSubsection(wasm: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void {3784 const sub_prog_node = prog_node.start("Wasm Flush", 0);
3321 const gpa = wasm.base.comp.gpa;3785 defer sub_prog_node.end();
33223786
3323 // We must emit subsection size, so first write to a temporary list3787 const functions_end_zcu: u32 = @intCast(wasm.functions.entries.len);
3324 var section_list = std.ArrayList(u8).init(gpa);3788 defer wasm.functions.shrinkRetainingCapacity(functions_end_zcu);
3325 defer section_list.deinit();
3326 const sub_writer = section_list.writer();
33273789
3328 try leb.writeUleb128(sub_writer, @as(u32, @intCast(names.len)));3790 const globals_end_zcu: u32 = @intCast(wasm.globals.entries.len);
3329 for (names) |name| {3791 defer wasm.globals.shrinkRetainingCapacity(globals_end_zcu);
3330 log.debug("Emit symbol '{s}' type({s})", .{ name.name, @tagName(section_id) });
3331 try leb.writeUleb128(sub_writer, name.index);
3332 try leb.writeUleb128(sub_writer, @as(u32, @intCast(name.name.len)));
3333 try sub_writer.writeAll(name.name);
3334 }
33353792
3336 // From now, write to the actual writer3793 const function_exports_end_zcu: u32 = @intCast(wasm.function_exports.entries.len);
3337 try leb.writeUleb128(writer, @intFromEnum(section_id));3794 defer wasm.function_exports.shrinkRetainingCapacity(function_exports_end_zcu);
3338 try leb.writeUleb128(writer, @as(u32, @intCast(section_list.items.len)));
3339 try writer.writeAll(section_list.items);
3340}
33413795
3342fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void {3796 const hidden_function_exports_end_zcu: u32 = @intCast(wasm.hidden_function_exports.entries.len);
3343 try writer.writeByte(limits.flags);3797 defer wasm.hidden_function_exports.shrinkRetainingCapacity(hidden_function_exports_end_zcu);
3344 try leb.writeUleb128(writer, limits.min);
3345 if (limits.hasFlag(.WASM_LIMITS_FLAG_HAS_MAX)) {
3346 try leb.writeUleb128(writer, limits.max);
3347 }
3348}
33493798
3350fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {3799 wasm.flush_buffer.clear();
3351 switch (init_expr) {3800 try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{});
3352 .i32_const => |val| {3801 try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values());
3353 try writer.writeByte(std.wasm.opcode(.i32_const));3802 try wasm.flush_buffer.global_imports.reinit(gpa, wasm.global_imports.keys(), wasm.global_imports.values());
3354 try leb.writeIleb128(writer, val);3803 try wasm.flush_buffer.data_imports.reinit(gpa, wasm.data_imports.keys(), wasm.data_imports.values());
3355 },
3356 .i64_const => |val| {
3357 try writer.writeByte(std.wasm.opcode(.i64_const));
3358 try leb.writeIleb128(writer, val);
3359 },
3360 .f32_const => |val| {
3361 try writer.writeByte(std.wasm.opcode(.f32_const));
3362 try writer.writeInt(u32, @bitCast(val), .little);
3363 },
3364 .f64_const => |val| {
3365 try writer.writeByte(std.wasm.opcode(.f64_const));
3366 try writer.writeInt(u64, @bitCast(val), .little);
3367 },
3368 .global_get => |val| {
3369 try writer.writeByte(std.wasm.opcode(.global_get));
3370 try leb.writeUleb128(writer, val);
3371 },
3372 }
3373 try writer.writeByte(std.wasm.opcode(.end));
3374}
33753804
3376fn emitImport(wasm: *Wasm, writer: anytype, import: Import) !void {3805 return wasm.flush_buffer.finish(wasm) catch |err| switch (err) {
3377 const module_name = wasm.stringSlice(import.module_name);3806 error.OutOfMemory => return error.OutOfMemory,
3378 try leb.writeUleb128(writer, @as(u32, @intCast(module_name.len)));3807 error.LinkFailure => return error.LinkFailure,
3379 try writer.writeAll(module_name);3808 else => |e| return diags.fail("failed to flush wasm: {s}", .{@errorName(e)}),
33803809 };
3381 const name = wasm.stringSlice(import.name);
3382 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
3383 try writer.writeAll(name);
3384
3385 try writer.writeByte(@intFromEnum(import.kind));
3386 switch (import.kind) {
3387 .function => |type_index| try leb.writeUleb128(writer, type_index),
3388 .global => |global_type| {
3389 try leb.writeUleb128(writer, std.wasm.valtype(global_type.valtype));
3390 try writer.writeByte(@intFromBool(global_type.mutable));
3391 },
3392 .table => |table| {
3393 try leb.writeUleb128(writer, std.wasm.reftype(table.reftype));
3394 try emitLimits(writer, table.limits);
3395 },
3396 .memory => |limits| {
3397 try emitLimits(writer, limits);
3398 },
3399 }
3400}3810}
34013811
3402fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {3812fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {
...@@ -3406,6 +3816,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3406,6 +3816,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
3406 defer tracy.end();3816 defer tracy.end();
34073817
3408 const comp = wasm.base.comp;3818 const comp = wasm.base.comp;
3819 const diags = &comp.link_diags;
3409 const shared_memory = comp.config.shared_memory;3820 const shared_memory = comp.config.shared_memory;
3410 const export_memory = comp.config.export_memory;3821 const export_memory = comp.config.export_memory;
3411 const import_memory = comp.config.import_memory;3822 const import_memory = comp.config.import_memory;
...@@ -3459,7 +3870,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3459,7 +3870,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
3459 }3870 }
3460 try man.addOptionalFile(module_obj_path);3871 try man.addOptionalFile(module_obj_path);
3461 try man.addOptionalFilePath(compiler_rt_path);3872 try man.addOptionalFilePath(compiler_rt_path);
3462 man.hash.addOptionalBytes(wasm.optionalStringSlice(wasm.entry_name));3873 man.hash.addOptionalBytes(wasm.entry_name.slice(wasm));
3463 man.hash.add(wasm.base.stack_size);3874 man.hash.add(wasm.base.stack_size);
3464 man.hash.add(wasm.base.build_id);3875 man.hash.add(wasm.base.build_id);
3465 man.hash.add(import_memory);3876 man.hash.add(import_memory);
...@@ -3608,7 +4019,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3608,7 +4019,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
3608 try argv.append("--export-dynamic");4019 try argv.append("--export-dynamic");
3609 }4020 }
36104021
3611 if (wasm.optionalStringSlice(wasm.entry_name)) |entry_name| {4022 if (wasm.entry_name.slice(wasm)) |entry_name| {
3612 try argv.appendSlice(&.{ "--entry", entry_name });4023 try argv.appendSlice(&.{ "--entry", entry_name });
3613 } else {4024 } else {
3614 try argv.append("--no-entry");4025 try argv.append("--no-entry");
...@@ -3750,14 +4161,12 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3750,14 +4161,12 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
3750 switch (term) {4161 switch (term) {
3751 .Exited => |code| {4162 .Exited => |code| {
3752 if (code != 0) {4163 if (code != 0) {
3753 const diags = &comp.link_diags;
3754 diags.lockAndParseLldStderr(linker_command, stderr);4164 diags.lockAndParseLldStderr(linker_command, stderr);
3755 return error.LLDReportedFailure;4165 return error.LinkFailure;
3756 }4166 }
3757 },4167 },
3758 else => {4168 else => {
3759 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });4169 return diags.fail("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
3760 return error.LLDCrashed;
3761 },4170 },
3762 }4171 }
37634172
...@@ -3771,7 +4180,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3771,7 +4180,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
3771 if (comp.clang_passthrough_mode) {4180 if (comp.clang_passthrough_mode) {
3772 std.process.exit(exit_code);4181 std.process.exit(exit_code);
3773 } else {4182 } else {
3774 return error.LLDReportedFailure;4183 return diags.fail("{s} returned exit code {d}:\n{s}", .{ argv.items[0], exit_code });
3775 }4184 }
3776 }4185 }
3777 }4186 }
...@@ -3811,969 +4220,507 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3811,969 +4220,507 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
3811 }4220 }
3812}4221}
38134222
3814fn reserveVecSectionHeader(bytes: *std.ArrayList(u8)) !u32 {4223fn defaultEntrySymbolName(
3815 // section id + fixed leb contents size + fixed leb vector length4224 preloaded_strings: *const PreloadedStrings,
3816 const header_size = 1 + 5 + 5;4225 wasi_exec_model: std.builtin.WasiExecModel,
3817 const offset = @as(u32, @intCast(bytes.items.len));4226) String {
3818 try bytes.appendSlice(&[_]u8{0} ** header_size);4227 return switch (wasi_exec_model) {
3819 return offset;4228 .reactor => preloaded_strings._initialize,
4229 .command => preloaded_strings._start,
4230 };
4231}
4232
4233pub fn internOptionalString(wasm: *Wasm, optional_bytes: ?[]const u8) Allocator.Error!OptionalString {
4234 const bytes = optional_bytes orelse return .none;
4235 const string = try internString(wasm, bytes);
4236 return string.toOptional();
4237}
4238
4239pub fn internString(wasm: *Wasm, bytes: []const u8) Allocator.Error!String {
4240 assert(mem.indexOfScalar(u8, bytes, 0) == null);
4241 wasm.string_bytes_lock.lock();
4242 defer wasm.string_bytes_lock.unlock();
4243 const gpa = wasm.base.comp.gpa;
4244 const gop = try wasm.string_table.getOrPutContextAdapted(
4245 gpa,
4246 @as([]const u8, bytes),
4247 @as(String.TableIndexAdapter, .{ .bytes = wasm.string_bytes.items }),
4248 @as(String.TableContext, .{ .bytes = wasm.string_bytes.items }),
4249 );
4250 if (gop.found_existing) return gop.key_ptr.*;
4251
4252 try wasm.string_bytes.ensureUnusedCapacity(gpa, bytes.len + 1);
4253 const new_off: String = @enumFromInt(wasm.string_bytes.items.len);
4254
4255 wasm.string_bytes.appendSliceAssumeCapacity(bytes);
4256 wasm.string_bytes.appendAssumeCapacity(0);
4257
4258 gop.key_ptr.* = new_off;
4259
4260 return new_off;
3820}4261}
38214262
3822fn reserveCustomSectionHeader(bytes: *std.ArrayList(u8)) !u32 {4263// TODO implement instead by appending to string_bytes
3823 // unlike regular section, we don't emit the count4264pub fn internStringFmt(wasm: *Wasm, comptime format: []const u8, args: anytype) Allocator.Error!String {
3824 const header_size = 1 + 5;4265 var buffer: [32]u8 = undefined;
3825 const offset = @as(u32, @intCast(bytes.items.len));4266 const slice = std.fmt.bufPrint(&buffer, format, args) catch unreachable;
3826 try bytes.appendSlice(&[_]u8{0} ** header_size);4267 return internString(wasm, slice);
3827 return offset;
3828}4268}
38294269
3830fn writeVecSectionHeader(buffer: []u8, offset: u32, section: std.wasm.Section, size: u32, items: u32) !void {4270pub fn getExistingString(wasm: *const Wasm, bytes: []const u8) ?String {
3831 var buf: [1 + 5 + 5]u8 = undefined;4271 assert(mem.indexOfScalar(u8, bytes, 0) == null);
3832 buf[0] = @intFromEnum(section);4272 return wasm.string_table.getKeyAdapted(bytes, @as(String.TableIndexAdapter, .{
3833 leb.writeUnsignedFixed(5, buf[1..6], size);4273 .bytes = wasm.string_bytes.items,
3834 leb.writeUnsignedFixed(5, buf[6..], items);4274 }));
3835 buffer[offset..][0..buf.len].* = buf;
3836}4275}
38374276
3838fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void {4277pub fn internValtypeList(wasm: *Wasm, valtype_list: []const std.wasm.Valtype) Allocator.Error!ValtypeList {
3839 var buf: [1 + 5]u8 = undefined;4278 return .fromString(try internString(wasm, @ptrCast(valtype_list)));
3840 buf[0] = 0; // 0 = 'custom' section
3841 leb.writeUnsignedFixed(5, buf[1..6], size);
3842 buffer[offset..][0..buf.len].* = buf;
3843}4279}
38444280
3845fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {4281pub fn getExistingValtypeList(wasm: *const Wasm, valtype_list: []const std.wasm.Valtype) ?ValtypeList {
3846 const offset = try reserveCustomSectionHeader(binary_bytes);4282 return .fromString(getExistingString(wasm, @ptrCast(valtype_list)) orelse return null);
3847 const writer = binary_bytes.writer();
3848 // emit "linking" custom section name
3849 const section_name = "linking";
3850 try leb.writeUleb128(writer, section_name.len);
3851 try writer.writeAll(section_name);
3852
3853 // meta data version, which is currently '2'
3854 try leb.writeUleb128(writer, @as(u32, 2));
3855
3856 // For each subsection type (found in Subsection) we can emit a section.
3857 // Currently, we only support emitting segment info and the symbol table.
3858 try wasm.emitSymbolTable(binary_bytes, symbol_table);
3859 try wasm.emitSegmentInfo(binary_bytes);
3860
3861 const size: u32 = @intCast(binary_bytes.items.len - offset - 6);
3862 try writeCustomSectionHeader(binary_bytes.items, offset, size);
3863}4283}
38644284
3865fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {4285pub fn addFuncType(wasm: *Wasm, ft: FunctionType) Allocator.Error!FunctionType.Index {
3866 const writer = binary_bytes.writer();4286 const gpa = wasm.base.comp.gpa;
38674287 const gop = try wasm.func_types.getOrPut(gpa, ft);
3868 try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SYMBOL_TABLE));4288 return @enumFromInt(gop.index);
3869 const table_offset = binary_bytes.items.len;
3870
3871 var symbol_count: u32 = 0;
3872 for (wasm.resolved_symbols.keys()) |sym_loc| {
3873 const symbol = wasm.symbolLocSymbol(sym_loc).*;
3874 if (symbol.tag == .dead) continue; // Do not emit dead symbols
3875 try symbol_table.putNoClobber(sym_loc, symbol_count);
3876 symbol_count += 1;
3877 log.debug("Emit symbol: {}", .{symbol});
3878 try leb.writeUleb128(writer, @intFromEnum(symbol.tag));
3879 try leb.writeUleb128(writer, symbol.flags);
3880
3881 const sym_name = wasm.symbolLocName(sym_loc);
3882 switch (symbol.tag) {
3883 .data => {
3884 try leb.writeUleb128(writer, @as(u32, @intCast(sym_name.len)));
3885 try writer.writeAll(sym_name);
3886
3887 if (symbol.isDefined()) {
3888 try leb.writeUleb128(writer, symbol.index);
3889 const atom_index = wasm.symbol_atom.get(sym_loc).?;
3890 const atom = wasm.getAtom(atom_index);
3891 try leb.writeUleb128(writer, @as(u32, atom.offset));
3892 try leb.writeUleb128(writer, @as(u32, atom.size));
3893 }
3894 },
3895 .section => {
3896 try leb.writeUleb128(writer, symbol.index);
3897 },
3898 else => {
3899 try leb.writeUleb128(writer, symbol.index);
3900 if (symbol.isDefined()) {
3901 try leb.writeUleb128(writer, @as(u32, @intCast(sym_name.len)));
3902 try writer.writeAll(sym_name);
3903 }
3904 },
3905 }
3906 }
3907
3908 var buf: [10]u8 = undefined;
3909 leb.writeUnsignedFixed(5, buf[0..5], @intCast(binary_bytes.items.len - table_offset + 5));
3910 leb.writeUnsignedFixed(5, buf[5..], symbol_count);
3911 try binary_bytes.insertSlice(table_offset, &buf);
3912}4289}
39134290
3914fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {4291pub fn getExistingFuncType(wasm: *const Wasm, ft: FunctionType) ?FunctionType.Index {
3915 const writer = binary_bytes.writer();4292 const index = wasm.func_types.getIndex(ft) orelse return null;
3916 try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SEGMENT_INFO));4293 return @enumFromInt(index);
3917 const segment_offset = binary_bytes.items.len;
3918
3919 try leb.writeUleb128(writer, @as(u32, @intCast(wasm.segment_info.count())));
3920 for (wasm.segment_info.values()) |segment_info| {
3921 log.debug("Emit segment: {s} align({d}) flags({b})", .{
3922 segment_info.name,
3923 segment_info.alignment,
3924 segment_info.flags,
3925 });
3926 try leb.writeUleb128(writer, @as(u32, @intCast(segment_info.name.len)));
3927 try writer.writeAll(segment_info.name);
3928 try leb.writeUleb128(writer, segment_info.alignment.toLog2Units());
3929 try leb.writeUleb128(writer, segment_info.flags);
3930 }
3931
3932 var buf: [5]u8 = undefined;
3933 leb.writeUnsignedFixed(5, &buf, @as(u32, @intCast(binary_bytes.items.len - segment_offset)));
3934 try binary_bytes.insertSlice(segment_offset, &buf);
3935}4294}
39364295
3937pub fn getUleb128Size(uint_value: anytype) u32 {4296pub fn getExistingFuncType2(wasm: *const Wasm, params: []const std.wasm.Valtype, returns: []const std.wasm.Valtype) FunctionType.Index {
3938 const T = @TypeOf(uint_value);4297 return getExistingFuncType(wasm, .{
3939 const U = if (@typeInfo(T).int.bits < 8) u8 else T;4298 .params = getExistingValtypeList(wasm, params).?,
3940 var value = @as(U, @intCast(uint_value));4299 .returns = getExistingValtypeList(wasm, returns).?,
39414300 }).?;
3942 var size: u32 = 0;
3943 while (value != 0) : (size += 1) {
3944 value >>= 7;
3945 }
3946 return size;
3947}4301}
39484302
3949/// For each relocatable section, emits a custom "relocation.<section_name>" section4303pub fn internFunctionType(
3950fn emitCodeRelocations(
3951 wasm: *Wasm,4304 wasm: *Wasm,
3952 binary_bytes: *std.ArrayList(u8),4305 cc: std.builtin.CallingConvention,
3953 section_index: u32,4306 params: []const InternPool.Index,
3954 symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),4307 return_type: Zcu.Type,
3955) !void {4308 target: *const std.Target,
3956 const code_index = wasm.code_section_index.unwrap() orelse return;4309) Allocator.Error!FunctionType.Index {
3957 const writer = binary_bytes.writer();4310 try convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch);
3958 const header_offset = try reserveCustomSectionHeader(binary_bytes);4311 return wasm.addFuncType(.{
39594312 .params = try wasm.internValtypeList(wasm.params_scratch.items),
3960 // write custom section information4313 .returns = try wasm.internValtypeList(wasm.returns_scratch.items),
3961 const name = "reloc.CODE";4314 });
3962 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
3963 try writer.writeAll(name);
3964 try leb.writeUleb128(writer, section_index);
3965 const reloc_start = binary_bytes.items.len;
3966
3967 var count: u32 = 0;
3968 var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(code_index).?);
3969 // for each atom, we calculate the uleb size and append that
3970 var size_offset: u32 = 5; // account for code section size leb128
3971 while (true) {
3972 size_offset += getUleb128Size(atom.size);
3973 for (atom.relocs.items) |relocation| {
3974 count += 1;
3975 const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) };
3976 const symbol_index = symbol_table.get(sym_loc).?;
3977 try leb.writeUleb128(writer, @intFromEnum(relocation.relocation_type));
3978 const offset = atom.offset + relocation.offset + size_offset;
3979 try leb.writeUleb128(writer, offset);
3980 try leb.writeUleb128(writer, symbol_index);
3981 if (relocation.relocation_type.addendIsPresent()) {
3982 try leb.writeIleb128(writer, relocation.addend);
3983 }
3984 log.debug("Emit relocation: {}", .{relocation});
3985 }
3986 if (atom.prev == .null) break;
3987 atom = wasm.getAtomPtr(atom.prev);
3988 }
3989 if (count == 0) return;
3990 var buf: [5]u8 = undefined;
3991 leb.writeUnsignedFixed(5, &buf, count);
3992 try binary_bytes.insertSlice(reloc_start, &buf);
3993 const size: u32 = @intCast(binary_bytes.items.len - header_offset - 6);
3994 try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
3995}4315}
39964316
3997fn emitDataRelocations(4317pub fn getExistingFunctionType(
3998 wasm: *Wasm,4318 wasm: *Wasm,
3999 binary_bytes: *std.ArrayList(u8),4319 cc: std.builtin.CallingConvention,
4000 section_index: u32,4320 params: []const InternPool.Index,
4001 symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),4321 return_type: Zcu.Type,
4002) !void {4322 target: *const std.Target,
4003 if (wasm.data_segments.count() == 0) return;4323) ?FunctionType.Index {
4004 const writer = binary_bytes.writer();4324 convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch) catch |err| switch (err) {
4005 const header_offset = try reserveCustomSectionHeader(binary_bytes);4325 error.OutOfMemory => return null,
40064326 };
4007 // write custom section information4327 return wasm.getExistingFuncType(.{
4008 const name = "reloc.DATA";4328 .params = wasm.getExistingValtypeList(wasm.params_scratch.items) orelse return null,
4009 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));4329 .returns = wasm.getExistingValtypeList(wasm.returns_scratch.items) orelse return null,
4010 try writer.writeAll(name);4330 });
4011 try leb.writeUleb128(writer, section_index);
4012 const reloc_start = binary_bytes.items.len;
4013
4014 var count: u32 = 0;
4015 // for each atom, we calculate the uleb size and append that
4016 var size_offset: u32 = 5; // account for code section size leb128
4017 for (wasm.data_segments.values()) |segment_index| {
4018 var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(segment_index).?);
4019 while (true) {
4020 size_offset += getUleb128Size(atom.size);
4021 for (atom.relocs.items) |relocation| {
4022 count += 1;
4023 const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) };
4024 const symbol_index = symbol_table.get(sym_loc).?;
4025 try leb.writeUleb128(writer, @intFromEnum(relocation.relocation_type));
4026 const offset = atom.offset + relocation.offset + size_offset;
4027 try leb.writeUleb128(writer, offset);
4028 try leb.writeUleb128(writer, symbol_index);
4029 if (relocation.relocation_type.addendIsPresent()) {
4030 try leb.writeIleb128(writer, relocation.addend);
4031 }
4032 log.debug("Emit relocation: {}", .{relocation});
4033 }
4034 if (atom.prev == .null) break;
4035 atom = wasm.getAtomPtr(atom.prev);
4036 }
4037 }
4038 if (count == 0) return;
4039
4040 var buf: [5]u8 = undefined;
4041 leb.writeUnsignedFixed(5, &buf, count);
4042 try binary_bytes.insertSlice(reloc_start, &buf);
4043 const size = @as(u32, @intCast(binary_bytes.items.len - header_offset - 6));
4044 try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
4045}4331}
40464332
4047fn hasPassiveInitializationSegments(wasm: *const Wasm) bool {4333pub fn addExpr(wasm: *Wasm, bytes: []const u8) Allocator.Error!Expr {
4048 const comp = wasm.base.comp;4334 const gpa = wasm.base.comp.gpa;
4049 const import_memory = comp.config.import_memory;4335 // We can't use string table deduplication here since these expressions can
40504336 // have null bytes in them however it may be interesting to explore since
4051 var it = wasm.data_segments.iterator();4337 // it is likely for globals to share initialization values. Then again
4052 while (it.next()) |entry| {4338 // there may not be very many globals in total.
4053 const segment = wasm.segmentPtr(entry.value_ptr.*);4339 try wasm.string_bytes.appendSlice(gpa, bytes);
4054 if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) {4340 return @enumFromInt(wasm.string_bytes.items.len - bytes.len);
4055 return true;
4056 }
4057 }
4058 return false;
4059}4341}
40604342
4061/// Searches for a matching function signature. When no matching signature is found,4343pub fn addRelocatableDataPayload(wasm: *Wasm, bytes: []const u8) Allocator.Error!DataPayload {
4062/// a new entry will be made. The value returned is the index of the type within `wasm.func_types`.
4063pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
4064 if (wasm.getTypeIndex(func_type)) |index| {
4065 return index;
4066 }
4067
4068 // functype does not exist.
4069 const gpa = wasm.base.comp.gpa;4344 const gpa = wasm.base.comp.gpa;
4070 const index: u32 = @intCast(wasm.func_types.items.len);4345 try wasm.string_bytes.appendSlice(gpa, bytes);
4071 const params = try gpa.dupe(std.wasm.Valtype, func_type.params);4346 return .{
4072 errdefer gpa.free(params);4347 .off = @enumFromInt(wasm.string_bytes.items.len - bytes.len),
4073 const returns = try gpa.dupe(std.wasm.Valtype, func_type.returns);4348 .len = @intCast(bytes.len),
4074 errdefer gpa.free(returns);4349 };
4075 try wasm.func_types.append(gpa, .{
4076 .params = params,
4077 .returns = returns,
4078 });
4079 return index;
4080}4350}
40814351
4082/// For the given `nav`, stores the corresponding type representing the function signature.4352pub fn uavSymbolIndex(wasm: *Wasm, ip_index: InternPool.Index) Allocator.Error!SymbolTableIndex {
4083/// Asserts declaration has an associated `Atom`.4353 const comp = wasm.base.comp;
4084/// Returns the index into the list of types.4354 assert(comp.config.output_mode == .Obj);
4085pub fn storeNavType(wasm: *Wasm, nav: InternPool.Nav.Index, func_type: std.wasm.Type) !u32 {4355 const gpa = comp.gpa;
4086 return wasm.zig_object.?.storeDeclType(wasm.base.comp.gpa, nav, func_type);4356 const name = try wasm.internStringFmt("__anon_{d}", .{@intFromEnum(ip_index)});
4357 const gop = try wasm.symbol_table.getOrPut(gpa, name);
4358 gop.value_ptr.* = {};
4359 return @enumFromInt(gop.index);
4087}4360}
40884361
4089/// Returns the symbol index of the error name table.4362pub fn navSymbolIndex(wasm: *Wasm, nav_index: InternPool.Nav.Index) Allocator.Error!SymbolTableIndex {
4090///4363 const comp = wasm.base.comp;
4091/// When the symbol does not yet exist, it will create a new one instead.4364 assert(comp.config.output_mode == .Obj);
4092pub fn getErrorTableSymbol(wasm: *Wasm, pt: Zcu.PerThread) !u32 {4365 const zcu = comp.zcu.?;
4093 const sym_index = try wasm.zig_object.?.getErrorTableSymbol(wasm, pt);4366 const ip = &zcu.intern_pool;
4094 return @intFromEnum(sym_index);4367 const gpa = comp.gpa;
4368 const nav = ip.getNav(nav_index);
4369 const name = try wasm.internString(nav.fqn.toSlice(ip));
4370 const gop = try wasm.symbol_table.getOrPut(gpa, name);
4371 gop.value_ptr.* = {};
4372 return @enumFromInt(gop.index);
4095}4373}
40964374
4097/// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`.4375pub fn errorNameTableSymbolIndex(wasm: *Wasm) Allocator.Error!SymbolTableIndex {
4098/// When the index was not found, a new `Atom` will be created, and its index will be returned.4376 const comp = wasm.base.comp;
4099/// The newly created Atom is empty with default fields as specified by `Atom.empty`.4377 assert(comp.config.output_mode == .Obj);
4100pub fn getOrCreateAtomForNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !Atom.Index {4378 const gpa = comp.gpa;
4101 return wasm.zig_object.?.getOrCreateAtomForNav(wasm, pt, nav);4379 const gop = try wasm.symbol_table.getOrPut(gpa, wasm.preloaded_strings.__zig_error_name_table);
4380 gop.value_ptr.* = {};
4381 return @enumFromInt(gop.index);
4102}4382}
41034383
4104/// Verifies all resolved symbols and checks whether itself needs to be marked alive,4384pub fn stackPointerSymbolIndex(wasm: *Wasm) Allocator.Error!SymbolTableIndex {
4105/// as well as any of its references.
4106fn markReferences(wasm: *Wasm) !void {
4107 const tracy = trace(@src());
4108 defer tracy.end();
4109
4110 const do_garbage_collect = wasm.base.gc_sections;
4111 const comp = wasm.base.comp;4385 const comp = wasm.base.comp;
41124386 assert(comp.config.output_mode == .Obj);
4113 for (wasm.resolved_symbols.keys()) |sym_loc| {4387 const gpa = comp.gpa;
4114 const sym = wasm.symbolLocSymbol(sym_loc);4388 const gop = try wasm.symbol_table.getOrPut(gpa, wasm.preloaded_strings.__stack_pointer);
4115 if (sym.isExported(comp.config.rdynamic) or sym.isNoStrip() or !do_garbage_collect) {4389 gop.value_ptr.* = {};
4116 try wasm.mark(sym_loc);4390 return @enumFromInt(gop.index);
4117 continue;
4118 }
4119
4120 // Debug sections may require to be parsed and marked when it contains
4121 // relocations to alive symbols.
4122 if (sym.tag == .section and comp.config.debug_format != .strip) {
4123 const object_id = sym_loc.file.unwrap() orelse continue; // Incremental debug info is done independently
4124 _ = try wasm.parseSymbolIntoAtom(object_id, sym_loc.index);
4125 sym.mark();
4126 }
4127 }
4128}4391}
41294392
4130/// Marks a symbol as 'alive' recursively so itself and any references it contains to4393pub fn tagNameSymbolIndex(wasm: *Wasm, ip_index: InternPool.Index) Allocator.Error!SymbolTableIndex {
4131/// other symbols will not be omit from the binary.4394 const comp = wasm.base.comp;
4132fn mark(wasm: *Wasm, loc: SymbolLoc) !void {4395 assert(comp.config.output_mode == .Obj);
4133 const symbol = wasm.symbolLocSymbol(loc);4396 const gpa = comp.gpa;
4134 if (symbol.isAlive()) {4397 const name = try wasm.internStringFmt("__zig_tag_name_{d}", .{@intFromEnum(ip_index)});
4135 // Symbol is already marked alive, including its references.4398 const gop = try wasm.symbol_table.getOrPut(gpa, name);
4136 // This means we can skip it so we don't end up marking the same symbols4399 gop.value_ptr.* = {};
4137 // multiple times.4400 return @enumFromInt(gop.index);
4138 return;
4139 }
4140 symbol.mark();
4141 gc_log.debug("Marked symbol '{s}'", .{wasm.symbolLocName(loc)});
4142 if (symbol.isUndefined()) {
4143 // undefined symbols do not have an associated `Atom` and therefore also
4144 // do not contain relocations.
4145 return;
4146 }
4147
4148 const atom_index = if (loc.file.unwrap()) |object_id|
4149 try wasm.parseSymbolIntoAtom(object_id, loc.index)
4150 else
4151 wasm.symbol_atom.get(loc) orelse return;
4152
4153 const atom = wasm.getAtom(atom_index);
4154 for (atom.relocs.items) |reloc| {
4155 const target_loc: SymbolLoc = .{ .index = @enumFromInt(reloc.index), .file = loc.file };
4156 try wasm.mark(wasm.symbolLocFinalLoc(target_loc));
4157 }
4158}4401}
41594402
4160fn defaultEntrySymbolName(4403pub fn symbolNameIndex(wasm: *Wasm, name: String) Allocator.Error!SymbolTableIndex {
4161 preloaded_strings: *const PreloadedStrings,4404 const comp = wasm.base.comp;
4162 wasi_exec_model: std.builtin.WasiExecModel,4405 assert(comp.config.output_mode == .Obj);
4163) String {4406 const gpa = comp.gpa;
4164 return switch (wasi_exec_model) {4407 const gop = try wasm.symbol_table.getOrPut(gpa, name);
4165 .reactor => preloaded_strings._initialize,4408 gop.value_ptr.* = {};
4166 .command => preloaded_strings._start,4409 return @enumFromInt(gop.index);
4167 };
4168}4410}
41694411
4170pub const Atom = struct {4412pub fn refUavObj(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsObjIndex {
4171 /// Represents the index of the file this atom was generated from.4413 const comp = wasm.base.comp;
4172 /// This is `none` when the atom was generated by a synthetic linker symbol.4414 const zcu = comp.zcu.?;
4173 file: OptionalObjectId,4415 const ip = &zcu.intern_pool;
4174 /// symbol index of the symbol representing this atom4416 const gpa = comp.gpa;
4175 sym_index: Symbol.Index,4417 assert(comp.config.output_mode == .Obj);
4176 /// Size of the atom, used to calculate section sizes in the final binary
4177 size: u32 = 0,
4178 /// List of relocations belonging to this atom
4179 relocs: std.ArrayListUnmanaged(Relocation) = .empty,
4180 /// Contains the binary data of an atom, which can be non-relocated
4181 code: std.ArrayListUnmanaged(u8) = .empty,
4182 /// For code this is 1, for data this is set to the highest value of all segments
4183 alignment: Wasm.Alignment = .@"1",
4184 /// Offset into the section where the atom lives, this already accounts
4185 /// for alignment.
4186 offset: u32 = 0,
4187 /// The original offset within the object file. This value is subtracted from
4188 /// relocation offsets to determine where in the `data` to rewrite the value
4189 original_offset: u32 = 0,
4190 /// Previous atom in relation to this atom.
4191 /// is null when this atom is the first in its order
4192 prev: Atom.Index = .null,
4193 /// Contains atoms local to a decl, all managed by this `Atom`.
4194 /// When the parent atom is being freed, it will also do so for all local atoms.
4195 locals: std.ArrayListUnmanaged(Atom.Index) = .empty,
4196
4197 /// Represents the index of an Atom where `null` is considered
4198 /// an invalid atom.
4199 pub const Index = enum(u32) {
4200 null = std.math.maxInt(u32),
4201 _,
4202 };
4203
4204 /// Frees all resources owned by this `Atom`.
4205 pub fn deinit(atom: *Atom, gpa: std.mem.Allocator) void {
4206 atom.relocs.deinit(gpa);
4207 atom.code.deinit(gpa);
4208 atom.locals.deinit(gpa);
4209 atom.* = undefined;
4210 }
4211
4212 /// Sets the length of relocations and code to '0',
4213 /// effectively resetting them and allowing them to be re-populated.
4214 pub fn clear(atom: *Atom) void {
4215 atom.relocs.clearRetainingCapacity();
4216 atom.code.clearRetainingCapacity();
4217 }
4218
4219 pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
4220 _ = fmt;
4221 _ = options;
4222 try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{
4223 @intFromEnum(atom.sym_index),
4224 atom.alignment,
4225 atom.size,
4226 atom.offset,
4227 });
4228 }
42294418
4230 /// Returns the location of the symbol that represents this `Atom`4419 if (orig_ptr_ty != .none) {
4231 pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {4420 const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu);
4232 return .{4421 const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment;
4233 .file = atom.file,4422 if (explicit_alignment.compare(.gt, abi_alignment)) {
4234 .index = atom.sym_index,4423 const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index);
4235 };4424 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment;
4425 }
4236 }4426 }
42374427
4238 /// Resolves the relocations within the atom, writing the new value4428 const gop = try wasm.uavs_obj.getOrPut(gpa, ip_index);
4239 /// at the calculated offset.4429 if (!gop.found_existing) gop.value_ptr.* = .{
4240 pub fn resolveRelocs(atom: *Atom, wasm: *const Wasm) void {4430 // Lowering the value is delayed to avoid recursion.
4241 if (atom.relocs.items.len == 0) return;4431 .code = undefined,
4242 const symbol_name = wasm.symbolLocName(atom.symbolLoc());4432 .relocs = undefined,
4243 log.debug("Resolving relocs in atom '{s}' count({d})", .{4433 };
4244 symbol_name,4434 return @enumFromInt(gop.index);
4245 atom.relocs.items.len,4435}
4246 });
4247
4248 for (atom.relocs.items) |reloc| {
4249 const value = atom.relocationValue(reloc, wasm);
4250 log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
4251 wasm.symbolLocName(.{
4252 .file = atom.file,
4253 .index = @enumFromInt(reloc.index),
4254 }),
4255 symbol_name,
4256 reloc.offset,
4257 value,
4258 });
42594436
4260 switch (reloc.relocation_type) {4437pub fn refUavExe(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsExeIndex {
4261 .R_WASM_TABLE_INDEX_I32,4438 const comp = wasm.base.comp;
4262 .R_WASM_FUNCTION_OFFSET_I32,4439 const zcu = comp.zcu.?;
4263 .R_WASM_GLOBAL_INDEX_I32,4440 const ip = &zcu.intern_pool;
4264 .R_WASM_MEMORY_ADDR_I32,4441 const gpa = comp.gpa;
4265 .R_WASM_SECTION_OFFSET_I32,4442 assert(comp.config.output_mode != .Obj);
4266 => std.mem.writeInt(u32, atom.code.items[reloc.offset - atom.original_offset ..][0..4], @as(u32, @truncate(value)), .little),
4267 .R_WASM_TABLE_INDEX_I64,
4268 .R_WASM_MEMORY_ADDR_I64,
4269 => std.mem.writeInt(u64, atom.code.items[reloc.offset - atom.original_offset ..][0..8], value, .little),
4270 .R_WASM_GLOBAL_INDEX_LEB,
4271 .R_WASM_EVENT_INDEX_LEB,
4272 .R_WASM_FUNCTION_INDEX_LEB,
4273 .R_WASM_MEMORY_ADDR_LEB,
4274 .R_WASM_MEMORY_ADDR_SLEB,
4275 .R_WASM_TABLE_INDEX_SLEB,
4276 .R_WASM_TABLE_NUMBER_LEB,
4277 .R_WASM_TYPE_INDEX_LEB,
4278 .R_WASM_MEMORY_ADDR_TLS_SLEB,
4279 => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset - atom.original_offset ..][0..5], @as(u32, @truncate(value))),
4280 .R_WASM_MEMORY_ADDR_LEB64,
4281 .R_WASM_MEMORY_ADDR_SLEB64,
4282 .R_WASM_TABLE_INDEX_SLEB64,
4283 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
4284 => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset - atom.original_offset ..][0..10], value),
4285 }
4286 }
4287 }
42884443
4289 /// From a given `relocation` will return the new value to be written.4444 if (orig_ptr_ty != .none) {
4290 /// All values will be represented as a `u64` as all values can fit within it.4445 const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu);
4291 /// The final value must be casted to the correct size.4446 const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment;
4292 fn relocationValue(atom: Atom, relocation: Relocation, wasm: *const Wasm) u64 {4447 if (explicit_alignment.compare(.gt, abi_alignment)) {
4293 const target_loc = wasm.symbolLocFinalLoc(.{4448 const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index);
4294 .file = atom.file,4449 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment;
4295 .index = @enumFromInt(relocation.index),
4296 });
4297 const symbol = wasm.symbolLocSymbol(target_loc);
4298 if (relocation.relocation_type != .R_WASM_TYPE_INDEX_LEB and
4299 symbol.tag != .section and
4300 symbol.isDead())
4301 {
4302 const val = atom.tombstone(wasm) orelse relocation.addend;
4303 return @bitCast(val);
4304 }
4305 switch (relocation.relocation_type) {
4306 .R_WASM_FUNCTION_INDEX_LEB => return symbol.index,
4307 .R_WASM_TABLE_NUMBER_LEB => return symbol.index,
4308 .R_WASM_TABLE_INDEX_I32,
4309 .R_WASM_TABLE_INDEX_I64,
4310 .R_WASM_TABLE_INDEX_SLEB,
4311 .R_WASM_TABLE_INDEX_SLEB64,
4312 => return wasm.function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0,
4313 .R_WASM_TYPE_INDEX_LEB => {
4314 const object_id = atom.file.unwrap() orelse return relocation.index;
4315 const original_type = objectFuncTypes(wasm, object_id)[relocation.index];
4316 return wasm.getTypeIndex(original_type).?;
4317 },
4318 .R_WASM_GLOBAL_INDEX_I32,
4319 .R_WASM_GLOBAL_INDEX_LEB,
4320 => return symbol.index,
4321 .R_WASM_MEMORY_ADDR_I32,
4322 .R_WASM_MEMORY_ADDR_I64,
4323 .R_WASM_MEMORY_ADDR_LEB,
4324 .R_WASM_MEMORY_ADDR_LEB64,
4325 .R_WASM_MEMORY_ADDR_SLEB,
4326 .R_WASM_MEMORY_ADDR_SLEB64,
4327 => {
4328 std.debug.assert(symbol.tag == .data);
4329 if (symbol.isUndefined()) {
4330 return 0;
4331 }
4332 const va: i33 = @intCast(symbol.virtual_address);
4333 return @intCast(va + relocation.addend);
4334 },
4335 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
4336 .R_WASM_SECTION_OFFSET_I32 => {
4337 const target_atom_index = wasm.symbol_atom.get(target_loc).?;
4338 const target_atom = wasm.getAtom(target_atom_index);
4339 const rel_value: i33 = @intCast(target_atom.offset);
4340 return @intCast(rel_value + relocation.addend);
4341 },
4342 .R_WASM_FUNCTION_OFFSET_I32 => {
4343 if (symbol.isUndefined()) {
4344 const val = atom.tombstone(wasm) orelse relocation.addend;
4345 return @bitCast(val);
4346 }
4347 const target_atom_index = wasm.symbol_atom.get(target_loc).?;
4348 const target_atom = wasm.getAtom(target_atom_index);
4349 const rel_value: i33 = @intCast(target_atom.offset);
4350 return @intCast(rel_value + relocation.addend);
4351 },
4352 .R_WASM_MEMORY_ADDR_TLS_SLEB,
4353 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
4354 => {
4355 const va: i33 = @intCast(symbol.virtual_address);
4356 return @intCast(va + relocation.addend);
4357 },
4358 }4450 }
4359 }4451 }
43604452
4361 // For a given `Atom` returns whether it has a tombstone value or not.4453 const gop = try wasm.uavs_exe.getOrPut(gpa, ip_index);
4362 /// This defines whether we want a specific value when a section is dead.4454 if (gop.found_existing) {
4363 fn tombstone(atom: Atom, wasm: *const Wasm) ?i64 {4455 gop.value_ptr.count += 1;
4364 const atom_name = wasm.symbolLocSymbol(atom.symbolLoc()).name;4456 } else {
4365 if (atom_name == wasm.custom_sections.@".debug_ranges".name or4457 gop.value_ptr.* = .{
4366 atom_name == wasm.custom_sections.@".debug_loc".name)4458 // Lowering the value is delayed to avoid recursion.
4367 {4459 .code = undefined,
4368 return -2;4460 .count = 1,
4369 } else if (std.mem.startsWith(u8, wasm.stringSlice(atom_name), ".debug_")) {4461 };
4370 return -1;
4371 } else {
4372 return null;
4373 }
4374 }4462 }
4375};4463 return @enumFromInt(gop.index);
4464}
43764465
4377pub const Relocation = struct {4466pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {
4378 /// Represents the type of the `Relocation`4467 const comp = wasm.base.comp;
4379 relocation_type: RelocationType,4468 const gpa = comp.gpa;
4380 /// Offset of the value to rewrite relative to the relevant section's contents.4469 assert(comp.config.output_mode != .Obj);
4381 /// When `offset` is zero, its position is immediately after the id and size of the section.4470 const gop = try wasm.navs_obj.getOrPut(gpa, nav_index);
4382 offset: u32,4471 if (!gop.found_existing) gop.value_ptr.* = .{
4383 /// The index of the symbol used.4472 // Lowering the value is delayed to avoid recursion.
4384 /// When the type is `R_WASM_TYPE_INDEX_LEB`, it represents the index of the type.4473 .code = undefined,
4385 index: u32,4474 .relocs = undefined,
4386 /// Addend to add to the address.
4387 /// This field is only non-zero for `R_WASM_MEMORY_ADDR_*`, `R_WASM_FUNCTION_OFFSET_I32` and `R_WASM_SECTION_OFFSET_I32`.
4388 addend: i32 = 0,
4389
4390 /// All possible relocation types currently existing.
4391 /// This enum is exhaustive as the spec is WIP and new types
4392 /// can be added which means that a generated binary will be invalid,
4393 /// so instead we will show an error in such cases.
4394 pub const RelocationType = enum(u8) {
4395 R_WASM_FUNCTION_INDEX_LEB = 0,
4396 R_WASM_TABLE_INDEX_SLEB = 1,
4397 R_WASM_TABLE_INDEX_I32 = 2,
4398 R_WASM_MEMORY_ADDR_LEB = 3,
4399 R_WASM_MEMORY_ADDR_SLEB = 4,
4400 R_WASM_MEMORY_ADDR_I32 = 5,
4401 R_WASM_TYPE_INDEX_LEB = 6,
4402 R_WASM_GLOBAL_INDEX_LEB = 7,
4403 R_WASM_FUNCTION_OFFSET_I32 = 8,
4404 R_WASM_SECTION_OFFSET_I32 = 9,
4405 R_WASM_EVENT_INDEX_LEB = 10,
4406 R_WASM_GLOBAL_INDEX_I32 = 13,
4407 R_WASM_MEMORY_ADDR_LEB64 = 14,
4408 R_WASM_MEMORY_ADDR_SLEB64 = 15,
4409 R_WASM_MEMORY_ADDR_I64 = 16,
4410 R_WASM_TABLE_INDEX_SLEB64 = 18,
4411 R_WASM_TABLE_INDEX_I64 = 19,
4412 R_WASM_TABLE_NUMBER_LEB = 20,
4413 R_WASM_MEMORY_ADDR_TLS_SLEB = 21,
4414 R_WASM_MEMORY_ADDR_TLS_SLEB64 = 25,
4415
4416 /// Returns true for relocation types where the `addend` field is present.
4417 pub fn addendIsPresent(self: RelocationType) bool {
4418 return switch (self) {
4419 .R_WASM_MEMORY_ADDR_LEB,
4420 .R_WASM_MEMORY_ADDR_SLEB,
4421 .R_WASM_MEMORY_ADDR_I32,
4422 .R_WASM_MEMORY_ADDR_LEB64,
4423 .R_WASM_MEMORY_ADDR_SLEB64,
4424 .R_WASM_MEMORY_ADDR_I64,
4425 .R_WASM_MEMORY_ADDR_TLS_SLEB,
4426 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
4427 .R_WASM_FUNCTION_OFFSET_I32,
4428 .R_WASM_SECTION_OFFSET_I32,
4429 => true,
4430 else => false,
4431 };
4432 }
4433 };4475 };
4476 return @enumFromInt(gop.index);
4477}
44344478
4435 /// Verifies the relocation type of a given `Relocation` and returns4479pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {
4436 /// true when the relocation references a function call or address to a function.4480 const comp = wasm.base.comp;
4437 pub fn isFunction(self: Relocation) bool {4481 const gpa = comp.gpa;
4438 return switch (self.relocation_type) {4482 assert(comp.config.output_mode != .Obj);
4439 .R_WASM_FUNCTION_INDEX_LEB,4483 const gop = try wasm.navs_exe.getOrPut(gpa, nav_index);
4440 .R_WASM_TABLE_INDEX_SLEB,4484 if (gop.found_existing) {
4441 => true,4485 gop.value_ptr.count += 1;
4442 else => false,4486 } else {
4487 gop.value_ptr.* = .{
4488 // Lowering the value is delayed to avoid recursion.
4489 .code = undefined,
4490 .count = 0,
4443 };4491 };
4444 }4492 }
4493 return @enumFromInt(gop.index);
4494}
44454495
4446 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {4496/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.
4447 _ = fmt;4497pub fn uavAddr(wasm: *Wasm, uav_index: UavsExeIndex) u32 {
4448 _ = options;4498 assert(wasm.flush_buffer.memory_layout_finished);
4449 try writer.print("{s} offset=0x{x:0>6} symbol={d}", .{4499 const comp = wasm.base.comp;
4450 @tagName(self.relocation_type),4500 assert(comp.config.output_mode != .Obj);
4451 self.offset,4501 const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index });
4452 self.index,4502 return wasm.flush_buffer.data_segments.get(ds_id).?;
4453 });4503}
4454 }
4455};
4456
4457/// Unlike the `Import` object defined by the wasm spec, and existing
4458/// in the std.wasm namespace, this construct saves the 'module name' and 'name'
4459/// of the import using offsets into a string table, rather than the slices itself.
4460/// This saves us (potentially) 24 bytes per import on 64bit machines.
4461pub const Import = struct {
4462 module_name: String,
4463 name: String,
4464 kind: std.wasm.Import.Kind,
4465};
4466
4467/// Unlike the `Export` object defined by the wasm spec, and existing
4468/// in the std.wasm namespace, this construct saves the 'name'
4469/// of the export using offsets into a string table, rather than the slice itself.
4470/// This saves us (potentially) 12 bytes per export on 64bit machines.
4471pub const Export = struct {
4472 name: String,
4473 index: u32,
4474 kind: std.wasm.ExternalKind,
4475};
4476
4477pub const SubsectionType = enum(u8) {
4478 WASM_SEGMENT_INFO = 5,
4479 WASM_INIT_FUNCS = 6,
4480 WASM_COMDAT_INFO = 7,
4481 WASM_SYMBOL_TABLE = 8,
4482};
4483
4484pub const Alignment = @import("../InternPool.zig").Alignment;
4485
4486pub const NamedSegment = struct {
4487 /// Segment's name, encoded as UTF-8 bytes.
4488 name: []const u8,
4489 /// The required alignment of the segment, encoded as a power of 2
4490 alignment: Alignment,
4491 /// Bitfield containing flags for a segment
4492 flags: u32,
4493
4494 pub fn isTLS(segment: NamedSegment) bool {
4495 return segment.flags & @intFromEnum(Flags.WASM_SEG_FLAG_TLS) != 0;
4496 }
4497
4498 /// Returns the name as how it will be output into the final object
4499 /// file or binary. When `merge_segments` is true, this will return the
4500 /// short name. i.e. ".rodata". When false, it returns the entire name instead.
4501 pub fn outputName(segment: NamedSegment, merge_segments: bool) []const u8 {
4502 if (segment.isTLS()) {
4503 return ".tdata";
4504 } else if (!merge_segments) {
4505 return segment.name;
4506 } else if (std.mem.startsWith(u8, segment.name, ".rodata.")) {
4507 return ".rodata";
4508 } else if (std.mem.startsWith(u8, segment.name, ".text.")) {
4509 return ".text";
4510 } else if (std.mem.startsWith(u8, segment.name, ".data.")) {
4511 return ".data";
4512 } else if (std.mem.startsWith(u8, segment.name, ".bss.")) {
4513 return ".bss";
4514 }
4515 return segment.name;
4516 }
4517
4518 pub const Flags = enum(u32) {
4519 WASM_SEG_FLAG_STRINGS = 0x1,
4520 WASM_SEG_FLAG_TLS = 0x2,
4521 };
4522};
4523
4524pub const InitFunc = struct {
4525 /// Priority of the init function
4526 priority: u32,
4527 /// The symbol index of init function (not the function index).
4528 symbol_index: u32,
4529};
4530
4531pub const Comdat = struct {
4532 name: []const u8,
4533 /// Must be zero, no flags are currently defined by the tool-convention.
4534 flags: u32,
4535 symbols: []const ComdatSym,
4536};
4537
4538pub const ComdatSym = struct {
4539 kind: @This().Type,
4540 /// Index of the data segment/function/global/event/table within a WASM module.
4541 /// The object must not be an import.
4542 index: u32,
4543
4544 pub const Type = enum(u8) {
4545 WASM_COMDAT_DATA = 0,
4546 WASM_COMDAT_FUNCTION = 1,
4547 WASM_COMDAT_GLOBAL = 2,
4548 WASM_COMDAT_EVENT = 3,
4549 WASM_COMDAT_TABLE = 4,
4550 WASM_COMDAT_SECTION = 5,
4551 };
4552};
4553
4554pub const Feature = struct {
4555 /// Provides information about the usage of the feature.
4556 /// - '0x2b' (+): Object uses this feature, and the link fails if feature is not in the allowed set.
4557 /// - '0x2d' (-): Object does not use this feature, and the link fails if this feature is in the allowed set.
4558 /// - '0x3d' (=): Object uses this feature, and the link fails if this feature is not in the allowed set,
4559 /// or if any object does not use this feature.
4560 prefix: Prefix,
4561 /// Type of the feature, must be unique in the sequence of features.
4562 tag: Tag,
4563
4564 /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem
4565 pub const Tag = enum {
4566 atomics,
4567 bulk_memory,
4568 exception_handling,
4569 extended_const,
4570 half_precision,
4571 multimemory,
4572 multivalue,
4573 mutable_globals,
4574 nontrapping_fptoint,
4575 reference_types,
4576 relaxed_simd,
4577 sign_ext,
4578 simd128,
4579 tail_call,
4580 shared_mem,
4581
4582 /// From a given cpu feature, returns its linker feature
4583 pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag {
4584 return @as(Tag, @enumFromInt(@intFromEnum(feature)));
4585 }
4586
4587 pub fn format(tag: Tag, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
4588 _ = fmt;
4589 _ = opt;
4590 try writer.writeAll(switch (tag) {
4591 .atomics => "atomics",
4592 .bulk_memory => "bulk-memory",
4593 .exception_handling => "exception-handling",
4594 .extended_const => "extended-const",
4595 .half_precision => "half-precision",
4596 .multimemory => "multimemory",
4597 .multivalue => "multivalue",
4598 .mutable_globals => "mutable-globals",
4599 .nontrapping_fptoint => "nontrapping-fptoint",
4600 .reference_types => "reference-types",
4601 .relaxed_simd => "relaxed-simd",
4602 .sign_ext => "sign-ext",
4603 .simd128 => "simd128",
4604 .tail_call => "tail-call",
4605 .shared_mem => "shared-mem",
4606 });
4607 }
4608 };
4609
4610 pub const Prefix = enum(u8) {
4611 used = '+',
4612 disallowed = '-',
4613 required = '=',
4614 };
4615
4616 pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
4617 _ = opt;
4618 _ = fmt;
4619 try writer.print("{c} {}", .{ feature.prefix, feature.tag });
4620 }
4621};
46224504
4623pub const known_features = std.StaticStringMap(Feature.Tag).initComptime(.{4505/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.
4624 .{ "atomics", .atomics },4506pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) u32 {
4625 .{ "bulk-memory", .bulk_memory },4507 assert(wasm.flush_buffer.memory_layout_finished);
4626 .{ "exception-handling", .exception_handling },
4627 .{ "extended-const", .extended_const },
4628 .{ "half-precision", .half_precision },
4629 .{ "multimemory", .multimemory },
4630 .{ "multivalue", .multivalue },
4631 .{ "mutable-globals", .mutable_globals },
4632 .{ "nontrapping-fptoint", .nontrapping_fptoint },
4633 .{ "reference-types", .reference_types },
4634 .{ "relaxed-simd", .relaxed_simd },
4635 .{ "sign-ext", .sign_ext },
4636 .{ "simd128", .simd128 },
4637 .{ "tail-call", .tail_call },
4638 .{ "shared-mem", .shared_mem },
4639});
4640
4641/// Parses an object file into atoms, for code and data sections
4642fn parseSymbolIntoAtom(wasm: *Wasm, object_id: ObjectId, symbol_index: Symbol.Index) !Atom.Index {
4643 const object = wasm.objectById(object_id) orelse
4644 return wasm.zig_object.?.parseSymbolIntoAtom(wasm, symbol_index);
4645 const comp = wasm.base.comp;4508 const comp = wasm.base.comp;
4646 const gpa = comp.gpa;4509 assert(comp.config.output_mode != .Obj);
4647 const symbol = &object.symtable[@intFromEnum(symbol_index)];4510 if (wasm.navs_exe.getIndex(nav_index)) |i| {
4648 const relocatable_data: Object.RelocatableData = switch (symbol.tag) {4511 const navs_exe_index: NavsExeIndex = @enumFromInt(i);
4649 .function => object.relocatable_data.get(.code).?[symbol.index - object.imported_functions_count],4512 log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index });
4650 .data => object.relocatable_data.get(.data).?[symbol.index],4513 const ds_id: DataSegmentId = .pack(wasm, .{ .nav_exe = navs_exe_index });
4651 .section => blk: {4514 return wasm.flush_buffer.data_segments.get(ds_id).?;
4652 const data = object.relocatable_data.get(.custom).?;4515 }
4653 for (data) |dat| {4516 const zcu = comp.zcu.?;
4654 if (dat.section_index == symbol.index) {4517 const ip = &zcu.intern_pool;
4655 break :blk dat;4518 const nav = ip.getNav(nav_index);
4519 if (nav.getResolvedExtern(ip)) |ext| {
4520 if (wasm.getExistingString(ext.name.toSlice(ip))) |symbol_name| {
4521 if (wasm.object_data_imports.getPtr(symbol_name)) |import| {
4522 switch (import.resolution.unpack(wasm)) {
4523 .unresolved => unreachable,
4524 .object => |object_data_index| {
4525 const object_data = object_data_index.ptr(wasm);
4526 const ds_id: DataSegmentId = .fromObjectDataSegment(wasm, object_data.segment);
4527 const segment_base_addr = wasm.flush_buffer.data_segments.get(ds_id).?;
4528 return segment_base_addr + object_data.offset;
4529 },
4530 .__zig_error_names => @panic("TODO"),
4531 .__zig_error_name_table => @panic("TODO"),
4532 .__heap_base => @panic("TODO"),
4533 .__heap_end => @panic("TODO"),
4534 .uav_exe => @panic("TODO"),
4535 .uav_obj => @panic("TODO"),
4536 .nav_exe => @panic("TODO"),
4537 .nav_obj => @panic("TODO"),
4656 }4538 }
4657 }4539 }
4658 unreachable;
4659 },
4660 else => unreachable,
4661 };
4662 const final_index = try wasm.getMatchingSegment(object_id, symbol_index);
4663 const atom_index = try wasm.createAtom(symbol_index, object_id.toOptional());
4664 try wasm.appendAtomAtIndex(final_index, atom_index);
4665
4666 const atom = wasm.getAtomPtr(atom_index);
4667 atom.size = relocatable_data.size;
4668 atom.alignment = relocatable_data.getAlignment(object);
4669 atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]);
4670 atom.original_offset = relocatable_data.offset;
4671
4672 const segment = wasm.segmentPtr(final_index);
4673 if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned
4674 segment.alignment = segment.alignment.max(atom.alignment);
4675 }
4676
4677 if (object.relocations.get(relocatable_data.section_index)) |relocations| {
4678 const start = searchRelocStart(relocations, relocatable_data.offset);
4679 const len = searchRelocEnd(relocations[start..], relocatable_data.offset + atom.size);
4680 atom.relocs = std.ArrayListUnmanaged(Wasm.Relocation).fromOwnedSlice(relocations[start..][0..len]);
4681 for (atom.relocs.items) |reloc| {
4682 switch (reloc.relocation_type) {
4683 .R_WASM_TABLE_INDEX_I32,
4684 .R_WASM_TABLE_INDEX_I64,
4685 .R_WASM_TABLE_INDEX_SLEB,
4686 .R_WASM_TABLE_INDEX_SLEB64,
4687 => {
4688 try wasm.function_table.put(gpa, .{
4689 .file = object_id.toOptional(),
4690 .index = @enumFromInt(reloc.index),
4691 }, 0);
4692 },
4693 .R_WASM_GLOBAL_INDEX_I32,
4694 .R_WASM_GLOBAL_INDEX_LEB,
4695 => {
4696 const sym = object.symtable[reloc.index];
4697 if (sym.tag != .global) {
4698 try wasm.got_symbols.append(gpa, .{
4699 .file = object_id.toOptional(),
4700 .index = @enumFromInt(reloc.index),
4701 });
4702 }
4703 },
4704 else => {},
4705 }
4706 }4540 }
4707 }4541 }
4542 // Otherwise it's a zero bit type; any address will do.
4543 return 0;
4544}
47084545
4709 return atom_index;4546/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.
4547pub fn errorNameTableAddr(wasm: *Wasm) u32 {
4548 assert(wasm.flush_buffer.memory_layout_finished);
4549 const comp = wasm.base.comp;
4550 assert(comp.config.output_mode != .Obj);
4551 return wasm.flush_buffer.data_segments.get(.__zig_error_name_table).?;
4710}4552}
47114553
4712fn searchRelocStart(relocs: []const Wasm.Relocation, address: u32) usize {4554fn convertZcuFnType(
4713 var min: usize = 0;4555 comp: *Compilation,
4714 var max: usize = relocs.len;4556 cc: std.builtin.CallingConvention,
4715 while (min < max) {4557 params: []const InternPool.Index,
4716 const index = (min + max) / 2;4558 return_type: Zcu.Type,
4717 const curr = relocs[index];4559 target: *const std.Target,
4718 if (curr.offset < address) {4560 params_buffer: *std.ArrayListUnmanaged(std.wasm.Valtype),
4719 min = index + 1;4561 returns_buffer: *std.ArrayListUnmanaged(std.wasm.Valtype),
4562) Allocator.Error!void {
4563 params_buffer.clearRetainingCapacity();
4564 returns_buffer.clearRetainingCapacity();
4565
4566 const gpa = comp.gpa;
4567 const zcu = comp.zcu.?;
4568
4569 if (CodeGen.firstParamSRet(cc, return_type, zcu, target)) {
4570 try params_buffer.append(gpa, .i32); // memory address is always a 32-bit handle
4571 } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) {
4572 if (cc == .wasm_watc) {
4573 const res_classes = abi.classifyType(return_type, zcu);
4574 assert(res_classes[0] == .direct and res_classes[1] == .none);
4575 const scalar_type = abi.scalarType(return_type, zcu);
4576 try returns_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target));
4720 } else {4577 } else {
4721 max = index;4578 try returns_buffer.append(gpa, CodeGen.typeToValtype(return_type, zcu, target));
4722 }4579 }
4580 } else if (return_type.isError(zcu)) {
4581 try returns_buffer.append(gpa, .i32);
4723 }4582 }
4724 return min;
4725}
47264583
4727fn searchRelocEnd(relocs: []const Wasm.Relocation, address: u32) usize {4584 // param types
4728 for (relocs, 0..relocs.len) |reloc, index| {4585 for (params) |param_type_ip| {
4729 if (reloc.offset > address) {4586 const param_type = Zcu.Type.fromInterned(param_type_ip);
4730 return index;4587 if (!param_type.hasRuntimeBitsIgnoreComptime(zcu)) continue;
4588
4589 switch (cc) {
4590 .wasm_watc => {
4591 const param_classes = abi.classifyType(param_type, zcu);
4592 if (param_classes[1] == .none) {
4593 if (param_classes[0] == .direct) {
4594 const scalar_type = abi.scalarType(param_type, zcu);
4595 try params_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target));
4596 } else {
4597 try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target));
4598 }
4599 } else {
4600 // i128/f128
4601 try params_buffer.append(gpa, .i64);
4602 try params_buffer.append(gpa, .i64);
4603 }
4604 },
4605 else => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)),
4731 }4606 }
4732 }4607 }
4733 return relocs.len;
4734}4608}
47354609
4736pub fn internString(wasm: *Wasm, bytes: []const u8) error{OutOfMemory}!String {4610pub fn isBss(wasm: *const Wasm, optional_name: OptionalString) bool {
4737 const gpa = wasm.base.comp.gpa;4611 const s = optional_name.slice(wasm) orelse return false;
4738 const gop = try wasm.string_table.getOrPutContextAdapted(4612 return mem.eql(u8, s, ".bss") or mem.startsWith(u8, s, ".bss.");
4739 gpa,4613}
4740 @as([]const u8, bytes),
4741 @as(String.TableIndexAdapter, .{ .bytes = wasm.string_bytes.items }),
4742 @as(String.TableContext, .{ .bytes = wasm.string_bytes.items }),
4743 );
4744 if (gop.found_existing) return gop.key_ptr.*;
4745
4746 try wasm.string_bytes.ensureUnusedCapacity(gpa, bytes.len + 1);
4747 const new_off: String = @enumFromInt(wasm.string_bytes.items.len);
47484614
4749 wasm.string_bytes.appendSliceAssumeCapacity(bytes);4615/// After this function is called, there may be additional entries in
4750 wasm.string_bytes.appendAssumeCapacity(0);4616/// `Wasm.uavs_obj`, `Wasm.uavs_exe`, `Wasm.navs_obj`, and `Wasm.navs_exe`
4617/// which have uninitialized code and relocations. This function is
4618/// non-recursive, so callers must coordinate additional calls to populate
4619/// those entries.
4620fn lowerZcuData(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !ZcuDataObj {
4621 const code_start: u32 = @intCast(wasm.string_bytes.items.len);
4622 const relocs_start: u32 = @intCast(wasm.out_relocs.len);
4623 const uav_fixups_start: u32 = @intCast(wasm.uav_fixups.items.len);
4624 const nav_fixups_start: u32 = @intCast(wasm.nav_fixups.items.len);
4625 const func_table_fixups_start: u32 = @intCast(wasm.func_table_fixups.items.len);
4626 wasm.string_bytes_lock.lock();
4627
4628 try codegen.generateSymbol(&wasm.base, pt, .unneeded, .fromInterned(ip_index), &wasm.string_bytes, .none);
4629
4630 const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start);
4631 const relocs_len: u32 = @intCast(wasm.out_relocs.len - relocs_start);
4632 const any_fixups =
4633 uav_fixups_start != wasm.uav_fixups.items.len or
4634 nav_fixups_start != wasm.nav_fixups.items.len or
4635 func_table_fixups_start != wasm.func_table_fixups.items.len;
4636 wasm.string_bytes_lock.unlock();
4637
4638 const naive_code: DataPayload = .{
4639 .off = @enumFromInt(code_start),
4640 .len = code_len,
4641 };
47514642
4752 gop.key_ptr.* = new_off;4643 // Only nonzero init values need to take up space in the output.
4644 // If any fixups are present, we still need the string bytes allocated since
4645 // that is the staging area for the fixups.
4646 const code: DataPayload = if (!any_fixups and std.mem.allEqual(u8, naive_code.slice(wasm), 0)) c: {
4647 wasm.string_bytes.shrinkRetainingCapacity(code_start);
4648 // Indicate empty by making off and len the same value, however, still
4649 // transmit the data size by using the size as that value.
4650 break :c .{
4651 .off = .none,
4652 .len = naive_code.len,
4653 };
4654 } else c: {
4655 wasm.any_passive_inits = wasm.any_passive_inits or wasm.base.comp.config.import_memory;
4656 break :c naive_code;
4657 };
47534658
4754 return new_off;4659 return .{
4660 .code = code,
4661 .relocs = .{
4662 .off = relocs_start,
4663 .len = relocs_len,
4664 },
4665 };
4755}4666}
47564667
4757pub fn getExistingString(wasm: *const Wasm, bytes: []const u8) ?String {4668fn pointerAlignment(wasm: *const Wasm) Alignment {
4758 return wasm.string_table.getKeyAdapted(bytes, @as(String.TableIndexAdapter, .{4669 const target = &wasm.base.comp.root_mod.resolved_target.result;
4759 .bytes = wasm.string_bytes.items,4670 return switch (target.cpu.arch) {
4760 }));4671 .wasm32 => .@"4",
4672 .wasm64 => .@"8",
4673 else => unreachable,
4674 };
4761}4675}
47624676
4763pub fn stringSlice(wasm: *const Wasm, index: String) [:0]const u8 {4677fn pointerSize(wasm: *const Wasm) u32 {
4764 const slice = wasm.string_bytes.items[@intFromEnum(index)..];4678 const target = &wasm.base.comp.root_mod.resolved_target.result;
4765 return slice[0..mem.indexOfScalar(u8, slice, 0).? :0];4679 return switch (target.cpu.arch) {
4680 .wasm32 => 4,
4681 .wasm64 => 8,
4682 else => unreachable,
4683 };
4766}4684}
47674685
4768pub fn optionalStringSlice(wasm: *const Wasm, index: OptionalString) ?[:0]const u8 {4686fn addZcuImportReserved(wasm: *Wasm, nav_index: InternPool.Nav.Index) ZcuImportIndex {
4769 return stringSlice(wasm, index.unwrap() orelse return null);4687 const gop = wasm.imports.getOrPutAssumeCapacity(nav_index);
4688 gop.value_ptr.* = {};
4689 return @enumFromInt(gop.index);
4770}4690}
47714691
4772pub fn castToString(wasm: *const Wasm, index: u32) String {4692fn resolveFunctionSynthetic(
4773 assert(index == 0 or wasm.string_bytes.items[index - 1] == 0);4693 wasm: *Wasm,
4774 return @enumFromInt(index);4694 import: *FunctionImport,
4695 res: FunctionImport.Resolution,
4696 params: []const std.wasm.Valtype,
4697 returns: []const std.wasm.Valtype,
4698) link.File.FlushError!void {
4699 import.resolution = res;
4700 wasm.functions.putAssumeCapacity(res, {});
4701 // This is not only used for type-checking but also ensures the function
4702 // type index is interned so that it is guaranteed to exist during `flush`.
4703 const correct_func_type = try addFuncType(wasm, .{
4704 .params = try internValtypeList(wasm, params),
4705 .returns = try internValtypeList(wasm, returns),
4706 });
4707 if (import.type != correct_func_type) {
4708 const diags = &wasm.base.comp.link_diags;
4709 return import.source_location.fail(diags, "synthetic function {s} {} imported with incorrect signature {}", .{
4710 @tagName(res), correct_func_type.fmt(wasm), import.type.fmt(wasm),
4711 });
4712 }
4775}4713}
47764714
4777fn segmentPtr(wasm: *const Wasm, index: Segment.Index) *Segment {4715pub fn addFunction(
4778 return &wasm.segments.items[@intFromEnum(index)];4716 wasm: *Wasm,
4717 resolution: FunctionImport.Resolution,
4718 params: []const std.wasm.Valtype,
4719 returns: []const std.wasm.Valtype,
4720) Allocator.Error!void {
4721 wasm.functions.putAssumeCapacity(resolution, {});
4722 _ = try wasm.addFuncType(.{
4723 .params = try wasm.internValtypeList(params),
4724 .returns = try wasm.internValtypeList(returns),
4725 });
4779}4726}
src/link/Wasm/Archive.zig+14-3
...@@ -142,8 +142,18 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive {...@@ -142,8 +142,18 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive {
142142
143/// From a given file offset, starts reading for a file header.143/// From a given file offset, starts reading for a file header.
144/// When found, parses the object file into an `Object` and returns it.144/// When found, parses the object file into an `Object` and returns it.
145pub fn parseObject(archive: Archive, wasm: *Wasm, file_contents: []const u8, path: Path) !Object {145pub fn parseObject(
146 const header = mem.bytesAsValue(Header, file_contents[0..@sizeOf(Header)]);146 archive: Archive,
147 wasm: *Wasm,
148 file_contents: []const u8,
149 object_offset: u32,
150 path: Path,
151 host_name: Wasm.OptionalString,
152 scratch_space: *Object.ScratchSpace,
153 must_link: bool,
154 gc_sections: bool,
155) !Object {
156 const header = mem.bytesAsValue(Header, file_contents[object_offset..][0..@sizeOf(Header)]);
147 if (!mem.eql(u8, &header.fmag, ARFMAG)) return error.BadHeaderDelimiter;157 if (!mem.eql(u8, &header.fmag, ARFMAG)) return error.BadHeaderDelimiter;
148158
149 const name_or_index = try header.nameOrIndex();159 const name_or_index = try header.nameOrIndex();
...@@ -157,8 +167,9 @@ pub fn parseObject(archive: Archive, wasm: *Wasm, file_contents: []const u8, pat...@@ -157,8 +167,9 @@ pub fn parseObject(archive: Archive, wasm: *Wasm, file_contents: []const u8, pat
157 };167 };
158168
159 const object_file_size = try header.parsedSize();169 const object_file_size = try header.parsedSize();
170 const contents = file_contents[object_offset + @sizeOf(Header) ..][0..object_file_size];
160171
161 return Object.create(wasm, file_contents[@sizeOf(Header)..][0..object_file_size], path, object_name);172 return Object.parse(wasm, contents, path, object_name, host_name, scratch_space, must_link, gc_sections);
162}173}
163174
164const Archive = @This();175const Archive = @This();
src/link/Wasm/Flush.zig created+1975
...@@ -0,0 +1,1975 @@
1//! Temporary, dynamically allocated structures used only during flush.
2//! Could be constructed fresh each time, or kept around between updates to reduce heap allocations.
3
4const Flush = @This();
5const Wasm = @import("../Wasm.zig");
6const Object = @import("Object.zig");
7const Zcu = @import("../../Zcu.zig");
8const Alignment = Wasm.Alignment;
9const String = Wasm.String;
10const Relocation = Wasm.Relocation;
11const InternPool = @import("../../InternPool.zig");
12
13const build_options = @import("build_options");
14
15const std = @import("std");
16const Allocator = std.mem.Allocator;
17const mem = std.mem;
18const leb = std.leb;
19const log = std.log.scoped(.link);
20const assert = std.debug.assert;
21
22/// Ordered list of data segments that will appear in the final binary.
23/// When sorted, to-be-merged segments will be made adjacent.
24/// Values are virtual address.
25data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegmentId, u32) = .empty,
26/// Each time a `data_segment` offset equals zero it indicates a new group, and
27/// the next element in this array will contain the total merged segment size.
28/// Value is the virtual memory address of the end of the segment.
29data_segment_groups: std.ArrayListUnmanaged(DataSegmentGroup) = .empty,
30
31binary_bytes: std.ArrayListUnmanaged(u8) = .empty,
32missing_exports: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
33function_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.FunctionImportId) = .empty,
34global_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.GlobalImportId) = .empty,
35data_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.DataImportId) = .empty,
36
37indirect_function_table: std.AutoArrayHashMapUnmanaged(Wasm.OutputFunctionIndex, void) = .empty,
38
39/// A subset of the full interned function type list created only during flush.
40func_types: std.AutoArrayHashMapUnmanaged(Wasm.FunctionType.Index, void) = .empty,
41
42/// For debug purposes only.
43memory_layout_finished: bool = false,
44
45/// Index into `func_types`.
46pub const FuncTypeIndex = enum(u32) {
47 _,
48
49 pub fn fromTypeIndex(i: Wasm.FunctionType.Index, f: *const Flush) FuncTypeIndex {
50 return @enumFromInt(f.func_types.getIndex(i).?);
51 }
52};
53
54/// Index into `indirect_function_table`.
55const IndirectFunctionTableIndex = enum(u32) {
56 _,
57
58 fn fromObjectFunctionHandlingWeak(wasm: *const Wasm, index: Wasm.ObjectFunctionIndex) IndirectFunctionTableIndex {
59 return fromOutputFunctionIndex(&wasm.flush_buffer, .fromObjectFunctionHandlingWeak(wasm, index));
60 }
61
62 fn fromSymbolName(wasm: *const Wasm, name: String) IndirectFunctionTableIndex {
63 return fromOutputFunctionIndex(&wasm.flush_buffer, .fromSymbolName(wasm, name));
64 }
65
66 fn fromOutputFunctionIndex(f: *const Flush, i: Wasm.OutputFunctionIndex) IndirectFunctionTableIndex {
67 return @enumFromInt(f.indirect_function_table.getIndex(i).?);
68 }
69
70 fn fromZcuIndirectFunctionSetIndex(i: Wasm.ZcuIndirectFunctionSetIndex) IndirectFunctionTableIndex {
71 // These are the same since those are added to the table first.
72 return @enumFromInt(@intFromEnum(i));
73 }
74
75 fn toAbi(i: IndirectFunctionTableIndex) u32 {
76 return @intFromEnum(i) + 1;
77 }
78};
79
80const DataSegmentGroup = struct {
81 first_segment: Wasm.DataSegmentId,
82 end_addr: u32,
83};
84
85pub fn clear(f: *Flush) void {
86 f.data_segments.clearRetainingCapacity();
87 f.data_segment_groups.clearRetainingCapacity();
88 f.binary_bytes.clearRetainingCapacity();
89 f.indirect_function_table.clearRetainingCapacity();
90 f.func_types.clearRetainingCapacity();
91 f.memory_layout_finished = false;
92}
93
94pub fn deinit(f: *Flush, gpa: Allocator) void {
95 f.data_segments.deinit(gpa);
96 f.data_segment_groups.deinit(gpa);
97 f.binary_bytes.deinit(gpa);
98 f.missing_exports.deinit(gpa);
99 f.function_imports.deinit(gpa);
100 f.global_imports.deinit(gpa);
101 f.data_imports.deinit(gpa);
102 f.indirect_function_table.deinit(gpa);
103 f.func_types.deinit(gpa);
104 f.* = undefined;
105}
106
107pub fn finish(f: *Flush, wasm: *Wasm) !void {
108 const comp = wasm.base.comp;
109 const shared_memory = comp.config.shared_memory;
110 const diags = &comp.link_diags;
111 const gpa = comp.gpa;
112 const import_memory = comp.config.import_memory;
113 const export_memory = comp.config.export_memory;
114 const target = &comp.root_mod.resolved_target.result;
115 const is64 = switch (target.cpu.arch) {
116 .wasm32 => false,
117 .wasm64 => true,
118 else => unreachable,
119 };
120 const is_obj = comp.config.output_mode == .Obj;
121 const allow_undefined = is_obj or wasm.import_symbols;
122
123 const entry_name = if (wasm.entry_resolution.isNavOrUnresolved(wasm)) wasm.entry_name else .none;
124
125 if (comp.zcu) |zcu| {
126 const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed!
127
128 // Detect any intrinsics that were called; they need to have dependencies on the symbols marked.
129 // Likewise detect `@tagName` calls so those functions can be included in the output and synthesized.
130 for (wasm.mir_instructions.items(.tag), wasm.mir_instructions.items(.data)) |tag, *data| switch (tag) {
131 .call_intrinsic => {
132 const symbol_name = try wasm.internString(@tagName(data.intrinsic));
133 const i: Wasm.FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(symbol_name) orelse {
134 return diags.fail("missing compiler runtime intrinsic '{s}' (undefined linker symbol)", .{
135 @tagName(data.intrinsic),
136 });
137 });
138 try wasm.markFunctionImport(symbol_name, i.value(wasm), i);
139 },
140 .call_tag_name => {
141 assert(ip.indexToKey(data.ip_index) == .enum_type);
142 const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index);
143 if (!gop.found_existing) {
144 wasm.tag_name_table_ref_count += 1;
145 const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).intTagType(zcu);
146 gop.value_ptr.* = .{ .tag_name = .{
147 .symbol_name = try wasm.internStringFmt("__zig_tag_name_{d}", .{@intFromEnum(data.ip_index)}),
148 .type_index = try wasm.internFunctionType(.Unspecified, &.{int_tag_ty.ip_index}, .slice_const_u8_sentinel_0, target),
149 .table_index = @intCast(wasm.tag_name_offs.items.len),
150 } };
151 try wasm.functions.put(gpa, .fromZcuFunc(wasm, @enumFromInt(gop.index)), {});
152 const tag_names = ip.loadEnumType(data.ip_index).names;
153 for (tag_names.get(ip)) |tag_name| {
154 const slice = tag_name.toSlice(ip);
155 try wasm.tag_name_offs.append(gpa, @intCast(wasm.tag_name_bytes.items.len));
156 try wasm.tag_name_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]);
157 }
158 }
159 },
160 else => continue,
161 };
162
163 {
164 var i = wasm.function_imports_len_prelink;
165 while (i < f.function_imports.entries.len) {
166 const symbol_name = f.function_imports.keys()[i];
167 if (wasm.object_function_imports.getIndex(symbol_name)) |import_index_usize| {
168 const import_index: Wasm.FunctionImport.Index = @enumFromInt(import_index_usize);
169 try wasm.markFunctionImport(symbol_name, import_index.value(wasm), import_index);
170 f.function_imports.swapRemoveAt(i);
171 continue;
172 }
173 i += 1;
174 }
175 }
176
177 {
178 var i = wasm.data_imports_len_prelink;
179 while (i < f.data_imports.entries.len) {
180 const symbol_name = f.data_imports.keys()[i];
181 if (wasm.object_data_imports.getIndex(symbol_name)) |import_index_usize| {
182 const import_index: Wasm.ObjectDataImport.Index = @enumFromInt(import_index_usize);
183 try wasm.markDataImport(symbol_name, import_index.value(wasm), import_index);
184 f.data_imports.swapRemoveAt(i);
185 continue;
186 }
187 i += 1;
188 }
189 }
190
191 if (wasm.error_name_table_ref_count > 0) {
192 // Ensure Zcu error name structures are populated.
193 const full_error_names = ip.global_error_set.getNamesFromMainThread();
194 try wasm.error_name_offs.ensureTotalCapacity(gpa, full_error_names.len + 1);
195 if (wasm.error_name_offs.items.len == 0) {
196 // Dummy entry at index 0 to avoid a sub instruction at `@errorName` sites.
197 wasm.error_name_offs.appendAssumeCapacity(0);
198 }
199 const new_error_names = full_error_names[wasm.error_name_offs.items.len - 1 ..];
200 for (new_error_names) |error_name| {
201 wasm.error_name_offs.appendAssumeCapacity(@intCast(wasm.error_name_bytes.items.len));
202 const s: [:0]const u8 = error_name.toSlice(ip);
203 try wasm.error_name_bytes.appendSlice(gpa, s[0 .. s.len + 1]);
204 }
205 }
206
207 for (wasm.nav_exports.keys(), wasm.nav_exports.values()) |*nav_export, export_index| {
208 if (ip.isFunctionType(ip.getNav(nav_export.nav_index).typeOf(ip))) {
209 log.debug("flush export '{s}' nav={d}", .{ nav_export.name.slice(wasm), nav_export.nav_index });
210 const function_index = Wasm.FunctionIndex.fromIpNav(wasm, nav_export.nav_index).?;
211 const explicit = f.missing_exports.swapRemove(nav_export.name);
212 const is_hidden = !explicit and switch (export_index.ptr(zcu).opts.visibility) {
213 .hidden => true,
214 .default, .protected => false,
215 };
216 if (is_hidden) {
217 try wasm.hidden_function_exports.put(gpa, nav_export.name, function_index);
218 } else {
219 try wasm.function_exports.put(gpa, nav_export.name, function_index);
220 }
221 _ = f.function_imports.swapRemove(nav_export.name);
222
223 if (nav_export.name.toOptional() == entry_name)
224 wasm.entry_resolution = .fromIpNav(wasm, nav_export.nav_index);
225 } else {
226 // This is a data export because Zcu currently has no way to
227 // export wasm globals.
228 _ = f.missing_exports.swapRemove(nav_export.name);
229 _ = f.data_imports.swapRemove(nav_export.name);
230 if (!is_obj) {
231 diags.addError("unable to export data symbol '{s}'; not emitting a relocatable", .{
232 nav_export.name.slice(wasm),
233 });
234 }
235 }
236 }
237
238 for (f.missing_exports.keys()) |exp_name| {
239 diags.addError("manually specified export name '{s}' undefined", .{exp_name.slice(wasm)});
240 }
241 }
242
243 if (entry_name.unwrap()) |name| {
244 if (wasm.entry_resolution == .unresolved) {
245 var err = try diags.addErrorWithNotes(1);
246 try err.addMsg("entry symbol '{s}' missing", .{name.slice(wasm)});
247 err.addNote("'-fno-entry' suppresses this error", .{});
248 }
249 }
250
251 if (!allow_undefined) {
252 for (f.function_imports.keys(), f.function_imports.values()) |name, function_import_id| {
253 if (function_import_id.undefinedAllowed(wasm)) continue;
254 const src_loc = function_import_id.sourceLocation(wasm);
255 src_loc.addError(wasm, "undefined function: {s}", .{name.slice(wasm)});
256 }
257 for (f.global_imports.keys(), f.global_imports.values()) |name, global_import_id| {
258 const src_loc = global_import_id.sourceLocation(wasm);
259 src_loc.addError(wasm, "undefined global: {s}", .{name.slice(wasm)});
260 }
261 for (wasm.table_imports.keys(), wasm.table_imports.values()) |name, table_import_id| {
262 const src_loc = table_import_id.value(wasm).source_location;
263 src_loc.addError(wasm, "undefined table: {s}", .{name.slice(wasm)});
264 }
265 for (f.data_imports.keys(), f.data_imports.values()) |name, data_import_id| {
266 const src_loc = data_import_id.sourceLocation(wasm);
267 src_loc.addError(wasm, "undefined data: {s}", .{name.slice(wasm)});
268 }
269 }
270
271 if (diags.hasErrors()) return error.LinkFailure;
272
273 // Merge indirect function tables.
274 try f.indirect_function_table.ensureUnusedCapacity(gpa, wasm.zcu_indirect_function_set.entries.len +
275 wasm.object_indirect_function_import_set.entries.len + wasm.object_indirect_function_set.entries.len);
276 // This one goes first so the indexes can be stable for MIR lowering.
277 for (wasm.zcu_indirect_function_set.keys()) |nav_index|
278 f.indirect_function_table.putAssumeCapacity(.fromIpNav(wasm, nav_index), {});
279 for (wasm.object_indirect_function_import_set.keys()) |symbol_name|
280 f.indirect_function_table.putAssumeCapacity(.fromSymbolName(wasm, symbol_name), {});
281 for (wasm.object_indirect_function_set.keys()) |object_function_index|
282 f.indirect_function_table.putAssumeCapacity(.fromObjectFunction(wasm, object_function_index), {});
283
284 if (wasm.object_init_funcs.items.len > 0) {
285 // Zig has no constructors so these are only for object file inputs.
286 mem.sortUnstable(Wasm.InitFunc, wasm.object_init_funcs.items, {}, Wasm.InitFunc.lessThan);
287 try wasm.functions.put(gpa, .__wasm_call_ctors, {});
288 }
289
290 // Merge and order the data segments. Depends on garbage collection so that
291 // unused segments can be omitted.
292 try f.data_segments.ensureUnusedCapacity(gpa, wasm.data_segments.entries.len +
293 wasm.uavs_obj.entries.len + wasm.navs_obj.entries.len +
294 wasm.uavs_exe.entries.len + wasm.navs_exe.entries.len + 4);
295 if (is_obj) assert(wasm.uavs_exe.entries.len == 0);
296 if (is_obj) assert(wasm.navs_exe.entries.len == 0);
297 if (!is_obj) assert(wasm.uavs_obj.entries.len == 0);
298 if (!is_obj) assert(wasm.navs_obj.entries.len == 0);
299 for (0..wasm.uavs_obj.entries.len) |uavs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{
300 .uav_obj = @enumFromInt(uavs_index),
301 }), @as(u32, undefined));
302 for (0..wasm.navs_obj.entries.len) |navs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{
303 .nav_obj = @enumFromInt(navs_index),
304 }), @as(u32, undefined));
305 for (0..wasm.uavs_exe.entries.len) |uavs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{
306 .uav_exe = @enumFromInt(uavs_index),
307 }), @as(u32, undefined));
308 for (0..wasm.navs_exe.entries.len) |navs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{
309 .nav_exe = @enumFromInt(navs_index),
310 }), @as(u32, undefined));
311 if (wasm.error_name_table_ref_count > 0) {
312 f.data_segments.putAssumeCapacity(.__zig_error_names, @as(u32, undefined));
313 f.data_segments.putAssumeCapacity(.__zig_error_name_table, @as(u32, undefined));
314 }
315 if (wasm.tag_name_table_ref_count > 0) {
316 f.data_segments.putAssumeCapacity(.__zig_tag_names, @as(u32, undefined));
317 f.data_segments.putAssumeCapacity(.__zig_tag_name_table, @as(u32, undefined));
318 }
319 for (wasm.data_segments.keys()) |data_id| f.data_segments.putAssumeCapacity(data_id, @as(u32, undefined));
320
321 try wasm.functions.ensureUnusedCapacity(gpa, 3);
322
323 // Passive segments are used to avoid memory being reinitialized on each
324 // thread's instantiation. These passive segments are initialized and
325 // dropped in __wasm_init_memory, which is registered as the start function
326 // We also initialize bss segments (using memory.fill) as part of this
327 // function.
328 if (wasm.any_passive_inits) {
329 try wasm.addFunction(.__wasm_init_memory, &.{}, &.{});
330 }
331
332 try wasm.tables.ensureUnusedCapacity(gpa, 1);
333
334 if (f.indirect_function_table.entries.len > 0) {
335 wasm.tables.putAssumeCapacity(.__indirect_function_table, {});
336 }
337
338 // Sort order:
339 // 0. Segment category (tls, data, zero)
340 // 1. Segment name prefix
341 // 2. Segment alignment
342 // 3. Reference count, descending (optimize for LEB encoding)
343 // 4. Segment name suffix
344 // 5. Segment ID interpreted as an integer (for determinism)
345 //
346 // TLS segments are intended to be merged with each other, and segments
347 // with a common prefix name are intended to be merged with each other.
348 // Sorting ensures the segments intended to be merged will be adjacent.
349 //
350 // Each Zcu Nav and Cau has an independent data segment ID in this logic.
351 // For the purposes of sorting, they are implicitly all named ".data".
352 const Sort = struct {
353 wasm: *const Wasm,
354 segments: []const Wasm.DataSegmentId,
355 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {
356 const lhs_segment = ctx.segments[lhs];
357 const rhs_segment = ctx.segments[rhs];
358 const lhs_category = @intFromEnum(lhs_segment.category(ctx.wasm));
359 const rhs_category = @intFromEnum(rhs_segment.category(ctx.wasm));
360 switch (std.math.order(lhs_category, rhs_category)) {
361 .lt => return true,
362 .gt => return false,
363 .eq => {},
364 }
365 const lhs_segment_name = lhs_segment.name(ctx.wasm);
366 const rhs_segment_name = rhs_segment.name(ctx.wasm);
367 const lhs_prefix, const lhs_suffix = splitSegmentName(lhs_segment_name);
368 const rhs_prefix, const rhs_suffix = splitSegmentName(rhs_segment_name);
369 switch (mem.order(u8, lhs_prefix, rhs_prefix)) {
370 .lt => return true,
371 .gt => return false,
372 .eq => {},
373 }
374 const lhs_alignment = lhs_segment.alignment(ctx.wasm);
375 const rhs_alignment = rhs_segment.alignment(ctx.wasm);
376 switch (lhs_alignment.order(rhs_alignment)) {
377 .lt => return false,
378 .gt => return true,
379 .eq => {},
380 }
381 switch (std.math.order(lhs_segment.refCount(ctx.wasm), rhs_segment.refCount(ctx.wasm))) {
382 .lt => return false,
383 .gt => return true,
384 .eq => {},
385 }
386 switch (mem.order(u8, lhs_suffix, rhs_suffix)) {
387 .lt => return true,
388 .gt => return false,
389 .eq => {},
390 }
391 return @intFromEnum(lhs_segment) < @intFromEnum(rhs_segment);
392 }
393 };
394 f.data_segments.sortUnstable(@as(Sort, .{
395 .wasm = wasm,
396 .segments = f.data_segments.keys(),
397 }));
398
399 const page_size = std.wasm.page_size; // 64kb
400 const stack_alignment: Alignment = .@"16"; // wasm's stack alignment as specified by tool-convention
401 const heap_alignment: Alignment = .@"16"; // wasm's heap alignment as specified by tool-convention
402 const pointer_alignment: Alignment = .@"4";
403 // Always place the stack at the start by default unless the user specified the global-base flag.
404 const place_stack_first, var memory_ptr: u64 = if (wasm.global_base) |base| .{ false, base } else .{ true, 0 };
405
406 var virtual_addrs: VirtualAddrs = .{
407 .stack_pointer = undefined,
408 .heap_base = undefined,
409 .heap_end = undefined,
410 .tls_base = null,
411 .tls_align = .none,
412 .tls_size = null,
413 .init_memory_flag = null,
414 };
415
416 if (place_stack_first and !is_obj) {
417 memory_ptr = stack_alignment.forward(memory_ptr);
418 memory_ptr += wasm.base.stack_size;
419 virtual_addrs.stack_pointer = @intCast(memory_ptr);
420 }
421
422 const segment_ids = f.data_segments.keys();
423 const segment_vaddrs = f.data_segments.values();
424 assert(f.data_segment_groups.items.len == 0);
425 const data_vaddr: u32 = @intCast(memory_ptr);
426 if (segment_ids.len > 0) {
427 var seen_tls: enum { before, during, after } = .before;
428 var category: Wasm.DataSegmentId.Category = undefined;
429 var first_segment: Wasm.DataSegmentId = segment_ids[0];
430 for (segment_ids, segment_vaddrs, 0..) |segment_id, *segment_vaddr, i| {
431 const alignment = segment_id.alignment(wasm);
432 category = segment_id.category(wasm);
433 const start_addr = alignment.forward(memory_ptr);
434
435 const want_new_segment = b: {
436 if (is_obj) break :b false;
437 switch (seen_tls) {
438 .before => switch (category) {
439 .tls => {
440 virtual_addrs.tls_base = if (shared_memory) 0 else @intCast(start_addr);
441 virtual_addrs.tls_align = alignment;
442 seen_tls = .during;
443 break :b f.data_segment_groups.items.len > 0;
444 },
445 else => {},
446 },
447 .during => switch (category) {
448 .tls => {
449 virtual_addrs.tls_align = virtual_addrs.tls_align.maxStrict(alignment);
450 virtual_addrs.tls_size = @intCast(memory_ptr - virtual_addrs.tls_base.?);
451 break :b false;
452 },
453 else => {
454 seen_tls = .after;
455 break :b true;
456 },
457 },
458 .after => {},
459 }
460 break :b i >= 1 and !wantSegmentMerge(wasm, segment_ids[i - 1], segment_id, category);
461 };
462 if (want_new_segment) {
463 log.debug("new segment group at 0x{x} {} {s} {}", .{ start_addr, segment_id, segment_id.name(wasm), category });
464 try f.data_segment_groups.append(gpa, .{
465 .end_addr = @intCast(memory_ptr),
466 .first_segment = first_segment,
467 });
468 first_segment = segment_id;
469 }
470
471 const size = segment_id.size(wasm);
472 segment_vaddr.* = @intCast(start_addr);
473 log.debug("0x{x} {d} {s}", .{ start_addr, @intFromEnum(segment_id), segment_id.name(wasm) });
474 memory_ptr = start_addr + size;
475 }
476 if (category != .zero) try f.data_segment_groups.append(gpa, .{
477 .first_segment = first_segment,
478 .end_addr = @intCast(memory_ptr),
479 });
480 if (category == .tls and seen_tls == .during) {
481 virtual_addrs.tls_size = @intCast(memory_ptr - virtual_addrs.tls_base.?);
482 }
483 }
484
485 if (shared_memory and wasm.any_passive_inits) {
486 memory_ptr = pointer_alignment.forward(memory_ptr);
487 virtual_addrs.init_memory_flag = @intCast(memory_ptr);
488 memory_ptr += 4;
489 }
490
491 if (!place_stack_first and !is_obj) {
492 memory_ptr = stack_alignment.forward(memory_ptr);
493 memory_ptr += wasm.base.stack_size;
494 virtual_addrs.stack_pointer = @intCast(memory_ptr);
495 }
496
497 memory_ptr = heap_alignment.forward(memory_ptr);
498 virtual_addrs.heap_base = @intCast(memory_ptr);
499
500 if (wasm.initial_memory) |initial_memory| {
501 if (!mem.isAlignedGeneric(u64, initial_memory, page_size)) {
502 diags.addError("initial memory value {d} is not {d}-byte aligned", .{ initial_memory, page_size });
503 }
504 if (memory_ptr > initial_memory) {
505 diags.addError("initial memory value {d} insufficient; minimum {d}", .{ initial_memory, memory_ptr });
506 }
507 if (initial_memory > std.math.maxInt(u32)) {
508 diags.addError("initial memory value {d} exceeds 32-bit address space", .{initial_memory});
509 }
510 if (diags.hasErrors()) return error.LinkFailure;
511 memory_ptr = initial_memory;
512 } else {
513 memory_ptr = mem.alignForward(u64, memory_ptr, std.wasm.page_size);
514 }
515 virtual_addrs.heap_end = @intCast(memory_ptr);
516
517 // In case we do not import memory, but define it ourselves, set the
518 // minimum amount of pages on the memory section.
519 wasm.memories.limits.min = @intCast(memory_ptr / page_size);
520 log.debug("total memory pages: {d}", .{wasm.memories.limits.min});
521
522 if (wasm.max_memory) |max_memory| {
523 if (!mem.isAlignedGeneric(u64, max_memory, page_size)) {
524 diags.addError("maximum memory value {d} is not {d}-byte aligned", .{ max_memory, page_size });
525 }
526 if (memory_ptr > max_memory) {
527 diags.addError("maximum memory value {d} insufficient; minimum {d}", .{ max_memory, memory_ptr });
528 }
529 if (max_memory > std.math.maxInt(u32)) {
530 diags.addError("maximum memory value {d} exceeds 32-bit address space", .{max_memory});
531 }
532 if (diags.hasErrors()) return error.LinkFailure;
533 wasm.memories.limits.max = @intCast(max_memory / page_size);
534 wasm.memories.limits.flags.has_max = true;
535 if (shared_memory) wasm.memories.limits.flags.is_shared = true;
536 log.debug("maximum memory pages: {?d}", .{wasm.memories.limits.max});
537 }
538 f.memory_layout_finished = true;
539
540 // When we have TLS GOT entries and shared memory is enabled, we must
541 // perform runtime relocations or else we don't create the function.
542 if (shared_memory and virtual_addrs.tls_base != null) {
543 // This logic that checks `any_tls_relocs` is missing the part where it
544 // also notices threadlocal globals from Zcu code.
545 if (wasm.any_tls_relocs) try wasm.addFunction(.__wasm_apply_global_tls_relocs, &.{}, &.{});
546 try wasm.addFunction(.__wasm_init_tls, &.{.i32}, &.{});
547 try wasm.globals.ensureUnusedCapacity(gpa, 3);
548 wasm.globals.putAssumeCapacity(.__tls_base, {});
549 wasm.globals.putAssumeCapacity(.__tls_size, {});
550 wasm.globals.putAssumeCapacity(.__tls_align, {});
551 }
552
553 var section_index: u32 = 0;
554 // Index of the code section. Used to tell relocation table where the section lives.
555 var code_section_index: ?u32 = null;
556 // Index of the data section. Used to tell relocation table where the section lives.
557 var data_section_index: ?u32 = null;
558
559 const binary_bytes = &f.binary_bytes;
560 assert(binary_bytes.items.len == 0);
561
562 try binary_bytes.appendSlice(gpa, &std.wasm.magic ++ &std.wasm.version);
563 assert(binary_bytes.items.len == 8);
564
565 const binary_writer = binary_bytes.writer(gpa);
566
567 // Type section.
568 for (f.function_imports.values()) |id| {
569 try f.func_types.put(gpa, id.functionType(wasm), {});
570 }
571 for (wasm.functions.keys()) |function| {
572 try f.func_types.put(gpa, function.typeIndex(wasm), {});
573 }
574 if (f.func_types.entries.len != 0) {
575 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
576 for (f.func_types.keys()) |func_type_index| {
577 const func_type = func_type_index.ptr(wasm);
578 try leb.writeUleb128(binary_writer, std.wasm.function_type);
579 const params = func_type.params.slice(wasm);
580 try leb.writeUleb128(binary_writer, @as(u32, @intCast(params.len)));
581 for (params) |param_ty| {
582 try leb.writeUleb128(binary_writer, @intFromEnum(param_ty));
583 }
584 const returns = func_type.returns.slice(wasm);
585 try leb.writeUleb128(binary_writer, @as(u32, @intCast(returns.len)));
586 for (returns) |ret_ty| {
587 try leb.writeUleb128(binary_writer, @intFromEnum(ret_ty));
588 }
589 }
590 replaceVecSectionHeader(binary_bytes, header_offset, .type, @intCast(f.func_types.entries.len));
591 section_index += 1;
592 }
593
594 if (!is_obj) {
595 // TODO: sort function_imports by ref count descending for optimal LEB encodings
596 // TODO: sort global_imports by ref count descending for optimal LEB encodings
597 // TODO: sort output functions by ref count descending for optimal LEB encodings
598 }
599
600 // Import section
601 {
602 var total_imports: usize = 0;
603 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
604
605 for (f.function_imports.values()) |id| {
606 const module_name = id.moduleName(wasm).slice(wasm).?;
607 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
608 try binary_writer.writeAll(module_name);
609
610 const name = id.importName(wasm).slice(wasm);
611 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
612 try binary_writer.writeAll(name);
613
614 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.function));
615 const type_index: FuncTypeIndex = .fromTypeIndex(id.functionType(wasm), f);
616 try leb.writeUleb128(binary_writer, @intFromEnum(type_index));
617 }
618 total_imports += f.function_imports.entries.len;
619
620 for (wasm.table_imports.values()) |id| {
621 const table_import = id.value(wasm);
622 const module_name = table_import.module_name.slice(wasm);
623 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
624 try binary_writer.writeAll(module_name);
625
626 const name = table_import.name.slice(wasm);
627 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
628 try binary_writer.writeAll(name);
629
630 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.table));
631 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to())));
632 try emitLimits(gpa, binary_bytes, table_import.limits());
633 }
634 total_imports += wasm.table_imports.entries.len;
635
636 if (import_memory) {
637 const name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory;
638 try emitMemoryImport(wasm, binary_bytes, name, &.{
639 // TODO the import_memory option needs to specify from which module
640 .module_name = wasm.object_host_name.unwrap().?,
641 .limits_min = wasm.memories.limits.min,
642 .limits_max = wasm.memories.limits.max,
643 .limits_has_max = wasm.memories.limits.flags.has_max,
644 .limits_is_shared = wasm.memories.limits.flags.is_shared,
645 .source_location = .none,
646 });
647 total_imports += 1;
648 }
649
650 for (f.global_imports.values()) |id| {
651 const module_name = id.moduleName(wasm).slice(wasm).?;
652 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
653 try binary_writer.writeAll(module_name);
654
655 const name = id.importName(wasm).slice(wasm);
656 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
657 try binary_writer.writeAll(name);
658
659 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.global));
660 const global_type = id.globalType(wasm);
661 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype)));
662 try binary_writer.writeByte(@intFromBool(global_type.mutable));
663 }
664 total_imports += f.global_imports.entries.len;
665
666 if (total_imports > 0) {
667 replaceVecSectionHeader(binary_bytes, header_offset, .import, @intCast(total_imports));
668 section_index += 1;
669 } else {
670 binary_bytes.shrinkRetainingCapacity(header_offset);
671 }
672 }
673
674 // Function section
675 if (wasm.functions.count() != 0) {
676 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
677 for (wasm.functions.keys()) |function| {
678 const index: FuncTypeIndex = .fromTypeIndex(function.typeIndex(wasm), f);
679 try leb.writeUleb128(binary_writer, @intFromEnum(index));
680 }
681
682 replaceVecSectionHeader(binary_bytes, header_offset, .function, @intCast(wasm.functions.count()));
683 section_index += 1;
684 }
685
686 // Table section
687 if (wasm.tables.entries.len > 0) {
688 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
689
690 for (wasm.tables.keys()) |table| {
691 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table.refType(wasm))));
692 try emitLimits(gpa, binary_bytes, table.limits(wasm));
693 }
694
695 replaceVecSectionHeader(binary_bytes, header_offset, .table, @intCast(wasm.tables.entries.len));
696 section_index += 1;
697 }
698
699 // Memory section. wasm currently only supports 1 linear memory segment.
700 if (!import_memory) {
701 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
702 try emitLimits(gpa, binary_bytes, wasm.memories.limits);
703 replaceVecSectionHeader(binary_bytes, header_offset, .memory, 1);
704 section_index += 1;
705 }
706
707 // Global section.
708 const globals_len: u32 = @intCast(wasm.globals.entries.len);
709 if (globals_len > 0) {
710 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
711
712 for (wasm.globals.keys()) |global_resolution| {
713 switch (global_resolution.unpack(wasm)) {
714 .unresolved => unreachable,
715 .__heap_base => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_base),
716 .__heap_end => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_end),
717 .__stack_pointer => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.stack_pointer),
718 .__tls_align => try appendGlobal(gpa, binary_bytes, 0, @intCast(virtual_addrs.tls_align.toByteUnits().?)),
719 .__tls_base => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.tls_base.?),
720 .__tls_size => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.tls_size.?),
721 .object_global => |i| {
722 const global = i.ptr(wasm);
723 try binary_bytes.appendSlice(gpa, &.{
724 @intFromEnum(@as(std.wasm.Valtype, global.flags.global_type.valtype.to())),
725 @intFromBool(global.flags.global_type.mutable),
726 });
727 try emitExpr(wasm, binary_bytes, global.expr);
728 },
729 .nav_exe => unreachable, // Zig source code currently cannot represent this.
730 .nav_obj => unreachable, // Zig source code currently cannot represent this.
731 }
732 }
733
734 replaceVecSectionHeader(binary_bytes, header_offset, .global, globals_len);
735 section_index += 1;
736 }
737
738 // Export section
739 {
740 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
741 var exports_len: usize = 0;
742
743 for (wasm.function_exports.keys(), wasm.function_exports.values()) |exp_name, function_index| {
744 const name = exp_name.slice(wasm);
745 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
746 try binary_bytes.appendSlice(gpa, name);
747 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function));
748 const func_index = Wasm.OutputFunctionIndex.fromFunctionIndex(wasm, function_index);
749 try leb.writeUleb128(binary_writer, @intFromEnum(func_index));
750 }
751 exports_len += wasm.function_exports.entries.len;
752
753 if (wasm.export_table and f.indirect_function_table.entries.len > 0) {
754 const name = "__indirect_function_table";
755 const index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?);
756 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
757 try binary_bytes.appendSlice(gpa, name);
758 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.table));
759 try leb.writeUleb128(binary_writer, index);
760 exports_len += 1;
761 }
762
763 if (export_memory) {
764 const name = "memory";
765 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
766 try binary_bytes.appendSlice(gpa, name);
767 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory));
768 try leb.writeUleb128(binary_writer, @as(u32, 0));
769 exports_len += 1;
770 }
771
772 for (wasm.global_exports.items) |exp| {
773 const name = exp.name.slice(wasm);
774 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
775 try binary_bytes.appendSlice(gpa, name);
776 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.global));
777 try leb.writeUleb128(binary_writer, @intFromEnum(exp.global_index));
778 }
779 exports_len += wasm.global_exports.items.len;
780
781 if (exports_len > 0) {
782 replaceVecSectionHeader(binary_bytes, header_offset, .@"export", @intCast(exports_len));
783 section_index += 1;
784 } else {
785 binary_bytes.shrinkRetainingCapacity(header_offset);
786 }
787 }
788
789 // start section
790 if (wasm.functions.getIndex(.__wasm_init_memory)) |func_index| {
791 try emitStartSection(gpa, binary_bytes, .fromFunctionIndex(wasm, @enumFromInt(func_index)));
792 } else if (Wasm.OutputFunctionIndex.fromResolution(wasm, wasm.entry_resolution)) |func_index| {
793 try emitStartSection(gpa, binary_bytes, func_index);
794 }
795
796 // element section
797 if (f.indirect_function_table.entries.len > 0) {
798 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
799
800 // indirect function table elements
801 const table_index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?);
802 // passive with implicit 0-index table or set table index manually
803 const flags: u32 = if (table_index == 0) 0x0 else 0x02;
804 try leb.writeUleb128(binary_writer, flags);
805 if (flags == 0x02) {
806 try leb.writeUleb128(binary_writer, table_index);
807 }
808 // We start at index 1, so unresolved function pointers are invalid
809 try emitInit(binary_writer, .{ .i32_const = 1 });
810 if (flags == 0x02) {
811 try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref
812 }
813 try leb.writeUleb128(binary_writer, @as(u32, @intCast(f.indirect_function_table.entries.len)));
814 for (f.indirect_function_table.keys()) |func_index| {
815 try leb.writeUleb128(binary_writer, @intFromEnum(func_index));
816 }
817
818 replaceVecSectionHeader(binary_bytes, header_offset, .element, 1);
819 section_index += 1;
820 }
821
822 // When the shared-memory option is enabled, we *must* emit the 'data count' section.
823 if (f.data_segment_groups.items.len > 0) {
824 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
825 replaceVecSectionHeader(binary_bytes, header_offset, .data_count, @intCast(f.data_segment_groups.items.len));
826 }
827
828 // Code section.
829 if (wasm.functions.count() != 0) {
830 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
831
832 for (wasm.functions.keys()) |resolution| switch (resolution.unpack(wasm)) {
833 .unresolved => unreachable,
834 .__wasm_apply_global_tls_relocs => @panic("TODO lower __wasm_apply_global_tls_relocs"),
835 .__wasm_call_ctors => {
836 const code_start = try reserveSize(gpa, binary_bytes);
837 defer replaceSize(binary_bytes, code_start);
838 try emitCallCtorsFunction(wasm, binary_bytes);
839 },
840 .__wasm_init_memory => {
841 const code_start = try reserveSize(gpa, binary_bytes);
842 defer replaceSize(binary_bytes, code_start);
843 try emitInitMemoryFunction(wasm, binary_bytes, &virtual_addrs);
844 },
845 .__wasm_init_tls => {
846 const code_start = try reserveSize(gpa, binary_bytes);
847 defer replaceSize(binary_bytes, code_start);
848 try emitInitTlsFunction(wasm, binary_bytes);
849 },
850 .object_function => |i| {
851 const ptr = i.ptr(wasm);
852 const code = ptr.code.slice(wasm);
853 try leb.writeUleb128(binary_writer, code.len);
854 const code_start = binary_bytes.items.len;
855 try binary_bytes.appendSlice(gpa, code);
856 if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm);
857 },
858 .zcu_func => |i| {
859 const code_start = try reserveSize(gpa, binary_bytes);
860 defer replaceSize(binary_bytes, code_start);
861
862 log.debug("lowering function code for '{s}'", .{resolution.name(wasm).?});
863
864 const zcu = comp.zcu.?;
865 const ip = &zcu.intern_pool;
866 const ip_index = i.key(wasm).*;
867 switch (ip.indexToKey(ip_index)) {
868 .enum_type => {
869 try emitTagNameFunction(wasm, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index);
870 },
871 else => try i.value(wasm).function.lower(wasm, binary_bytes),
872 }
873 },
874 };
875
876 replaceVecSectionHeader(binary_bytes, header_offset, .code, @intCast(wasm.functions.entries.len));
877 code_section_index = section_index;
878 section_index += 1;
879 }
880
881 if (!is_obj) {
882 for (wasm.uav_fixups.items) |uav_fixup| {
883 const ds_id: Wasm.DataSegmentId = .pack(wasm, .{ .uav_exe = uav_fixup.uavs_exe_index });
884 const vaddr = f.data_segments.get(ds_id).? + uav_fixup.addend;
885 if (!is64) {
886 mem.writeInt(u32, wasm.string_bytes.items[uav_fixup.offset..][0..4], vaddr, .little);
887 } else {
888 mem.writeInt(u64, wasm.string_bytes.items[uav_fixup.offset..][0..8], vaddr, .little);
889 }
890 }
891 for (wasm.nav_fixups.items) |nav_fixup| {
892 const ds_id: Wasm.DataSegmentId = .pack(wasm, .{ .nav_exe = nav_fixup.navs_exe_index });
893 const vaddr = f.data_segments.get(ds_id).? + nav_fixup.addend;
894 if (!is64) {
895 mem.writeInt(u32, wasm.string_bytes.items[nav_fixup.offset..][0..4], vaddr, .little);
896 } else {
897 mem.writeInt(u64, wasm.string_bytes.items[nav_fixup.offset..][0..8], vaddr, .little);
898 }
899 }
900 for (wasm.func_table_fixups.items) |fixup| {
901 const table_index: IndirectFunctionTableIndex = .fromZcuIndirectFunctionSetIndex(fixup.table_index);
902 if (!is64) {
903 mem.writeInt(u32, wasm.string_bytes.items[fixup.offset..][0..4], table_index.toAbi(), .little);
904 } else {
905 mem.writeInt(u64, wasm.string_bytes.items[fixup.offset..][0..8], table_index.toAbi(), .little);
906 }
907 }
908 }
909
910 // Data section.
911 if (f.data_segment_groups.items.len != 0) {
912 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
913
914 var group_index: u32 = 0;
915 var segment_offset: u32 = 0;
916 var group_start_addr: u32 = data_vaddr;
917 var group_end_addr = f.data_segment_groups.items[group_index].end_addr;
918 for (segment_ids, segment_vaddrs) |segment_id, segment_vaddr| {
919 if (segment_vaddr >= group_end_addr) {
920 try binary_bytes.appendNTimes(gpa, 0, group_end_addr - group_start_addr - segment_offset);
921 group_index += 1;
922 if (group_index >= f.data_segment_groups.items.len) {
923 // All remaining segments are zero.
924 break;
925 }
926 group_start_addr = group_end_addr;
927 group_end_addr = f.data_segment_groups.items[group_index].end_addr;
928 segment_offset = 0;
929 }
930 if (segment_offset == 0) {
931 const group_size = group_end_addr - group_start_addr;
932 log.debug("emit data section group, {d} bytes", .{group_size});
933 const flags: Object.DataSegmentFlags = if (segment_id.isPassive(wasm)) .passive else .active;
934 try leb.writeUleb128(binary_writer, @intFromEnum(flags));
935 // Passive segments are initialized at runtime.
936 if (flags != .passive) {
937 try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(group_start_addr)) });
938 }
939 try leb.writeUleb128(binary_writer, group_size);
940 }
941 if (segment_id.isEmpty(wasm)) {
942 // It counted for virtual memory but it does not go into the binary.
943 continue;
944 }
945
946 // Padding for alignment.
947 const needed_offset = segment_vaddr - group_start_addr;
948 try binary_bytes.appendNTimes(gpa, 0, needed_offset - segment_offset);
949 segment_offset = needed_offset;
950
951 const code_start = binary_bytes.items.len;
952 append: {
953 const code = switch (segment_id.unpack(wasm)) {
954 .__heap_base => {
955 mem.writeInt(u32, try binary_bytes.addManyAsArray(gpa, 4), virtual_addrs.heap_base, .little);
956 break :append;
957 },
958 .__heap_end => {
959 mem.writeInt(u32, try binary_bytes.addManyAsArray(gpa, 4), virtual_addrs.heap_end, .little);
960 break :append;
961 },
962 .__zig_error_names => {
963 try binary_bytes.appendSlice(gpa, wasm.error_name_bytes.items);
964 break :append;
965 },
966 .__zig_error_name_table => {
967 if (is_obj) @panic("TODO error name table reloc");
968 const base = f.data_segments.get(.__zig_error_names).?;
969 if (!is64) {
970 try emitTagNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u32);
971 } else {
972 try emitTagNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u64);
973 }
974 break :append;
975 },
976 .__zig_tag_names => {
977 try binary_bytes.appendSlice(gpa, wasm.tag_name_bytes.items);
978 break :append;
979 },
980 .__zig_tag_name_table => {
981 if (is_obj) @panic("TODO tag name table reloc");
982 const base = f.data_segments.get(.__zig_tag_names).?;
983 if (!is64) {
984 try emitTagNameTable(gpa, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u32);
985 } else {
986 try emitTagNameTable(gpa, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u64);
987 }
988 break :append;
989 },
990 .object => |i| {
991 const ptr = i.ptr(wasm);
992 try binary_bytes.appendSlice(gpa, ptr.payload.slice(wasm));
993 if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm);
994 break :append;
995 },
996 inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code,
997 };
998 try binary_bytes.appendSlice(gpa, code.slice(wasm));
999 }
1000 segment_offset += @intCast(binary_bytes.items.len - code_start);
1001 }
1002
1003 replaceVecSectionHeader(binary_bytes, header_offset, .data, @intCast(f.data_segment_groups.items.len));
1004 data_section_index = section_index;
1005 section_index += 1;
1006 }
1007
1008 if (is_obj) {
1009 @panic("TODO emit link section for object file and emit modified relocations");
1010 } else if (comp.config.debug_format != .strip) {
1011 try emitNameSection(wasm, f.data_segment_groups.items, binary_bytes);
1012 }
1013
1014 if (comp.config.debug_format != .strip) {
1015 // The build id must be computed on the main sections only,
1016 // so we have to do it now, before the debug sections.
1017 switch (wasm.base.build_id) {
1018 .none => {},
1019 .fast => {
1020 var id: [16]u8 = undefined;
1021 std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{});
1022 var uuid: [36]u8 = undefined;
1023 _ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{
1024 std.fmt.fmtSliceHexLower(id[0..4]),
1025 std.fmt.fmtSliceHexLower(id[4..6]),
1026 std.fmt.fmtSliceHexLower(id[6..8]),
1027 std.fmt.fmtSliceHexLower(id[8..10]),
1028 std.fmt.fmtSliceHexLower(id[10..]),
1029 });
1030 try emitBuildIdSection(gpa, binary_bytes, &uuid);
1031 },
1032 .hexstring => |hs| {
1033 var buffer: [32 * 2]u8 = undefined;
1034 const str = std.fmt.bufPrint(&buffer, "{s}", .{
1035 std.fmt.fmtSliceHexLower(hs.toSlice()),
1036 }) catch unreachable;
1037 try emitBuildIdSection(gpa, binary_bytes, str);
1038 },
1039 else => |mode| {
1040 var err = try diags.addErrorWithNotes(0);
1041 try err.addMsg("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)});
1042 },
1043 }
1044
1045 var debug_bytes = std.ArrayList(u8).init(gpa);
1046 defer debug_bytes.deinit();
1047
1048 try emitProducerSection(gpa, binary_bytes);
1049 try emitFeaturesSection(gpa, binary_bytes, target);
1050 }
1051
1052 // Finally, write the entire binary into the file.
1053 const file = wasm.base.file.?;
1054 try file.pwriteAll(binary_bytes.items, 0);
1055 try file.setEndPos(binary_bytes.items.len);
1056}
1057
1058const VirtualAddrs = struct {
1059 stack_pointer: u32,
1060 heap_base: u32,
1061 heap_end: u32,
1062 tls_base: ?u32,
1063 tls_align: Alignment,
1064 tls_size: ?u32,
1065 init_memory_flag: ?u32,
1066};
1067
1068fn emitNameSection(
1069 wasm: *Wasm,
1070 data_segment_groups: []const DataSegmentGroup,
1071 binary_bytes: *std.ArrayListUnmanaged(u8),
1072) !void {
1073 const f = &wasm.flush_buffer;
1074 const comp = wasm.base.comp;
1075 const gpa = comp.gpa;
1076
1077 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
1078 defer writeCustomSectionHeader(binary_bytes, header_offset);
1079
1080 const name_name = "name";
1081 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, name_name.len));
1082 try binary_bytes.appendSlice(gpa, name_name);
1083
1084 {
1085 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
1086 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.function));
1087
1088 const total_functions: u32 = @intCast(f.function_imports.entries.len + wasm.functions.entries.len);
1089 try leb.writeUleb128(binary_bytes.writer(gpa), total_functions);
1090
1091 for (f.function_imports.keys(), 0..) |name_index, function_index| {
1092 const name = name_index.slice(wasm);
1093 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index)));
1094 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
1095 try binary_bytes.appendSlice(gpa, name);
1096 }
1097 for (wasm.functions.keys(), f.function_imports.entries.len..) |resolution, function_index| {
1098 const name = resolution.name(wasm).?;
1099 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index)));
1100 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
1101 try binary_bytes.appendSlice(gpa, name);
1102 }
1103 }
1104
1105 {
1106 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
1107 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.global));
1108
1109 const total_globals: u32 = @intCast(f.global_imports.entries.len + wasm.globals.entries.len);
1110 try leb.writeUleb128(binary_bytes.writer(gpa), total_globals);
1111
1112 for (f.global_imports.keys(), 0..) |name_index, global_index| {
1113 const name = name_index.slice(wasm);
1114 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index)));
1115 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
1116 try binary_bytes.appendSlice(gpa, name);
1117 }
1118 for (wasm.globals.keys(), f.global_imports.entries.len..) |resolution, global_index| {
1119 const name = resolution.name(wasm).?;
1120 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index)));
1121 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
1122 try binary_bytes.appendSlice(gpa, name);
1123 }
1124 }
1125
1126 {
1127 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
1128 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.data_segment));
1129
1130 const total_data_segments: u32 = @intCast(data_segment_groups.len);
1131 try leb.writeUleb128(binary_bytes.writer(gpa), total_data_segments);
1132
1133 for (data_segment_groups, 0..) |group, i| {
1134 const name, _ = splitSegmentName(group.first_segment.name(wasm));
1135 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(i)));
1136 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
1137 try binary_bytes.appendSlice(gpa, name);
1138 }
1139 }
1140}
1141
1142fn emitFeaturesSection(
1143 gpa: Allocator,
1144 binary_bytes: *std.ArrayListUnmanaged(u8),
1145 target: *const std.Target,
1146) Allocator.Error!void {
1147 const feature_count = target.cpu.features.count();
1148 if (feature_count == 0) return;
1149
1150 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
1151 defer writeCustomSectionHeader(binary_bytes, header_offset);
1152
1153 const writer = binary_bytes.writer(gpa);
1154 const target_features = "target_features";
1155 try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len)));
1156 try writer.writeAll(target_features);
1157
1158 try leb.writeUleb128(writer, @as(u32, @intCast(feature_count)));
1159
1160 var safety_count = feature_count;
1161 for (target.cpu.arch.allFeaturesList(), 0..) |*feature, i| {
1162 if (!std.Target.wasm.featureSetHas(target.cpu.features, @enumFromInt(i))) continue;
1163 safety_count -= 1;
1164
1165 try leb.writeUleb128(writer, @as(u32, '+'));
1166 // Depends on llvm_name for the hyphenated version that matches wasm tooling conventions.
1167 const name = feature.llvm_name.?;
1168 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
1169 try writer.writeAll(name);
1170 }
1171 assert(safety_count == 0);
1172}
1173
1174fn emitBuildIdSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8), build_id: []const u8) !void {
1175 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
1176 defer writeCustomSectionHeader(binary_bytes, header_offset);
1177
1178 const writer = binary_bytes.writer(gpa);
1179 const hdr_build_id = "build_id";
1180 try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len)));
1181 try writer.writeAll(hdr_build_id);
1182
1183 try leb.writeUleb128(writer, @as(u32, 1));
1184 try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len)));
1185 try writer.writeAll(build_id);
1186}
1187
1188fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)) !void {
1189 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
1190 defer writeCustomSectionHeader(binary_bytes, header_offset);
1191
1192 const writer = binary_bytes.writer(gpa);
1193 const producers = "producers";
1194 try leb.writeUleb128(writer, @as(u32, @intCast(producers.len)));
1195 try writer.writeAll(producers);
1196
1197 try leb.writeUleb128(writer, @as(u32, 2)); // 2 fields: Language + processed-by
1198
1199 // language field
1200 {
1201 const language = "language";
1202 try leb.writeUleb128(writer, @as(u32, @intCast(language.len)));
1203 try writer.writeAll(language);
1204
1205 // field_value_count (TODO: Parse object files for producer sections to detect their language)
1206 try leb.writeUleb128(writer, @as(u32, 1));
1207
1208 // versioned name
1209 {
1210 try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig"
1211 try writer.writeAll("Zig");
1212
1213 try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len)));
1214 try writer.writeAll(build_options.version);
1215 }
1216 }
1217
1218 // processed-by field
1219 {
1220 const processed_by = "processed-by";
1221 try leb.writeUleb128(writer, @as(u32, @intCast(processed_by.len)));
1222 try writer.writeAll(processed_by);
1223
1224 // field_value_count (TODO: Parse object files for producer sections to detect other used tools)
1225 try leb.writeUleb128(writer, @as(u32, 1));
1226
1227 // versioned name
1228 {
1229 try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig"
1230 try writer.writeAll("Zig");
1231
1232 try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len)));
1233 try writer.writeAll(build_options.version);
1234 }
1235 }
1236}
1237
1238fn splitSegmentName(name: []const u8) struct { []const u8, []const u8 } {
1239 const start = @intFromBool(name.len >= 1 and name[0] == '.');
1240 const pivot = mem.indexOfScalarPos(u8, name, start, '.') orelse name.len;
1241 return .{ name[0..pivot], name[pivot..] };
1242}
1243
1244test splitSegmentName {
1245 {
1246 const a, const b = splitSegmentName(".data");
1247 try std.testing.expectEqualStrings(".data", a);
1248 try std.testing.expectEqualStrings("", b);
1249 }
1250}
1251
1252fn wantSegmentMerge(
1253 wasm: *const Wasm,
1254 a_id: Wasm.DataSegmentId,
1255 b_id: Wasm.DataSegmentId,
1256 b_category: Wasm.DataSegmentId.Category,
1257) bool {
1258 const a_category = a_id.category(wasm);
1259 if (a_category != b_category) return false;
1260 if (a_category == .tls or b_category == .tls) return false;
1261 if (a_id.isPassive(wasm) != b_id.isPassive(wasm)) return false;
1262 if (b_category == .zero) return true;
1263 const a_name = a_id.name(wasm);
1264 const b_name = b_id.name(wasm);
1265 const a_prefix, _ = splitSegmentName(a_name);
1266 const b_prefix, _ = splitSegmentName(b_name);
1267 return mem.eql(u8, a_prefix, b_prefix);
1268}
1269
1270/// section id + fixed leb contents size + fixed leb vector length
1271const section_header_reserve_size = 1 + 5 + 5;
1272const section_header_size = 5 + 1;
1273
1274fn reserveVecSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {
1275 try bytes.appendNTimes(gpa, 0, section_header_reserve_size);
1276 return @intCast(bytes.items.len - section_header_reserve_size);
1277}
1278
1279fn replaceVecSectionHeader(
1280 bytes: *std.ArrayListUnmanaged(u8),
1281 offset: u32,
1282 section: std.wasm.Section,
1283 n_items: u32,
1284) void {
1285 const size: u32 = @intCast(bytes.items.len - offset - section_header_reserve_size + uleb128size(n_items));
1286 var buf: [section_header_reserve_size]u8 = undefined;
1287 var fbw = std.io.fixedBufferStream(&buf);
1288 const w = fbw.writer();
1289 w.writeByte(@intFromEnum(section)) catch unreachable;
1290 leb.writeUleb128(w, size) catch unreachable;
1291 leb.writeUleb128(w, n_items) catch unreachable;
1292 bytes.replaceRangeAssumeCapacity(offset, section_header_reserve_size, fbw.getWritten());
1293}
1294
1295fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {
1296 try bytes.appendNTimes(gpa, 0, section_header_size);
1297 return @intCast(bytes.items.len - section_header_size);
1298}
1299
1300fn writeCustomSectionHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void {
1301 return replaceHeader(bytes, offset, 0); // 0 = 'custom' section
1302}
1303
1304fn replaceHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32, tag: u8) void {
1305 const size: u32 = @intCast(bytes.items.len - offset - section_header_size);
1306 var buf: [section_header_size]u8 = undefined;
1307 var fbw = std.io.fixedBufferStream(&buf);
1308 const w = fbw.writer();
1309 w.writeByte(tag) catch unreachable;
1310 leb.writeUleb128(w, size) catch unreachable;
1311 bytes.replaceRangeAssumeCapacity(offset, section_header_size, fbw.getWritten());
1312}
1313
1314const max_size_encoding = 5;
1315
1316fn reserveSize(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {
1317 try bytes.appendNTimes(gpa, 0, max_size_encoding);
1318 return @intCast(bytes.items.len - max_size_encoding);
1319}
1320
1321fn replaceSize(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void {
1322 const size: u32 = @intCast(bytes.items.len - offset - max_size_encoding);
1323 var buf: [max_size_encoding]u8 = undefined;
1324 var fbw = std.io.fixedBufferStream(&buf);
1325 leb.writeUleb128(fbw.writer(), size) catch unreachable;
1326 bytes.replaceRangeAssumeCapacity(offset, max_size_encoding, fbw.getWritten());
1327}
1328
1329fn emitLimits(
1330 gpa: Allocator,
1331 binary_bytes: *std.ArrayListUnmanaged(u8),
1332 limits: std.wasm.Limits,
1333) Allocator.Error!void {
1334 try binary_bytes.append(gpa, @bitCast(limits.flags));
1335 try leb.writeUleb128(binary_bytes.writer(gpa), limits.min);
1336 if (limits.flags.has_max) try leb.writeUleb128(binary_bytes.writer(gpa), limits.max);
1337}
1338
1339fn emitMemoryImport(
1340 wasm: *Wasm,
1341 binary_bytes: *std.ArrayListUnmanaged(u8),
1342 name_index: String,
1343 memory_import: *const Wasm.MemoryImport,
1344) Allocator.Error!void {
1345 const gpa = wasm.base.comp.gpa;
1346 const module_name = memory_import.module_name.slice(wasm);
1347 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(module_name.len)));
1348 try binary_bytes.appendSlice(gpa, module_name);
1349
1350 const name = name_index.slice(wasm);
1351 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
1352 try binary_bytes.appendSlice(gpa, name);
1353
1354 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory));
1355 try emitLimits(gpa, binary_bytes, memory_import.limits());
1356}
1357
1358pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {
1359 switch (init_expr) {
1360 .i32_const => |val| {
1361 try writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1362 try leb.writeIleb128(writer, val);
1363 },
1364 .i64_const => |val| {
1365 try writer.writeByte(@intFromEnum(std.wasm.Opcode.i64_const));
1366 try leb.writeIleb128(writer, val);
1367 },
1368 .f32_const => |val| {
1369 try writer.writeByte(@intFromEnum(std.wasm.Opcode.f32_const));
1370 try writer.writeInt(u32, @bitCast(val), .little);
1371 },
1372 .f64_const => |val| {
1373 try writer.writeByte(@intFromEnum(std.wasm.Opcode.f64_const));
1374 try writer.writeInt(u64, @bitCast(val), .little);
1375 },
1376 .global_get => |val| {
1377 try writer.writeByte(@intFromEnum(std.wasm.Opcode.global_get));
1378 try leb.writeUleb128(writer, val);
1379 },
1380 }
1381 try writer.writeByte(@intFromEnum(std.wasm.Opcode.end));
1382}
1383
1384pub fn emitExpr(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), expr: Wasm.Expr) Allocator.Error!void {
1385 const gpa = wasm.base.comp.gpa;
1386 const slice = expr.slice(wasm);
1387 try binary_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]); // +1 to include end opcode
1388}
1389
1390fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
1391 const gpa = wasm.base.comp.gpa;
1392 const writer = binary_bytes.writer(gpa);
1393 try leb.writeUleb128(writer, @intFromEnum(Wasm.SubsectionType.segment_info));
1394 const segment_offset = binary_bytes.items.len;
1395
1396 try leb.writeUleb128(writer, @as(u32, @intCast(wasm.segment_info.count())));
1397 for (wasm.segment_info.values()) |segment_info| {
1398 log.debug("Emit segment: {s} align({d}) flags({b})", .{
1399 segment_info.name,
1400 segment_info.alignment,
1401 segment_info.flags,
1402 });
1403 try leb.writeUleb128(writer, @as(u32, @intCast(segment_info.name.len)));
1404 try writer.writeAll(segment_info.name);
1405 try leb.writeUleb128(writer, segment_info.alignment.toLog2Units());
1406 try leb.writeUleb128(writer, segment_info.flags);
1407 }
1408
1409 var buf: [5]u8 = undefined;
1410 leb.writeUnsignedFixed(5, &buf, @as(u32, @intCast(binary_bytes.items.len - segment_offset)));
1411 try binary_bytes.insertSlice(segment_offset, &buf);
1412}
1413
1414fn uleb128size(x: u32) u32 {
1415 var value = x;
1416 var size: u32 = 0;
1417 while (value != 0) : (size += 1) value >>= 7;
1418 return size;
1419}
1420
1421fn emitTagNameTable(
1422 gpa: Allocator,
1423 code: *std.ArrayListUnmanaged(u8),
1424 tag_name_offs: []const u32,
1425 tag_name_bytes: []const u8,
1426 base: u32,
1427 comptime Int: type,
1428) error{OutOfMemory}!void {
1429 const ptr_size_bytes = @divExact(@bitSizeOf(Int), 8);
1430 try code.ensureUnusedCapacity(gpa, ptr_size_bytes * 2 * tag_name_offs.len);
1431 for (tag_name_offs) |off| {
1432 const name_len: u32 = @intCast(mem.indexOfScalar(u8, tag_name_bytes[off..], 0).?);
1433 mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), base + off, .little);
1434 mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), name_len, .little);
1435 }
1436}
1437
1438fn applyRelocs(code: []u8, code_offset: u32, relocs: Wasm.ObjectRelocation.IterableSlice, wasm: *const Wasm) void {
1439 for (
1440 relocs.slice.tags(wasm),
1441 relocs.slice.pointees(wasm),
1442 relocs.slice.offsets(wasm),
1443 relocs.slice.addends(wasm),
1444 ) |tag, pointee, offset, *addend| {
1445 if (offset >= relocs.end) break;
1446 const sliced_code = code[offset - code_offset ..];
1447 switch (tag) {
1448 .function_index_i32 => reloc_u32_function(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
1449 .function_index_leb => reloc_leb_function(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
1450 .function_offset_i32 => @panic("TODO this value is not known yet"),
1451 .function_offset_i64 => @panic("TODO this value is not known yet"),
1452 .table_index_i32 => reloc_u32_table_index(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
1453 .table_index_i64 => reloc_u64_table_index(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
1454 .table_index_rel_sleb => @panic("TODO what does this reloc tag mean?"),
1455 .table_index_rel_sleb64 => @panic("TODO what does this reloc tag mean?"),
1456 .table_index_sleb => reloc_sleb_table_index(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
1457 .table_index_sleb64 => reloc_sleb64_table_index(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
1458
1459 .function_import_index_i32 => reloc_u32_function(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1460 .function_import_index_leb => reloc_leb_function(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1461 .function_import_offset_i32 => @panic("TODO this value is not known yet"),
1462 .function_import_offset_i64 => @panic("TODO this value is not known yet"),
1463 .table_import_index_i32 => reloc_u32_table_index(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1464 .table_import_index_i64 => reloc_u64_table_index(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1465 .table_import_index_rel_sleb => @panic("TODO what does this reloc tag mean?"),
1466 .table_import_index_rel_sleb64 => @panic("TODO what does this reloc tag mean?"),
1467 .table_import_index_sleb => reloc_sleb_table_index(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1468 .table_import_index_sleb64 => reloc_sleb64_table_index(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1469
1470 .global_index_i32 => reloc_u32_global(sliced_code, .fromObjectGlobalHandlingWeak(wasm, pointee.global)),
1471 .global_index_leb => reloc_leb_global(sliced_code, .fromObjectGlobalHandlingWeak(wasm, pointee.global)),
1472
1473 .global_import_index_i32 => reloc_u32_global(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1474 .global_import_index_leb => reloc_leb_global(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1475
1476 .memory_addr_i32 => reloc_u32_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
1477 .memory_addr_i64 => reloc_u64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
1478 .memory_addr_leb => reloc_leb_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
1479 .memory_addr_leb64 => reloc_leb64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
1480 .memory_addr_locrel_i32 => @panic("TODO implement relocation memory_addr_locrel_i32"),
1481 .memory_addr_rel_sleb => @panic("TODO implement relocation memory_addr_rel_sleb"),
1482 .memory_addr_rel_sleb64 => @panic("TODO implement relocation memory_addr_rel_sleb64"),
1483 .memory_addr_sleb => reloc_sleb_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
1484 .memory_addr_sleb64 => reloc_sleb64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
1485 .memory_addr_tls_sleb => reloc_sleb_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
1486 .memory_addr_tls_sleb64 => reloc_sleb64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
1487
1488 .memory_addr_import_i32 => reloc_u32_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
1489 .memory_addr_import_i64 => reloc_u64_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
1490 .memory_addr_import_leb => reloc_leb_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
1491 .memory_addr_import_leb64 => reloc_leb64_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
1492 .memory_addr_import_locrel_i32 => @panic("TODO implement relocation memory_addr_import_locrel_i32"),
1493 .memory_addr_import_rel_sleb => @panic("TODO implement relocation memory_addr_import_rel_sleb"),
1494 .memory_addr_import_rel_sleb64 => @panic("TODO implement memory_addr_import_rel_sleb64"),
1495 .memory_addr_import_sleb => reloc_sleb_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
1496 .memory_addr_import_sleb64 => reloc_sleb64_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
1497 .memory_addr_import_tls_sleb => @panic("TODO"),
1498 .memory_addr_import_tls_sleb64 => @panic("TODO"),
1499
1500 .section_offset_i32 => @panic("TODO this value is not known yet"),
1501
1502 .table_number_leb => reloc_leb_table(sliced_code, .fromObjectTable(wasm, pointee.table)),
1503 .table_import_number_leb => reloc_leb_table(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1504
1505 .type_index_leb => reloc_leb_type(sliced_code, .fromTypeIndex(pointee.type_index, &wasm.flush_buffer)),
1506 }
1507 }
1508}
1509
1510fn reloc_u32_table_index(code: []u8, i: IndirectFunctionTableIndex) void {
1511 mem.writeInt(u32, code[0..4], i.toAbi(), .little);
1512}
1513
1514fn reloc_u64_table_index(code: []u8, i: IndirectFunctionTableIndex) void {
1515 mem.writeInt(u64, code[0..8], i.toAbi(), .little);
1516}
1517
1518fn reloc_sleb_table_index(code: []u8, i: IndirectFunctionTableIndex) void {
1519 leb.writeSignedFixed(5, code[0..5], i.toAbi());
1520}
1521
1522fn reloc_sleb64_table_index(code: []u8, i: IndirectFunctionTableIndex) void {
1523 leb.writeSignedFixed(11, code[0..11], i.toAbi());
1524}
1525
1526fn reloc_u32_function(code: []u8, function: Wasm.OutputFunctionIndex) void {
1527 mem.writeInt(u32, code[0..4], @intFromEnum(function), .little);
1528}
1529
1530fn reloc_leb_function(code: []u8, function: Wasm.OutputFunctionIndex) void {
1531 leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(function));
1532}
1533
1534fn reloc_u32_global(code: []u8, global: Wasm.GlobalIndex) void {
1535 mem.writeInt(u32, code[0..4], @intFromEnum(global), .little);
1536}
1537
1538fn reloc_leb_global(code: []u8, global: Wasm.GlobalIndex) void {
1539 leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(global));
1540}
1541
1542const RelocAddr = struct {
1543 addr: u32,
1544
1545 fn fromObjectData(wasm: *const Wasm, i: Wasm.ObjectData.Index, addend: i32) RelocAddr {
1546 return fromDataLoc(&wasm.flush_buffer, .fromObjectDataIndex(wasm, i), addend);
1547 }
1548
1549 fn fromSymbolName(wasm: *const Wasm, name: String, addend: i32) RelocAddr {
1550 const flush = &wasm.flush_buffer;
1551 if (wasm.object_data_imports.getPtr(name)) |import| {
1552 return fromDataLoc(flush, import.resolution.dataLoc(wasm), addend);
1553 } else if (wasm.data_imports.get(name)) |id| {
1554 return fromDataLoc(flush, .fromDataImportId(wasm, id), addend);
1555 } else {
1556 unreachable;
1557 }
1558 }
1559
1560 fn fromDataLoc(flush: *const Flush, data_loc: Wasm.DataLoc, addend: i32) RelocAddr {
1561 const base_addr: i64 = flush.data_segments.get(data_loc.segment).?;
1562 return .{ .addr = @intCast(base_addr + data_loc.offset + addend) };
1563 }
1564};
1565
1566fn reloc_u32_addr(code: []u8, ra: RelocAddr) void {
1567 mem.writeInt(u32, code[0..4], ra.addr, .little);
1568}
1569
1570fn reloc_u64_addr(code: []u8, ra: RelocAddr) void {
1571 mem.writeInt(u64, code[0..8], ra.addr, .little);
1572}
1573
1574fn reloc_leb_addr(code: []u8, ra: RelocAddr) void {
1575 leb.writeUnsignedFixed(5, code[0..5], ra.addr);
1576}
1577
1578fn reloc_leb64_addr(code: []u8, ra: RelocAddr) void {
1579 leb.writeUnsignedFixed(11, code[0..11], ra.addr);
1580}
1581
1582fn reloc_sleb_addr(code: []u8, ra: RelocAddr) void {
1583 leb.writeSignedFixed(5, code[0..5], ra.addr);
1584}
1585
1586fn reloc_sleb64_addr(code: []u8, ra: RelocAddr) void {
1587 leb.writeSignedFixed(11, code[0..11], ra.addr);
1588}
1589
1590fn reloc_leb_table(code: []u8, table: Wasm.TableIndex) void {
1591 leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(table));
1592}
1593
1594fn reloc_leb_type(code: []u8, index: FuncTypeIndex) void {
1595 leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(index));
1596}
1597
1598fn emitCallCtorsFunction(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!void {
1599 const gpa = wasm.base.comp.gpa;
1600
1601 try binary_bytes.ensureUnusedCapacity(gpa, 5 + 1);
1602 appendReservedUleb32(binary_bytes, 0); // no locals
1603
1604 for (wasm.object_init_funcs.items) |init_func| {
1605 const func = init_func.function_index.ptr(wasm);
1606 if (!func.object_index.ptr(wasm).is_included) continue;
1607 const ty = func.type_index.ptr(wasm);
1608 const n_returns = ty.returns.slice(wasm).len;
1609
1610 // Call function by its function index
1611 try binary_bytes.ensureUnusedCapacity(gpa, 1 + 5 + n_returns + 1);
1612 const call_index: Wasm.OutputFunctionIndex = .fromObjectFunction(wasm, init_func.function_index);
1613 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
1614 appendReservedUleb32(binary_bytes, @intFromEnum(call_index));
1615
1616 // drop all returned values from the stack as __wasm_call_ctors has no return value
1617 binary_bytes.appendNTimesAssumeCapacity(@intFromEnum(std.wasm.Opcode.drop), n_returns);
1618 }
1619
1620 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); // end function body
1621}
1622
1623fn emitInitMemoryFunction(
1624 wasm: *const Wasm,
1625 binary_bytes: *std.ArrayListUnmanaged(u8),
1626 virtual_addrs: *const VirtualAddrs,
1627) Allocator.Error!void {
1628 const comp = wasm.base.comp;
1629 const gpa = comp.gpa;
1630 const shared_memory = comp.config.shared_memory;
1631
1632 // Passive segments are used to avoid memory being reinitialized on each
1633 // thread's instantiation. These passive segments are initialized and
1634 // dropped in __wasm_init_memory, which is registered as the start function
1635 // We also initialize bss segments (using memory.fill) as part of this
1636 // function.
1637 assert(wasm.any_passive_inits);
1638
1639 try binary_bytes.ensureUnusedCapacity(gpa, 5 + 1);
1640 appendReservedUleb32(binary_bytes, 0); // no locals
1641
1642 if (virtual_addrs.init_memory_flag) |flag_address| {
1643 assert(shared_memory);
1644 try binary_bytes.ensureUnusedCapacity(gpa, 2 * 3 + 6 * 3 + 1 + 6 * 3 + 1 + 5 * 4 + 1 + 1);
1645 // destination blocks
1646 // based on values we jump to corresponding label
1647 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $drop
1648 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty));
1649
1650 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $wait
1651 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty));
1652
1653 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $init
1654 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty));
1655
1656 // atomically check
1657 appendReservedI32Const(binary_bytes, flag_address);
1658 appendReservedI32Const(binary_bytes, 0);
1659 appendReservedI32Const(binary_bytes, 1);
1660 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));
1661 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.i32_atomic_rmw_cmpxchg));
1662 appendReservedUleb32(binary_bytes, 2); // alignment
1663 appendReservedUleb32(binary_bytes, 0); // offset
1664
1665 // based on the value from the atomic check, jump to the label.
1666 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_table));
1667 appendReservedUleb32(binary_bytes, 2); // length of the table (we have 3 blocks but because of the mandatory default the length is 2).
1668 appendReservedUleb32(binary_bytes, 0); // $init
1669 appendReservedUleb32(binary_bytes, 1); // $wait
1670 appendReservedUleb32(binary_bytes, 2); // $drop
1671 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1672 }
1673
1674 const segment_groups = wasm.flush_buffer.data_segment_groups.items;
1675 var prev_end: u32 = 0;
1676 for (segment_groups, 0..) |group, segment_index| {
1677 defer prev_end = group.end_addr;
1678 const segment = group.first_segment;
1679 if (!segment.isPassive(wasm)) continue;
1680
1681 const start_addr: u32 = @intCast(segment.alignment(wasm).forward(prev_end));
1682 const segment_size: u32 = group.end_addr - start_addr;
1683
1684 try binary_bytes.ensureUnusedCapacity(gpa, 6 + 6 + 1 + 5 + 6 + 6 + 1 + 6 * 2 + 1 + 1);
1685
1686 // For passive BSS segments we can simply issue a memory.fill(0). For
1687 // non-BSS segments we do a memory.init. Both instructions take as
1688 // their first argument the destination address.
1689 appendReservedI32Const(binary_bytes, start_addr);
1690
1691 if (shared_memory and segment.isTls(wasm)) {
1692 // When we initialize the TLS segment we also set the `__tls_base`
1693 // global. This allows the runtime to use this static copy of the
1694 // TLS data for the first/main thread.
1695 appendReservedI32Const(binary_bytes, start_addr);
1696 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
1697 appendReservedUleb32(binary_bytes, virtual_addrs.tls_base.?);
1698 }
1699
1700 appendReservedI32Const(binary_bytes, 0);
1701 appendReservedI32Const(binary_bytes, segment_size);
1702 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix));
1703 if (segment.isBss(wasm)) {
1704 // fill bss segment with zeroes
1705 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.MiscOpcode.memory_fill));
1706 } else {
1707 // initialize the segment
1708 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.MiscOpcode.memory_init));
1709 appendReservedUleb32(binary_bytes, @intCast(segment_index));
1710 }
1711 binary_bytes.appendAssumeCapacity(0); // memory index immediate
1712 }
1713
1714 if (virtual_addrs.init_memory_flag) |flag_address| {
1715 assert(shared_memory);
1716 try binary_bytes.ensureUnusedCapacity(gpa, 6 + 6 + 1 + 3 * 5 + 6 + 1 + 5 + 1 + 3 * 5 + 1 + 1 + 5 + 1 + 6 * 2 + 1 + 5 + 1 + 3 * 5 + 1 + 1 + 1);
1717 // we set the init memory flag to value '2'
1718 appendReservedI32Const(binary_bytes, flag_address);
1719 appendReservedI32Const(binary_bytes, 2);
1720 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));
1721 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.i32_atomic_store));
1722 appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment
1723 appendReservedUleb32(binary_bytes, @as(u32, 0)); // offset
1724
1725 // notify any waiters for segment initialization completion
1726 appendReservedI32Const(binary_bytes, flag_address);
1727 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1728 leb.writeIleb128(binary_bytes.fixedWriter(), @as(i32, -1)) catch unreachable; // number of waiters
1729 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));
1730 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_notify));
1731 appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment
1732 appendReservedUleb32(binary_bytes, @as(u32, 0)); // offset
1733 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.drop));
1734
1735 // branch and drop segments
1736 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br));
1737 appendReservedUleb32(binary_bytes, @as(u32, 1));
1738
1739 // wait for thread to initialize memory segments
1740 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); // end $wait
1741 appendReservedI32Const(binary_bytes, flag_address);
1742 appendReservedI32Const(binary_bytes, 1); // expected flag value
1743 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const));
1744 leb.writeIleb128(binary_bytes.fixedWriter(), @as(i64, -1)) catch unreachable; // timeout
1745 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));
1746 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_wait32));
1747 appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment
1748 appendReservedUleb32(binary_bytes, @as(u32, 0)); // offset
1749 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.drop));
1750
1751 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); // end $drop
1752 }
1753
1754 for (segment_groups, 0..) |group, segment_index| {
1755 const segment = group.first_segment;
1756 if (!segment.isPassive(wasm)) continue;
1757 if (segment.isBss(wasm)) continue;
1758 // The TLS region should not be dropped since its is needed
1759 // during the initialization of each thread (__wasm_init_tls).
1760 if (shared_memory and segment.isTls(wasm)) continue;
1761
1762 try binary_bytes.ensureUnusedCapacity(gpa, 1 + 5 + 5 + 1);
1763
1764 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix));
1765 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.MiscOpcode.data_drop));
1766 appendReservedUleb32(binary_bytes, @intCast(segment_index));
1767 }
1768
1769 // End of the function body
1770 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1771}
1772
1773fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!void {
1774 const comp = wasm.base.comp;
1775 const gpa = comp.gpa;
1776
1777 assert(comp.config.shared_memory);
1778
1779 try bytes.ensureUnusedCapacity(gpa, 5 * 10 + 8);
1780
1781 appendReservedUleb32(bytes, 0); // no locals
1782
1783 // If there's a TLS segment, initialize it during runtime using the bulk-memory feature
1784 // TLS segment is always the first one due to how we sort the data segments.
1785 const data_segments = wasm.flush_buffer.data_segments.keys();
1786 if (data_segments.len > 0 and data_segments[0].isTls(wasm)) {
1787 const start_addr = wasm.flush_buffer.data_segments.values()[0];
1788 const end_addr = wasm.flush_buffer.data_segment_groups.items[0].end_addr;
1789 const group_size = end_addr - start_addr;
1790 const data_segment_index = 0;
1791
1792 const param_local: u32 = 0;
1793
1794 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1795 appendReservedUleb32(bytes, param_local);
1796
1797 const tls_base_global_index: Wasm.GlobalIndex = @enumFromInt(wasm.globals.getIndex(.__tls_base).?);
1798 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
1799 appendReservedUleb32(bytes, @intFromEnum(tls_base_global_index));
1800
1801 // load stack values for the bulk-memory operation
1802 {
1803 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1804 appendReservedUleb32(bytes, param_local);
1805
1806 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1807 appendReservedUleb32(bytes, 0); //segment offset
1808
1809 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1810 appendReservedUleb32(bytes, group_size); //segment offset
1811 }
1812
1813 // perform the bulk-memory operation to initialize the data segment
1814 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix));
1815 appendReservedUleb32(bytes, @intFromEnum(std.wasm.MiscOpcode.memory_init));
1816 // segment immediate
1817 appendReservedUleb32(bytes, data_segment_index);
1818 // memory index immediate (always 0)
1819 appendReservedUleb32(bytes, 0);
1820 }
1821
1822 // If we have to perform any TLS relocations, call the corresponding function
1823 // which performs all runtime TLS relocations. This is a synthetic function,
1824 // generated by the linker.
1825 if (wasm.functions.getIndex(.__wasm_apply_global_tls_relocs)) |function_index| {
1826 const output_function_index: Wasm.OutputFunctionIndex = .fromFunctionIndex(wasm, @enumFromInt(function_index));
1827 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
1828 appendReservedUleb32(bytes, @intFromEnum(output_function_index));
1829 }
1830
1831 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1832}
1833
1834fn emitStartSection(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) !void {
1835 const header_offset = try reserveVecSectionHeader(gpa, bytes);
1836 replaceVecSectionHeader(bytes, header_offset, .start, @intFromEnum(i));
1837}
1838
1839fn emitTagNameFunction(
1840 wasm: *Wasm,
1841 code: *std.ArrayListUnmanaged(u8),
1842 table_base_addr: u32,
1843 table_index: u32,
1844 enum_type_ip: InternPool.Index,
1845) !void {
1846 const comp = wasm.base.comp;
1847 const gpa = comp.gpa;
1848 const diags = &comp.link_diags;
1849 const zcu = comp.zcu.?;
1850 const ip = &zcu.intern_pool;
1851 const enum_type = ip.loadEnumType(enum_type_ip);
1852 const tag_values = enum_type.values.get(ip);
1853
1854 try code.ensureUnusedCapacity(gpa, 7 * 5 + 6 + 1 * 6);
1855 appendReservedUleb32(code, 0); // no locals
1856
1857 const slice_abi_size = 8;
1858 const encoded_alignment = @ctz(@as(u32, 4));
1859 if (tag_values.len == 0) {
1860 // Then it's auto-numbered and therefore a direct table lookup.
1861 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1862 appendReservedUleb32(code, 0);
1863
1864 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1865 appendReservedUleb32(code, 1);
1866
1867 appendReservedI32Const(code, slice_abi_size);
1868 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_mul));
1869
1870 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_load));
1871 appendReservedUleb32(code, encoded_alignment);
1872 appendReservedUleb32(code, table_base_addr + table_index * 8);
1873
1874 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store));
1875 appendReservedUleb32(code, encoded_alignment);
1876 appendReservedUleb32(code, 0);
1877 } else {
1878 const int_info = Zcu.Type.intInfo(.fromInterned(enum_type.tag_ty), zcu);
1879 const outer_block_type: std.wasm.BlockType = switch (int_info.bits) {
1880 0...32 => .i32,
1881 33...64 => .i64,
1882 else => return diags.fail("wasm linker does not yet implement @tagName for sparse enums with more than 64 bit integer tag types", .{}),
1883 };
1884
1885 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1886 appendReservedUleb32(code, 0);
1887
1888 // Outer block that computes table offset.
1889 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block));
1890 code.appendAssumeCapacity(@intFromEnum(outer_block_type));
1891
1892 for (tag_values, 0..) |tag_value, tag_index| {
1893 // block for this if case
1894 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block));
1895 code.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty));
1896
1897 // Tag value whose name should be returned.
1898 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1899 appendReservedUleb32(code, 1);
1900
1901 const val: Zcu.Value = .fromInterned(tag_value);
1902 switch (outer_block_type) {
1903 .i32 => {
1904 const x: u32 = switch (int_info.signedness) {
1905 .signed => @bitCast(@as(i32, @intCast(val.toSignedInt(zcu)))),
1906 .unsigned => @intCast(val.toUnsignedInt(zcu)),
1907 };
1908 appendReservedI32Const(code, x);
1909 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_ne));
1910 },
1911 .i64 => {
1912 const x: u64 = switch (int_info.signedness) {
1913 .signed => @bitCast(val.toSignedInt(zcu)),
1914 .unsigned => val.toUnsignedInt(zcu),
1915 };
1916 appendReservedI64Const(code, x);
1917 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_ne));
1918 },
1919 else => unreachable,
1920 }
1921
1922 // if they're not equal, break out of current branch
1923 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_if));
1924 appendReservedUleb32(code, 0);
1925
1926 // Put the table offset of the result on the stack.
1927 appendReservedI32Const(code, @intCast(tag_index * slice_abi_size));
1928
1929 // break outside blocks
1930 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br));
1931 appendReservedUleb32(code, 1);
1932
1933 // end the block for this case
1934 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1935 }
1936 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.@"unreachable"));
1937 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1938
1939 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_load));
1940 appendReservedUleb32(code, encoded_alignment);
1941 appendReservedUleb32(code, table_base_addr + table_index * 8);
1942
1943 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store));
1944 appendReservedUleb32(code, encoded_alignment);
1945 appendReservedUleb32(code, 0);
1946 }
1947
1948 // End of the function body
1949 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1950}
1951
1952/// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value.
1953fn appendReservedI32Const(bytes: *std.ArrayListUnmanaged(u8), val: u32) void {
1954 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1955 leb.writeIleb128(bytes.fixedWriter(), @as(i32, @bitCast(val))) catch unreachable;
1956}
1957
1958/// Writes an unsigned 64-bit integer as a LEB128-encoded 'i64.const' value.
1959fn appendReservedI64Const(bytes: *std.ArrayListUnmanaged(u8), val: u64) void {
1960 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const));
1961 leb.writeIleb128(bytes.fixedWriter(), @as(i64, @bitCast(val))) catch unreachable;
1962}
1963
1964fn appendReservedUleb32(bytes: *std.ArrayListUnmanaged(u8), val: u32) void {
1965 leb.writeUleb128(bytes.fixedWriter(), val) catch unreachable;
1966}
1967
1968fn appendGlobal(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), mutable: u8, val: u32) Allocator.Error!void {
1969 try bytes.ensureUnusedCapacity(gpa, 9);
1970 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Valtype.i32));
1971 bytes.appendAssumeCapacity(mutable);
1972 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1973 appendReservedUleb32(bytes, val);
1974 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1975}
src/link/Wasm/Object.zig+1416-737
...@@ -1,841 +1,1520 @@...@@ -1,841 +1,1520 @@
1//! Object represents a wasm object file. When initializing a new
2//! `Object`, it will parse the contents of a given file handler, and verify
3//! the data on correctness. The result can then be used by the linker.
4const Object = @This();1const Object = @This();
52
6const Wasm = @import("../Wasm.zig");3const Wasm = @import("../Wasm.zig");
7const Atom = Wasm.Atom;
8const Alignment = Wasm.Alignment;4const Alignment = Wasm.Alignment;
9const Symbol = @import("Symbol.zig");
105
11const std = @import("std");6const std = @import("std");
12const Allocator = std.mem.Allocator;7const Allocator = std.mem.Allocator;
13const leb = std.leb;
14const meta = std.meta;
15const Path = std.Build.Cache.Path;8const Path = std.Build.Cache.Path;
16
17const log = std.log.scoped(.object);9const log = std.log.scoped(.object);
10const assert = std.debug.assert;
1811
19/// Wasm spec version used for this `Object`12/// Wasm spec version used for this `Object`
20version: u32 = 0,13version: u32,
21/// For error reporting purposes only.14/// For error reporting purposes only.
22/// Name (read path) of the object or archive file.15/// Name (read path) of the object or archive file.
23path: Path,16path: Path,
24/// For error reporting purposes only.17/// For error reporting purposes only.
25/// If this represents an object in an archive, it's the basename of the18/// If this represents an object in an archive, it's the basename of the
26/// object, and path refers to the archive.19/// object, and path refers to the archive.
27archive_member_name: ?[]const u8,20archive_member_name: Wasm.OptionalString,
28/// Parsed type section
29func_types: []const std.wasm.Type = &.{},
30/// A list of all imports for this module
31imports: []const Wasm.Import = &.{},
32/// Parsed function section
33functions: []const std.wasm.Func = &.{},
34/// Parsed table section
35tables: []const std.wasm.Table = &.{},
36/// Parsed memory section
37memories: []const std.wasm.Memory = &.{},
38/// Parsed global section
39globals: []const std.wasm.Global = &.{},
40/// Parsed export section
41exports: []const Wasm.Export = &.{},
42/// Parsed element section
43elements: []const std.wasm.Element = &.{},
44/// Represents the function ID that must be called on startup.21/// Represents the function ID that must be called on startup.
45/// This is `null` by default as runtimes may determine the startup22/// This is `null` by default as runtimes may determine the startup
46/// function themselves. This is essentially legacy.23/// function themselves. This is essentially legacy.
47start: ?u32 = null,24start_function: Wasm.OptionalObjectFunctionIndex,
48/// A slice of features that tell the linker what features are mandatory,25/// A slice of features that tell the linker what features are mandatory, used
49/// used (or therefore missing) and must generate an error when another26/// (or therefore missing) and must generate an error when another object uses
50/// object uses features that are not supported by the other.27/// features that are not supported by the other.
51features: []const Wasm.Feature = &.{},28features: Wasm.Feature.Set,
52/// A table that maps the relocations we must perform where the key represents29/// Points into `Wasm.object_functions`
53/// the section that the list of relocations applies to.30functions: RelativeSlice,
54relocations: std.AutoArrayHashMapUnmanaged(u32, []Wasm.Relocation) = .empty,31/// Points into `Wasm.object_function_imports`
55/// Table of symbols belonging to this Object file32function_imports: RelativeSlice,
56symtable: []Symbol = &.{},33/// Points into `Wasm.object_global_imports`
57/// Extra metadata about the linking section, such as alignment of segments and their name34global_imports: RelativeSlice,
58segment_info: []const Wasm.NamedSegment = &.{},35/// Points into `Wasm.object_table_imports`
59/// A sequence of function initializers that must be called on startup36table_imports: RelativeSlice,
60init_funcs: []const Wasm.InitFunc = &.{},37// Points into `Wasm.object_data_imports`
61/// Comdat information38data_imports: RelativeSlice,
62comdat_info: []const Wasm.Comdat = &.{},39/// Points into Wasm object_custom_segments
63/// Represents non-synthetic sections that can essentially be mem-cpy'd into place40custom_segments: RelativeSlice,
64/// after performing relocations.41/// Points into Wasm object_init_funcs
65relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableData) = .empty,42init_funcs: RelativeSlice,
66/// Amount of functions in the `import` sections.43/// Points into Wasm object_comdats
67imported_functions_count: u32 = 0,44comdats: RelativeSlice,
68/// Amount of globals in the `import` section.45/// Guaranteed to be non-null when functions has nonzero length.
69imported_globals_count: u32 = 0,46code_section_index: ?Wasm.ObjectSectionIndex,
70/// Amount of tables in the `import` section.47/// Guaranteed to be non-null when globals has nonzero length.
71imported_tables_count: u32 = 0,48global_section_index: ?Wasm.ObjectSectionIndex,
7249/// Guaranteed to be non-null when data segments has nonzero length.
73/// Represents a single item within a section (depending on its `type`)50data_section_index: ?Wasm.ObjectSectionIndex,
74pub const RelocatableData = struct {51is_included: bool,
75 /// The type of the relocatable data52
76 type: Tag,53pub const RelativeSlice = struct {
77 /// Pointer to the data of the segment, where its length is written to `size`54 off: u32,
78 data: [*]u8,55 len: u32,
79 /// The size in bytes of the data representing the segment within the section
80 size: u32,
81 /// The index within the section itself, or in case of a debug section,
82 /// the offset within the `string_table`.
83 index: u32,
84 /// The offset within the section where the data starts
85 offset: u32,
86 /// Represents the index of the section it belongs to
87 section_index: u32,
88 /// Whether the relocatable section is represented by a symbol or not.
89 /// Can only be `true` for custom sections.
90 represented: bool = false,
91
92 const Tag = enum { data, code, custom };
93
94 /// Returns the alignment of the segment, by retrieving it from the segment
95 /// meta data of the given object file.
96 /// NOTE: Alignment is encoded as a power of 2, so we shift the symbol's
97 /// alignment to retrieve the natural alignment.
98 pub fn getAlignment(relocatable_data: RelocatableData, object: *const Object) Alignment {
99 if (relocatable_data.type != .data) return .@"1";
100 return object.segment_info[relocatable_data.index].alignment;
101 }
102
103 /// Returns the symbol kind that corresponds to the relocatable section
104 pub fn getSymbolKind(relocatable_data: RelocatableData) Symbol.Tag {
105 return switch (relocatable_data.type) {
106 .data => .data,
107 .code => .function,
108 .custom => .section,
109 };
110 }
111
112 /// Returns the index within a section, or in case of a custom section,
113 /// returns the section index within the object file.
114 pub fn getIndex(relocatable_data: RelocatableData) u32 {
115 if (relocatable_data.type == .custom) return relocatable_data.section_index;
116 return relocatable_data.index;
117 }
118};56};
11957
120/// Initializes a new `Object` from a wasm object file.58pub const SegmentInfo = struct {
121/// This also parses and verifies the object file.59 name: Wasm.String,
122/// When a max size is given, will only parse up to the given size,60 flags: Flags,
123/// else will read until the end of the file.61
124pub fn create(62 /// Matches the ABI.
125 wasm: *Wasm,63 pub const Flags = packed struct(u32) {
126 file_contents: []const u8,64 /// Signals that the segment contains only null terminated strings allowing
127 path: Path,65 /// the linker to perform merging.
128 archive_member_name: ?[]const u8,66 strings: bool,
129) !Object {67 /// The segment contains thread-local data. This means that a unique copy
130 const gpa = wasm.base.comp.gpa;68 /// of this segment will be created for each thread.
131 var object: Object = .{69 tls: bool,
132 .path = path,70 /// If the object file is included in the final link, the segment should be
133 .archive_member_name = archive_member_name,71 /// retained in the final output regardless of whether it is used by the
72 /// program.
73 retain: bool,
74 alignment: Alignment,
75
76 _: u23 = 0,
134 };77 };
78};
13579
136 var parser: Parser = .{80pub const FunctionImport = struct {
137 .object = &object,81 module_name: Wasm.String,
138 .wasm = wasm,82 name: Wasm.String,
139 .reader = std.io.fixedBufferStream(file_contents),83 function_index: ScratchSpace.FuncTypeIndex,
140 };84};
141 try parser.parseObject(gpa);
14285
143 return object;86pub const GlobalImport = struct {
144}87 module_name: Wasm.String,
88 name: Wasm.String,
89 valtype: std.wasm.Valtype,
90 mutable: bool,
91};
14592
146/// Frees all memory of `Object` at once. The given `Allocator` must be93pub const TableImport = struct {
147/// the same allocator that was used when `init` was called.94 module_name: Wasm.String,
148pub fn deinit(object: *Object, gpa: Allocator) void {95 name: Wasm.String,
149 for (object.func_types) |func_ty| {96 limits_min: u32,
150 gpa.free(func_ty.params);97 limits_max: u32,
151 gpa.free(func_ty.returns);98 limits_has_max: bool,
152 }99 limits_is_shared: bool,
153 gpa.free(object.func_types);100 ref_type: std.wasm.RefType,
154 gpa.free(object.functions);101};
155 gpa.free(object.imports);
156 gpa.free(object.tables);
157 gpa.free(object.memories);
158 gpa.free(object.globals);
159 gpa.free(object.exports);
160 for (object.elements) |el| {
161 gpa.free(el.func_indexes);
162 }
163 gpa.free(object.elements);
164 gpa.free(object.features);
165 for (object.relocations.values()) |val| {
166 gpa.free(val);
167 }
168 object.relocations.deinit(gpa);
169 gpa.free(object.symtable);
170 gpa.free(object.comdat_info);
171 gpa.free(object.init_funcs);
172 for (object.segment_info) |info| {
173 gpa.free(info.name);
174 }
175 gpa.free(object.segment_info);
176 {
177 var it = object.relocatable_data.valueIterator();
178 while (it.next()) |relocatable_data| {
179 for (relocatable_data.*) |rel_data| {
180 gpa.free(rel_data.data[0..rel_data.size]);
181 }
182 gpa.free(relocatable_data.*);
183 }
184 }
185 object.relocatable_data.deinit(gpa);
186 object.* = undefined;
187}
188102
189/// Finds the import within the list of imports from a given kind and index of that kind.103pub const DataSegmentFlags = enum(u32) { active, passive, active_memidx };
190/// Asserts the import exists
191pub fn findImport(object: *const Object, sym: Symbol) Wasm.Import {
192 var i: u32 = 0;
193 return for (object.imports) |import| {
194 if (std.meta.activeTag(import.kind) == sym.tag.externalType()) {
195 if (i == sym.index) return import;
196 i += 1;
197 }
198 } else unreachable; // Only existing imports are allowed to be found
199}
200104
201/// Checks if the object file is an MVP version.105pub const SubsectionType = enum(u8) {
202/// When that's the case, we check if there's an import table definition with its name106 segment_info = 5,
203/// set to '__indirect_function_table". When that's also the case,107 init_funcs = 6,
204/// we initialize a new table symbol that corresponds to that import and return that symbol.108 comdat_info = 7,
205///109 symbol_table = 8,
206/// When the object file is *NOT* MVP, we return `null`.110};
207fn checkLegacyIndirectFunctionTable(object: *Object, wasm: *const Wasm) !?Symbol {
208 const diags = &wasm.base.comp.link_diags;
209111
210 var table_count: usize = 0;112/// Specified by https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md
211 for (object.symtable) |sym| {113pub const RelocationType = enum(u8) {
212 if (sym.tag == .table) table_count += 1;114 function_index_leb = 0,
213 }115 table_index_sleb = 1,
116 table_index_i32 = 2,
117 memory_addr_leb = 3,
118 memory_addr_sleb = 4,
119 memory_addr_i32 = 5,
120 type_index_leb = 6,
121 global_index_leb = 7,
122 function_offset_i32 = 8,
123 section_offset_i32 = 9,
124 event_index_leb = 10,
125 memory_addr_rel_sleb = 11,
126 table_index_rel_sleb = 12,
127 global_index_i32 = 13,
128 memory_addr_leb64 = 14,
129 memory_addr_sleb64 = 15,
130 memory_addr_i64 = 16,
131 memory_addr_rel_sleb64 = 17,
132 table_index_sleb64 = 18,
133 table_index_i64 = 19,
134 table_number_leb = 20,
135 memory_addr_tls_sleb = 21,
136 function_offset_i64 = 22,
137 memory_addr_locrel_i32 = 23,
138 table_index_rel_sleb64 = 24,
139 memory_addr_tls_sleb64 = 25,
140 function_index_i32 = 26,
141};
214142
215 // For each import table, we also have a symbol so this is not a legacy object file143pub const Symbol = struct {
216 if (object.imported_tables_count == table_count) return null;144 flags: Wasm.SymbolFlags,
145 name: Wasm.OptionalString,
146 pointee: Pointee,
147
148 /// https://github.com/WebAssembly/tool-conventions/blob/df8d737539eb8a8f446ba5eab9dc670c40dfb81e/Linking.md#symbol-table-subsection
149 const Tag = enum(u8) {
150 function,
151 data,
152 global,
153 section,
154 event,
155 table,
156 };
217157
218 if (table_count != 0) {158 const Pointee = union(enum) {
219 return diags.failParse(object.path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{159 function: Wasm.ObjectFunctionIndex,
220 object.imported_tables_count,160 function_import: ScratchSpace.FuncImportIndex,
221 table_count,161 data: Wasm.ObjectData.Index,
222 });162 data_import: void,
223 }163 global: Wasm.ObjectGlobalIndex,
164 global_import: ScratchSpace.GlobalImportIndex,
165 section: Wasm.ObjectSectionIndex,
166 table: Wasm.ObjectTableIndex,
167 table_import: ScratchSpace.TableImportIndex,
168 };
169};
224170
225 // MVP object files cannot have any table definitions, only imports (for the indirect function table).171pub const ScratchSpace = struct {
226 if (object.tables.len > 0) {172 func_types: std.ArrayListUnmanaged(Wasm.FunctionType.Index) = .empty,
227 return diags.failParse(object.path, "unexpected table definition without representing table symbols.", .{});173 func_type_indexes: std.ArrayListUnmanaged(FuncTypeIndex) = .empty,
228 }174 func_imports: std.ArrayListUnmanaged(FunctionImport) = .empty,
175 global_imports: std.ArrayListUnmanaged(GlobalImport) = .empty,
176 table_imports: std.ArrayListUnmanaged(TableImport) = .empty,
177 symbol_table: std.ArrayListUnmanaged(Symbol) = .empty,
178 segment_info: std.ArrayListUnmanaged(SegmentInfo) = .empty,
179 exports: std.ArrayListUnmanaged(Export) = .empty,
180
181 const Export = struct {
182 name: Wasm.String,
183 pointee: Pointee,
184
185 const Pointee = union(std.wasm.ExternalKind) {
186 function: Wasm.ObjectFunctionIndex,
187 table: Wasm.ObjectTableIndex,
188 memory: Wasm.ObjectMemory.Index,
189 global: Wasm.ObjectGlobalIndex,
190 };
191 };
229192
230 if (object.imported_tables_count != 1) {193 /// Index into `func_imports`.
231 return diags.failParse(object.path, "found more than one table import, but no representing table symbols", .{});194 const FuncImportIndex = enum(u32) {
232 }195 _,
233196
234 const table_import: Wasm.Import = for (object.imports) |imp| {197 fn ptr(index: FuncImportIndex, ss: *const ScratchSpace) *FunctionImport {
235 if (imp.kind == .table) {198 return &ss.func_imports.items[@intFromEnum(index)];
236 break imp;
237 }199 }
238 } else unreachable;200 };
239201
240 if (table_import.name != wasm.preloaded_strings.__indirect_function_table) {202 /// Index into `global_imports`.
241 return diags.failParse(object.path, "non-indirect function table import '{s}' is missing a corresponding symbol", .{203 const GlobalImportIndex = enum(u32) {
242 wasm.stringSlice(table_import.name),204 _,
243 });
244 }
245205
246 var table_symbol: Symbol = .{206 fn ptr(index: GlobalImportIndex, ss: *const ScratchSpace) *GlobalImport {
247 .flags = 0,207 return &ss.global_imports.items[@intFromEnum(index)];
248 .name = table_import.name,208 }
249 .tag = .table,
250 .index = 0,
251 .virtual_address = undefined,
252 };209 };
253 table_symbol.setFlag(.WASM_SYM_UNDEFINED);
254 table_symbol.setFlag(.WASM_SYM_NO_STRIP);
255 return table_symbol;
256}
257210
258const Parser = struct {211 /// Index into `table_imports`.
259 reader: std.io.FixedBufferStream([]const u8),212 const TableImportIndex = enum(u32) {
260 /// Object file we're building213 _,
261 object: *Object,214
262 /// Mutable so that the string table can be modified.215 fn ptr(index: TableImportIndex, ss: *const ScratchSpace) *TableImport {
263 wasm: *Wasm,216 return &ss.table_imports.items[@intFromEnum(index)];
217 }
218 };
264219
265 fn parseObject(parser: *Parser, gpa: Allocator) anyerror!void {220 /// Index into `func_types`.
266 const wasm = parser.wasm;221 const FuncTypeIndex = enum(u32) {
222 _,
267223
268 {224 fn ptr(index: FuncTypeIndex, ss: *const ScratchSpace) *Wasm.FunctionType.Index {
269 var magic_bytes: [4]u8 = undefined;225 return &ss.func_types.items[@intFromEnum(index)];
270 try parser.reader.reader().readNoEof(&magic_bytes);
271 if (!std.mem.eql(u8, &magic_bytes, &std.wasm.magic)) return error.BadObjectMagic;
272 }226 }
227 };
273228
274 const version = try parser.reader.reader().readInt(u32, .little);229 pub fn deinit(ss: *ScratchSpace, gpa: Allocator) void {
275 parser.object.version = version;230 ss.exports.deinit(gpa);
276231 ss.func_types.deinit(gpa);
277 var saw_linking_section = false;232 ss.func_type_indexes.deinit(gpa);
278233 ss.func_imports.deinit(gpa);
279 var section_index: u32 = 0;234 ss.global_imports.deinit(gpa);
280 while (parser.reader.reader().readByte()) |byte| : (section_index += 1) {235 ss.table_imports.deinit(gpa);
281 const len = try readLeb(u32, parser.reader.reader());236 ss.symbol_table.deinit(gpa);
282 var limited_reader = std.io.limitedReader(parser.reader.reader(), len);237 ss.segment_info.deinit(gpa);
283 const reader = limited_reader.reader();238 ss.* = undefined;
284 switch (@as(std.wasm.Section, @enumFromInt(byte))) {239 }
285 .custom => {
286 const name_len = try readLeb(u32, reader);
287 const name = try gpa.alloc(u8, name_len);
288 defer gpa.free(name);
289 try reader.readNoEof(name);
290
291 if (std.mem.eql(u8, name, "linking")) {
292 saw_linking_section = true;
293 try parser.parseMetadata(gpa, @as(usize, @intCast(reader.context.bytes_left)));
294 } else if (std.mem.startsWith(u8, name, "reloc")) {
295 try parser.parseRelocations(gpa);
296 } else if (std.mem.eql(u8, name, "target_features")) {
297 try parser.parseFeatures(gpa);
298 } else if (std.mem.startsWith(u8, name, ".debug")) {
299 const gop = try parser.object.relocatable_data.getOrPut(gpa, .custom);
300 var relocatable_data: std.ArrayListUnmanaged(RelocatableData) = .empty;
301 defer relocatable_data.deinit(gpa);
302 if (!gop.found_existing) {
303 gop.value_ptr.* = &.{};
304 } else {
305 relocatable_data = std.ArrayListUnmanaged(RelocatableData).fromOwnedSlice(gop.value_ptr.*);
306 }
307 const debug_size = @as(u32, @intCast(reader.context.bytes_left));
308 const debug_content = try gpa.alloc(u8, debug_size);
309 errdefer gpa.free(debug_content);
310 try reader.readNoEof(debug_content);
311
312 try relocatable_data.append(gpa, .{
313 .type = .custom,
314 .data = debug_content.ptr,
315 .size = debug_size,
316 .index = @intFromEnum(try wasm.internString(name)),
317 .offset = 0, // debug sections only contain 1 entry, so no need to calculate offset
318 .section_index = section_index,
319 });
320 gop.value_ptr.* = try relocatable_data.toOwnedSlice(gpa);
321 } else {
322 try reader.skipBytes(reader.context.bytes_left, .{});
323 }
324 },
325 .type => {
326 for (try readVec(&parser.object.func_types, reader, gpa)) |*type_val| {
327 if ((try reader.readByte()) != std.wasm.function_type) return error.ExpectedFuncType;
328240
329 for (try readVec(&type_val.params, reader, gpa)) |*param| {241 fn clear(ss: *ScratchSpace) void {
330 param.* = try readEnum(std.wasm.Valtype, reader);242 ss.exports.clearRetainingCapacity();
331 }243 ss.func_types.clearRetainingCapacity();
244 ss.func_type_indexes.clearRetainingCapacity();
245 ss.func_imports.clearRetainingCapacity();
246 ss.global_imports.clearRetainingCapacity();
247 ss.table_imports.clearRetainingCapacity();
248 ss.symbol_table.clearRetainingCapacity();
249 ss.segment_info.clearRetainingCapacity();
250 }
251};
332252
333 for (try readVec(&type_val.returns, reader, gpa)) |*result| {253pub fn parse(
334 result.* = try readEnum(std.wasm.Valtype, reader);254 wasm: *Wasm,
255 bytes: []const u8,
256 path: Path,
257 archive_member_name: ?[]const u8,
258 host_name: Wasm.OptionalString,
259 ss: *ScratchSpace,
260 must_link: bool,
261 gc_sections: bool,
262) anyerror!Object {
263 const comp = wasm.base.comp;
264 const gpa = comp.gpa;
265 const diags = &comp.link_diags;
266
267 var pos: usize = 0;
268
269 if (!std.mem.eql(u8, bytes[0..std.wasm.magic.len], &std.wasm.magic)) return error.BadObjectMagic;
270 pos += std.wasm.magic.len;
271
272 const version = std.mem.readInt(u32, bytes[pos..][0..4], .little);
273 pos += 4;
274
275 const data_segment_start: u32 = @intCast(wasm.object_data_segments.items.len);
276 const custom_segment_start: u32 = @intCast(wasm.object_custom_segments.entries.len);
277 const functions_start: u32 = @intCast(wasm.object_functions.items.len);
278 const tables_start: u32 = @intCast(wasm.object_tables.items.len);
279 const memories_start: u32 = @intCast(wasm.object_memories.items.len);
280 const globals_start: u32 = @intCast(wasm.object_globals.items.len);
281 const init_funcs_start: u32 = @intCast(wasm.object_init_funcs.items.len);
282 const comdats_start: u32 = @intCast(wasm.object_comdats.items.len);
283 const function_imports_start: u32 = @intCast(wasm.object_function_imports.entries.len);
284 const global_imports_start: u32 = @intCast(wasm.object_global_imports.entries.len);
285 const table_imports_start: u32 = @intCast(wasm.object_table_imports.entries.len);
286 const data_imports_start: u32 = @intCast(wasm.object_data_imports.entries.len);
287 const local_section_index_base = wasm.object_total_sections;
288 const object_index: Wasm.ObjectIndex = @enumFromInt(wasm.objects.items.len);
289 const source_location: Wasm.SourceLocation = .fromObject(object_index, wasm);
290
291 ss.clear();
292
293 var start_function: Wasm.OptionalObjectFunctionIndex = .none;
294 var opt_features: ?Wasm.Feature.Set = null;
295 var saw_linking_section = false;
296 var has_tls = false;
297 var table_import_symbol_count: usize = 0;
298 var code_section_index: ?Wasm.ObjectSectionIndex = null;
299 var global_section_index: ?Wasm.ObjectSectionIndex = null;
300 var data_section_index: ?Wasm.ObjectSectionIndex = null;
301 while (pos < bytes.len) : (wasm.object_total_sections += 1) {
302 const section_index: Wasm.ObjectSectionIndex = @enumFromInt(wasm.object_total_sections);
303
304 const section_tag: std.wasm.Section = @enumFromInt(bytes[pos]);
305 pos += 1;
306
307 const len, pos = readLeb(u32, bytes, pos);
308 const section_end = pos + len;
309 switch (section_tag) {
310 .custom => {
311 const section_name, pos = readBytes(bytes, pos);
312 if (std.mem.eql(u8, section_name, "linking")) {
313 saw_linking_section = true;
314 const section_version, pos = readLeb(u32, bytes, pos);
315 log.debug("link meta data version: {d}", .{section_version});
316 if (section_version != 2) return error.UnsupportedVersion;
317 while (pos < section_end) {
318 const sub_type, pos = readLeb(u8, bytes, pos);
319 log.debug("found subsection: {s}", .{@tagName(@as(SubsectionType, @enumFromInt(sub_type)))});
320 const payload_len, pos = readLeb(u32, bytes, pos);
321 if (payload_len == 0) break;
322
323 const count, pos = readLeb(u32, bytes, pos);
324
325 switch (@as(SubsectionType, @enumFromInt(sub_type))) {
326 .segment_info => {
327 for (try ss.segment_info.addManyAsSlice(gpa, count)) |*segment| {
328 const name, pos = readBytes(bytes, pos);
329 const alignment, pos = readLeb(u32, bytes, pos);
330 const flags_u32, pos = readLeb(u32, bytes, pos);
331 const flags: SegmentInfo.Flags = @bitCast(flags_u32);
332 const tls = flags.tls or
333 // Supports legacy object files that specified
334 // being TLS by the name instead of the TLS flag.
335 std.mem.startsWith(u8, name, ".tdata") or
336 std.mem.startsWith(u8, name, ".tbss");
337 has_tls = has_tls or tls;
338 segment.* = .{
339 .name = try wasm.internString(name),
340 .flags = .{
341 .strings = flags.strings,
342 .tls = tls,
343 .alignment = @enumFromInt(alignment),
344 .retain = flags.retain,
345 },
346 };
347 }
348 },
349 .init_funcs => {
350 for (try wasm.object_init_funcs.addManyAsSlice(gpa, count)) |*func| {
351 const priority, pos = readLeb(u32, bytes, pos);
352 const symbol_index, pos = readLeb(u32, bytes, pos);
353 if (symbol_index > ss.symbol_table.items.len)
354 return diags.failParse(path, "init_funcs before symbol table", .{});
355 const sym = &ss.symbol_table.items[symbol_index];
356 if (sym.pointee != .function) {
357 return diags.failParse(path, "init_func symbol '{s}' not a function", .{
358 sym.name.slice(wasm).?,
359 });
360 } else if (sym.flags.undefined) {
361 return diags.failParse(path, "init_func symbol '{s}' is an import", .{
362 sym.name.slice(wasm).?,
363 });
364 }
365 func.* = .{
366 .priority = priority,
367 .function_index = sym.pointee.function,
368 };
369 }
370 },
371 .comdat_info => {
372 for (try wasm.object_comdats.addManyAsSlice(gpa, count)) |*comdat| {
373 const name, pos = readBytes(bytes, pos);
374 const flags, pos = readLeb(u32, bytes, pos);
375 if (flags != 0) return error.UnexpectedComdatFlags;
376 const symbol_count, pos = readLeb(u32, bytes, pos);
377 const start_off: u32 = @intCast(wasm.object_comdat_symbols.len);
378 try wasm.object_comdat_symbols.ensureUnusedCapacity(gpa, symbol_count);
379 for (0..symbol_count) |_| {
380 const kind, pos = readEnum(Wasm.Comdat.Symbol.Type, bytes, pos);
381 const index, pos = readLeb(u32, bytes, pos);
382 if (true) @panic("TODO rebase index depending on kind");
383 wasm.object_comdat_symbols.appendAssumeCapacity(.{
384 .kind = kind,
385 .index = index,
386 });
387 }
388 comdat.* = .{
389 .name = try wasm.internString(name),
390 .flags = flags,
391 .symbols = .{
392 .off = start_off,
393 .len = @intCast(wasm.object_comdat_symbols.len - start_off),
394 },
395 };
396 }
397 },
398 .symbol_table => {
399 for (try ss.symbol_table.addManyAsSlice(gpa, count)) |*symbol| {
400 const tag, pos = readEnum(Symbol.Tag, bytes, pos);
401 const flags, pos = readLeb(u32, bytes, pos);
402 symbol.* = .{
403 .flags = @bitCast(flags),
404 .name = .none,
405 .pointee = undefined,
406 };
407 symbol.flags.initZigSpecific(must_link, gc_sections);
408
409 switch (tag) {
410 .data => {
411 const name, pos = readBytes(bytes, pos);
412 const interned_name = try wasm.internString(name);
413 symbol.name = interned_name.toOptional();
414 if (symbol.flags.undefined) {
415 symbol.pointee = .data_import;
416 } else {
417 const segment_index, pos = readLeb(u32, bytes, pos);
418 const segment_offset, pos = readLeb(u32, bytes, pos);
419 const size, pos = readLeb(u32, bytes, pos);
420 try wasm.object_datas.append(gpa, .{
421 .segment = @enumFromInt(data_segment_start + segment_index),
422 .offset = segment_offset,
423 .size = size,
424 .name = interned_name,
425 .flags = symbol.flags,
426 });
427 symbol.pointee = .{
428 .data = @enumFromInt(wasm.object_datas.items.len - 1),
429 };
430 }
431 },
432 .section => {
433 const local_section, pos = readLeb(u32, bytes, pos);
434 const section: Wasm.ObjectSectionIndex = @enumFromInt(local_section_index_base + local_section);
435 symbol.pointee = .{ .section = section };
436 },
437
438 .function => {
439 const local_index, pos = readLeb(u32, bytes, pos);
440 if (symbol.flags.undefined) {
441 const function_import: ScratchSpace.FuncImportIndex = @enumFromInt(local_index);
442 symbol.pointee = .{ .function_import = function_import };
443 if (symbol.flags.explicit_name) {
444 const name, pos = readBytes(bytes, pos);
445 symbol.name = (try wasm.internString(name)).toOptional();
446 } else {
447 symbol.name = function_import.ptr(ss).name.toOptional();
448 }
449 } else {
450 symbol.pointee = .{ .function = @enumFromInt(functions_start + (local_index - ss.func_imports.items.len)) };
451 const name, pos = readBytes(bytes, pos);
452 symbol.name = (try wasm.internString(name)).toOptional();
453 }
454 },
455 .global => {
456 const local_index, pos = readLeb(u32, bytes, pos);
457 if (symbol.flags.undefined) {
458 const global_import: ScratchSpace.GlobalImportIndex = @enumFromInt(local_index);
459 symbol.pointee = .{ .global_import = global_import };
460 if (symbol.flags.explicit_name) {
461 const name, pos = readBytes(bytes, pos);
462 symbol.name = (try wasm.internString(name)).toOptional();
463 } else {
464 symbol.name = global_import.ptr(ss).name.toOptional();
465 }
466 } else {
467 symbol.pointee = .{ .global = @enumFromInt(globals_start + (local_index - ss.global_imports.items.len)) };
468 const name, pos = readBytes(bytes, pos);
469 symbol.name = (try wasm.internString(name)).toOptional();
470 }
471 },
472 .table => {
473 const local_index, pos = readLeb(u32, bytes, pos);
474 if (symbol.flags.undefined) {
475 table_import_symbol_count += 1;
476 const table_import: ScratchSpace.TableImportIndex = @enumFromInt(local_index);
477 symbol.pointee = .{ .table_import = table_import };
478 if (symbol.flags.explicit_name) {
479 const name, pos = readBytes(bytes, pos);
480 symbol.name = (try wasm.internString(name)).toOptional();
481 } else {
482 symbol.name = table_import.ptr(ss).name.toOptional();
483 }
484 } else {
485 symbol.pointee = .{ .table = @enumFromInt(tables_start + (local_index - ss.table_imports.items.len)) };
486 const name, pos = readBytes(bytes, pos);
487 symbol.name = (try wasm.internString(name)).toOptional();
488 }
489 },
490 else => {
491 log.debug("unrecognized symbol type tag: {x}", .{@intFromEnum(tag)});
492 return error.UnrecognizedSymbolType;
493 },
494 }
495 }
496 },
335 }497 }
336 }498 }
337 try assertEnd(reader);499 } else if (std.mem.startsWith(u8, section_name, "reloc.")) {
338 },500 // 'The "reloc." custom sections must come after the "linking" custom section'
339 .import => {501 if (!saw_linking_section) return error.RelocBeforeLinkingSection;
340 for (try readVec(&parser.object.imports, reader, gpa)) |*import| {502
341 const module_len = try readLeb(u32, reader);503 // "Relocation sections start with an identifier specifying
342 const module_name = try gpa.alloc(u8, module_len);504 // which section they apply to, and must be sequenced in
343 defer gpa.free(module_name);505 // the module after that section."
344 try reader.readNoEof(module_name);506 // "Relocation sections can only target code, data and custom sections."
345507 const local_section, pos = readLeb(u32, bytes, pos);
346 const name_len = try readLeb(u32, reader);508 const count, pos = readLeb(u32, bytes, pos);
347 const name = try gpa.alloc(u8, name_len);509 const section: Wasm.ObjectSectionIndex = @enumFromInt(local_section_index_base + local_section);
348 defer gpa.free(name);510
349 try reader.readNoEof(name);511 log.debug("found {d} relocations for section={d}", .{ count, section });
350512
351 const kind = try readEnum(std.wasm.ExternalKind, reader);513 var prev_offset: u32 = 0;
352 const kind_value: std.wasm.Import.Kind = switch (kind) {514 try wasm.object_relocations.ensureUnusedCapacity(gpa, count);
353 .function => val: {515 for (0..count) |_| {
354 parser.object.imported_functions_count += 1;516 const tag: RelocationType = @enumFromInt(bytes[pos]);
355 break :val .{ .function = try readLeb(u32, reader) };517 pos += 1;
518 const offset, pos = readLeb(u32, bytes, pos);
519 const index, pos = readLeb(u32, bytes, pos);
520
521 if (offset < prev_offset)
522 return diags.failParse(path, "relocation entries not sorted by offset", .{});
523 prev_offset = offset;
524
525 const sym = &ss.symbol_table.items[index];
526
527 switch (tag) {
528 .memory_addr_leb,
529 .memory_addr_sleb,
530 .memory_addr_i32,
531 .memory_addr_rel_sleb,
532 .memory_addr_leb64,
533 .memory_addr_sleb64,
534 .memory_addr_i64,
535 .memory_addr_rel_sleb64,
536 .memory_addr_tls_sleb,
537 .memory_addr_locrel_i32,
538 .memory_addr_tls_sleb64,
539 => {
540 const addend: i32, pos = readLeb(i32, bytes, pos);
541 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
542 .data => |data| .{
543 .tag = .fromType(tag),
544 .offset = offset,
545 .pointee = .{ .data = data },
546 .addend = addend,
547 },
548 .data_import => .{
549 .tag = .fromTypeImport(tag),
550 .offset = offset,
551 .pointee = .{ .symbol_name = sym.name.unwrap().? },
552 .addend = addend,
553 },
554 else => unreachable,
555 });
556 },
557 .function_offset_i32, .function_offset_i64 => {
558 const addend: i32, pos = readLeb(i32, bytes, pos);
559 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
560 .function => .{
561 .tag = .fromType(tag),
562 .offset = offset,
563 .pointee = .{ .function = sym.pointee.function },
564 .addend = addend,
565 },
566 .function_import => .{
567 .tag = .fromTypeImport(tag),
568 .offset = offset,
569 .pointee = .{ .symbol_name = sym.name.unwrap().? },
570 .addend = addend,
571 },
572 else => unreachable,
573 });
356 },574 },
357 .memory => .{ .memory = try readLimits(reader) },575 .section_offset_i32 => {
358 .global => val: {576 const addend: i32, pos = readLeb(i32, bytes, pos);
359 parser.object.imported_globals_count += 1;577 wasm.object_relocations.appendAssumeCapacity(.{
360 break :val .{ .global = .{578 .tag = .section_offset_i32,
361 .valtype = try readEnum(std.wasm.Valtype, reader),579 .offset = offset,
362 .mutable = (try reader.readByte()) == 0x01,580 .pointee = .{ .section = sym.pointee.section },
363 } };581 .addend = addend,
582 });
364 },583 },
365 .table => val: {584 .type_index_leb => {
366 parser.object.imported_tables_count += 1;585 wasm.object_relocations.appendAssumeCapacity(.{
367 break :val .{ .table = .{586 .tag = .type_index_leb,
368 .reftype = try readEnum(std.wasm.RefType, reader),587 .offset = offset,
369 .limits = try readLimits(reader),588 .pointee = .{ .type_index = ss.func_types.items[index] },
370 } };589 .addend = undefined,
590 });
591 },
592 .function_index_leb,
593 .function_index_i32,
594 .table_index_sleb,
595 .table_index_i32,
596 .table_index_sleb64,
597 .table_index_i64,
598 .table_index_rel_sleb,
599 .table_index_rel_sleb64,
600 => {
601 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
602 .function => .{
603 .tag = .fromType(tag),
604 .offset = offset,
605 .pointee = .{ .function = sym.pointee.function },
606 .addend = undefined,
607 },
608 .function_import => .{
609 .tag = .fromTypeImport(tag),
610 .offset = offset,
611 .pointee = .{ .symbol_name = sym.name.unwrap().? },
612 .addend = undefined,
613 },
614 else => unreachable,
615 });
616 },
617 .global_index_leb, .global_index_i32 => {
618 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
619 .global => .{
620 .tag = .fromType(tag),
621 .offset = offset,
622 .pointee = .{ .global = sym.pointee.global },
623 .addend = undefined,
624 },
625 .global_import => .{
626 .tag = .fromTypeImport(tag),
627 .offset = offset,
628 .pointee = .{ .symbol_name = sym.name.unwrap().? },
629 .addend = undefined,
630 },
631 else => unreachable,
632 });
371 },633 },
372 };
373634
374 import.* = .{635 .table_number_leb => {
375 .module_name = try wasm.internString(module_name),636 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
376 .name = try wasm.internString(name),637 .table => .{
377 .kind = kind_value,638 .tag = .fromType(tag),
378 };639 .offset = offset,
379 }640 .pointee = .{ .table = sym.pointee.table },
380 try assertEnd(reader);641 .addend = undefined,
381 },642 },
382 .function => {643 .table_import => .{
383 for (try readVec(&parser.object.functions, reader, gpa)) |*func| {644 .tag = .fromTypeImport(tag),
384 func.* = .{ .type_index = try readLeb(u32, reader) };645 .offset = offset,
385 }646 .pointee = .{ .symbol_name = sym.name.unwrap().? },
386 try assertEnd(reader);647 .addend = undefined,
387 },648 },
388 .table => {649 else => unreachable,
389 for (try readVec(&parser.object.tables, reader, gpa)) |*table| {650 });
390 table.* = .{651 },
391 .reftype = try readEnum(std.wasm.RefType, reader),652 .event_index_leb => return diags.failParse(path, "unsupported relocation: R_WASM_EVENT_INDEX_LEB", .{}),
392 .limits = try readLimits(reader),653 }
393 };
394 }654 }
395 try assertEnd(reader);655
396 },656 try wasm.object_relocations_table.putNoClobber(gpa, section, .{
397 .memory => {657 .off = @intCast(wasm.object_relocations.len - count),
398 for (try readVec(&parser.object.memories, reader, gpa)) |*memory| {658 .len = count,
399 memory.* = .{ .limits = try readLimits(reader) };659 });
660 } else if (std.mem.eql(u8, section_name, "target_features")) {
661 opt_features, pos = try parseFeatures(wasm, bytes, pos, path);
662 } else if (std.mem.startsWith(u8, section_name, ".debug")) {
663 const debug_content = bytes[pos..section_end];
664 pos = section_end;
665
666 const data_off: u32 = @intCast(wasm.string_bytes.items.len);
667 try wasm.string_bytes.appendSlice(gpa, debug_content);
668
669 try wasm.object_custom_segments.put(gpa, section_index, .{
670 .payload = .{
671 .off = @enumFromInt(data_off),
672 .len = @intCast(debug_content.len),
673 },
674 .flags = .{},
675 .section_name = try wasm.internString(section_name),
676 });
677 } else {
678 pos = section_end;
679 }
680 },
681 .type => {
682 const func_types_len, pos = readLeb(u32, bytes, pos);
683 for (try ss.func_types.addManyAsSlice(gpa, func_types_len)) |*func_type| {
684 if (bytes[pos] != std.wasm.function_type) return error.ExpectedFuncType;
685 pos += 1;
686
687 const params, pos = readBytes(bytes, pos);
688 const returns, pos = readBytes(bytes, pos);
689 func_type.* = try wasm.addFuncType(.{
690 .params = .fromString(try wasm.internString(params)),
691 .returns = .fromString(try wasm.internString(returns)),
692 });
693 }
694 },
695 .import => {
696 const imports_len, pos = readLeb(u32, bytes, pos);
697 for (0..imports_len) |_| {
698 const module_name, pos = readBytes(bytes, pos);
699 const name, pos = readBytes(bytes, pos);
700 const kind, pos = readEnum(std.wasm.ExternalKind, bytes, pos);
701 const interned_module_name = try wasm.internString(module_name);
702 const interned_name = try wasm.internString(name);
703 switch (kind) {
704 .function => {
705 const function, pos = readLeb(u32, bytes, pos);
706 try ss.func_imports.append(gpa, .{
707 .module_name = interned_module_name,
708 .name = interned_name,
709 .function_index = @enumFromInt(function),
710 });
711 },
712 .memory => {
713 const limits, pos = readLimits(bytes, pos);
714 const gop = try wasm.object_memory_imports.getOrPut(gpa, interned_name);
715 if (gop.found_existing) {
716 if (gop.value_ptr.module_name != interned_module_name) {
717 var err = try diags.addErrorWithNotes(2);
718 try err.addMsg("memory '{s}' mismatching module names", .{name});
719 gop.value_ptr.source_location.addNote(&err, "module '{s}' here", .{
720 gop.value_ptr.module_name.slice(wasm),
721 });
722 source_location.addNote(&err, "module '{s}' here", .{module_name});
723 }
724 // TODO error for mismatching flags
725 gop.value_ptr.limits_min = @min(gop.value_ptr.limits_min, limits.min);
726 gop.value_ptr.limits_max = @max(gop.value_ptr.limits_max, limits.max);
727 } else {
728 gop.value_ptr.* = .{
729 .module_name = interned_module_name,
730 .limits_min = limits.min,
731 .limits_max = limits.max,
732 .limits_has_max = limits.flags.has_max,
733 .limits_is_shared = limits.flags.is_shared,
734 .source_location = source_location,
735 };
736 }
737 },
738 .global => {
739 const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos);
740 const mutable = bytes[pos] == 0x01;
741 pos += 1;
742 try ss.global_imports.append(gpa, .{
743 .name = interned_name,
744 .valtype = valtype,
745 .mutable = mutable,
746 .module_name = interned_module_name,
747 });
748 },
749 .table => {
750 const ref_type, pos = readEnum(std.wasm.RefType, bytes, pos);
751 const limits, pos = readLimits(bytes, pos);
752 try ss.table_imports.append(gpa, .{
753 .name = interned_name,
754 .module_name = interned_module_name,
755 .limits_min = limits.min,
756 .limits_max = limits.max,
757 .limits_has_max = limits.flags.has_max,
758 .limits_is_shared = limits.flags.is_shared,
759 .ref_type = ref_type,
760 });
761 },
400 }762 }
401 try assertEnd(reader);763 }
402 },764 },
403 .global => {765 .function => {
404 for (try readVec(&parser.object.globals, reader, gpa)) |*global| {766 const functions_len, pos = readLeb(u32, bytes, pos);
405 global.* = .{767 for (try ss.func_type_indexes.addManyAsSlice(gpa, functions_len)) |*func_type_index| {
768 const i, pos = readLeb(u32, bytes, pos);
769 func_type_index.* = @enumFromInt(i);
770 }
771 },
772 .table => {
773 const tables_len, pos = readLeb(u32, bytes, pos);
774 for (try wasm.object_tables.addManyAsSlice(gpa, tables_len)) |*table| {
775 const ref_type, pos = readEnum(std.wasm.RefType, bytes, pos);
776 const limits, pos = readLimits(bytes, pos);
777 table.* = .{
778 .name = .none,
779 .module_name = .none,
780 .flags = .{
781 .ref_type = .from(ref_type),
782 .limits_has_max = limits.flags.has_max,
783 .limits_is_shared = limits.flags.is_shared,
784 },
785 .limits_min = limits.min,
786 .limits_max = limits.max,
787 };
788 }
789 },
790 .memory => {
791 const memories_len, pos = readLeb(u32, bytes, pos);
792 for (try wasm.object_memories.addManyAsSlice(gpa, memories_len)) |*memory| {
793 const limits, pos = readLimits(bytes, pos);
794 memory.* = .{
795 .name = .none,
796 .flags = .{
797 .limits_has_max = limits.flags.has_max,
798 .limits_is_shared = limits.flags.is_shared,
799 },
800 .limits_min = limits.min,
801 .limits_max = limits.max,
802 };
803 }
804 },
805 .global => {
806 if (global_section_index != null)
807 return diags.failParse(path, "object has more than one global section", .{});
808 global_section_index = section_index;
809
810 const section_start = pos;
811 const globals_len, pos = readLeb(u32, bytes, pos);
812 for (try wasm.object_globals.addManyAsSlice(gpa, globals_len)) |*global| {
813 const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos);
814 const mutable = bytes[pos] == 0x01;
815 pos += 1;
816 const init_start = pos;
817 const expr, pos = try readInit(wasm, bytes, pos);
818 global.* = .{
819 .name = .none,
820 .flags = .{
406 .global_type = .{821 .global_type = .{
407 .valtype = try readEnum(std.wasm.Valtype, reader),822 .valtype = .from(valtype),
408 .mutable = (try reader.readByte()) == 0x01,823 .mutable = mutable,
409 },824 },
410 .init = try readInit(reader),825 },
411 };826 .expr = expr,
412 }827 .object_index = object_index,
413 try assertEnd(reader);828 .offset = @intCast(init_start - section_start),
414 },829 .size = @intCast(pos - init_start),
415 .@"export" => {830 };
416 for (try readVec(&parser.object.exports, reader, gpa)) |*exp| {831 }
417 const name_len = try readLeb(u32, reader);832 },
418 const name = try gpa.alloc(u8, name_len);833 .@"export" => {
419 defer gpa.free(name);834 const exports_len, pos = readLeb(u32, bytes, pos);
420 try reader.readNoEof(name);835 // Read into scratch space, and then later add this data as if
421 exp.* = .{836 // it were extra symbol table entries, but allow merging with
422 .name = try wasm.internString(name),837 // existing symbol table data if the name matches.
423 .kind = try readEnum(std.wasm.ExternalKind, reader),838 for (try ss.exports.addManyAsSlice(gpa, exports_len)) |*exp| {
424 .index = try readLeb(u32, reader),839 const name, pos = readBytes(bytes, pos);
425 };840 const kind: std.wasm.ExternalKind = @enumFromInt(bytes[pos]);
841 pos += 1;
842 const index, pos = readLeb(u32, bytes, pos);
843 exp.* = .{
844 .name = try wasm.internString(name),
845 .pointee = switch (kind) {
846 .function => .{ .function = @enumFromInt(functions_start + (index - ss.func_imports.items.len)) },
847 .table => .{ .table = @enumFromInt(tables_start + (index - ss.table_imports.items.len)) },
848 .memory => .{ .memory = @enumFromInt(memories_start + index) },
849 .global => .{ .global = @enumFromInt(globals_start + (index - ss.global_imports.items.len)) },
850 },
851 };
852 }
853 },
854 .start => {
855 const index, pos = readLeb(u32, bytes, pos);
856 start_function = @enumFromInt(functions_start + index);
857 },
858 .element => {
859 log.warn("unimplemented: element section in {} {?s}", .{ path, archive_member_name });
860 pos = section_end;
861 },
862 .code => {
863 if (code_section_index != null)
864 return diags.failParse(path, "object has more than one code section", .{});
865 code_section_index = section_index;
866
867 const start = pos;
868 const count, pos = readLeb(u32, bytes, pos);
869 for (try wasm.object_functions.addManyAsSlice(gpa, count)) |*elem| {
870 const code_len, pos = readLeb(u32, bytes, pos);
871 const offset: u32 = @intCast(pos - start);
872 const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..code_len]);
873 pos += code_len;
874 elem.* = .{
875 .flags = .{}, // populated from symbol table
876 .name = .none, // populated from symbol table
877 .type_index = undefined, // populated from func_types
878 .code = payload,
879 .offset = offset,
880 .object_index = object_index,
881 };
882 }
883 },
884 .data => {
885 if (data_section_index != null)
886 return diags.failParse(path, "object has more than one data section", .{});
887 data_section_index = section_index;
888
889 const section_start = pos;
890 const count, pos = readLeb(u32, bytes, pos);
891 for (try wasm.object_data_segments.addManyAsSlice(gpa, count)) |*elem| {
892 const flags, pos = readEnum(DataSegmentFlags, bytes, pos);
893 if (flags == .active_memidx) {
894 const memidx, pos = readLeb(u32, bytes, pos);
895 if (memidx != 0) return diags.failParse(path, "data section uses mem index {d}", .{memidx});
426 }896 }
427 try assertEnd(reader);897 //const expr, pos = if (flags != .passive) try readInit(wasm, bytes, pos) else .{ .none, pos };
428 },898 if (flags != .passive) pos = try skipInit(bytes, pos);
429 .start => {899 const data_len, pos = readLeb(u32, bytes, pos);
430 parser.object.start = try readLeb(u32, reader);900 const segment_start = pos;
431 try assertEnd(reader);901 const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..data_len]);
432 },902 pos += data_len;
433 .element => {903 elem.* = .{
434 for (try readVec(&parser.object.elements, reader, gpa)) |*elem| {904 .payload = payload,
435 elem.table_index = try readLeb(u32, reader);905 .name = .none, // Populated from segment_info
436 elem.offset = try readInit(reader);906 .flags = .{
907 .is_passive = flags == .passive,
908 }, // Remainder populated from segment_info
909 .offset = @intCast(segment_start - section_start),
910 .object_index = object_index,
911 };
912 }
913 },
914 else => pos = section_end,
915 }
916 if (pos != section_end) return error.MalformedSection;
917 }
918 if (!saw_linking_section) return error.MissingLinkingSection;
437919
438 for (try readVec(&elem.func_indexes, reader, gpa)) |*idx| {920 const target_features = comp.root_mod.resolved_target.result.cpu.features;
439 idx.* = try readLeb(u32, reader);921
440 }922 if (has_tls) {
441 }923 if (!std.Target.wasm.featureSetHas(target_features, .atomics))
442 try assertEnd(reader);924 return diags.failParse(path, "object has TLS segment but target CPU feature atomics is disabled", .{});
925 if (!std.Target.wasm.featureSetHas(target_features, .bulk_memory))
926 return diags.failParse(path, "object has TLS segment but target CPU feature bulk_memory is disabled", .{});
927 }
928
929 const features = opt_features orelse return error.MissingFeatures;
930 for (features.slice(wasm)) |feat| {
931 log.debug("feature: {s}{s}", .{ @tagName(feat.prefix), @tagName(feat.tag) });
932 switch (feat.prefix) {
933 .invalid => unreachable,
934 .@"-" => switch (feat.tag) {
935 .@"shared-mem" => if (comp.config.shared_memory) {
936 return diags.failParse(path, "object forbids shared-mem but compilation enables it", .{});
443 },937 },
444 .code => {938 else => {
445 const start = reader.context.bytes_left;939 const f = feat.tag.toCpuFeature().?;
446 var index: u32 = 0;940 if (std.Target.wasm.featureSetHas(target_features, f)) {
447 const count = try readLeb(u32, reader);941 return diags.failParse(
448 const imported_function_count = parser.object.imported_functions_count;942 path,
449 var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count);943 "object forbids {s} but specified target features include {s}",
450 defer relocatable_data.deinit();944 .{ @tagName(feat.tag), @tagName(f) },
451 while (index < count) : (index += 1) {945 );
452 const code_len = try readLeb(u32, reader);
453 const offset = @as(u32, @intCast(start - reader.context.bytes_left));
454 const data = try gpa.alloc(u8, code_len);
455 errdefer gpa.free(data);
456 try reader.readNoEof(data);
457 relocatable_data.appendAssumeCapacity(.{
458 .type = .code,
459 .data = data.ptr,
460 .size = code_len,
461 .index = imported_function_count + index,
462 .offset = offset,
463 .section_index = section_index,
464 });
465 }946 }
466 try parser.object.relocatable_data.put(gpa, .code, try relocatable_data.toOwnedSlice());
467 },947 },
468 .data => {948 },
469 const start = reader.context.bytes_left;949 .@"+", .@"=" => switch (feat.tag) {
470 var index: u32 = 0;950 .@"shared-mem" => if (!comp.config.shared_memory) {
471 const count = try readLeb(u32, reader);951 return diags.failParse(path, "object requires shared-mem but compilation disables it", .{});
472 var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count);952 },
473 defer relocatable_data.deinit();953 else => {
474 while (index < count) : (index += 1) {954 const f = feat.tag.toCpuFeature().?;
475 const flags = try readLeb(u32, reader);955 if (!std.Target.wasm.featureSetHas(target_features, f)) {
476 const data_offset = try readInit(reader);956 return diags.failParse(
477 _ = flags; // TODO: Do we need to check flags to detect passive/active memory?957 path,
478 _ = data_offset;958 "object requires {s} but specified target features exclude {s}",
479 const data_len = try readLeb(u32, reader);959 .{ @tagName(feat.tag), @tagName(f) },
480 const offset = @as(u32, @intCast(start - reader.context.bytes_left));960 );
481 const data = try gpa.alloc(u8, data_len);
482 errdefer gpa.free(data);
483 try reader.readNoEof(data);
484 relocatable_data.appendAssumeCapacity(.{
485 .type = .data,
486 .data = data.ptr,
487 .size = data_len,
488 .index = index,
489 .offset = offset,
490 .section_index = section_index,
491 });
492 }961 }
493 try parser.object.relocatable_data.put(gpa, .data, try relocatable_data.toOwnedSlice());
494 },962 },
495 else => try parser.reader.reader().skipBytes(len, .{}),963 },
496 }
497 } else |err| switch (err) {
498 error.EndOfStream => {}, // finished parsing the file
499 else => |e| return e,
500 }
501 if (!saw_linking_section) return error.MissingLinkingSection;
502 }
503
504 /// Based on the "features" custom section, parses it into a list of
505 /// features that tell the linker what features were enabled and may be mandatory
506 /// to be able to link.
507 /// Logs an info message when an undefined feature is detected.
508 fn parseFeatures(parser: *Parser, gpa: Allocator) !void {
509 const diags = &parser.wasm.base.comp.link_diags;
510 const reader = parser.reader.reader();
511 for (try readVec(&parser.object.features, reader, gpa)) |*feature| {
512 const prefix = try readEnum(Wasm.Feature.Prefix, reader);
513 const name_len = try leb.readUleb128(u32, reader);
514 const name = try gpa.alloc(u8, name_len);
515 defer gpa.free(name);
516 try reader.readNoEof(name);
517
518 const tag = Wasm.known_features.get(name) orelse {
519 return diags.failParse(parser.object.path, "object file contains unknown feature: {s}", .{name});
520 };
521 feature.* = .{
522 .prefix = prefix,
523 .tag = tag,
524 };
525 }
526 }
527
528 /// Parses a "reloc" custom section into a list of relocations.
529 /// The relocations are mapped into `Object` where the key is the section
530 /// they apply to.
531 fn parseRelocations(parser: *Parser, gpa: Allocator) !void {
532 const reader = parser.reader.reader();
533 const section = try leb.readUleb128(u32, reader);
534 const count = try leb.readUleb128(u32, reader);
535 const relocations = try gpa.alloc(Wasm.Relocation, count);
536 errdefer gpa.free(relocations);
537
538 log.debug("Found {d} relocations for section ({d})", .{
539 count,
540 section,
541 });
542
543 for (relocations) |*relocation| {
544 const rel_type = try reader.readByte();
545 const rel_type_enum = std.meta.intToEnum(Wasm.Relocation.RelocationType, rel_type) catch return error.MalformedSection;
546 relocation.* = .{
547 .relocation_type = rel_type_enum,
548 .offset = try leb.readUleb128(u32, reader),
549 .index = try leb.readUleb128(u32, reader),
550 .addend = if (rel_type_enum.addendIsPresent()) try leb.readIleb128(i32, reader) else 0,
551 };
552 log.debug("Found relocation: type({s}) offset({d}) index({d}) addend({?d})", .{
553 @tagName(relocation.relocation_type),
554 relocation.offset,
555 relocation.index,
556 relocation.addend,
557 });
558 }964 }
559
560 try parser.object.relocations.putNoClobber(gpa, section, relocations);
561 }965 }
562966
563 /// Parses the "linking" custom section. Versions that are not967 // Apply function type information.
564 /// supported will be an error. `payload_size` is required to be able968 for (ss.func_type_indexes.items, wasm.object_functions.items[functions_start..]) |func_type, *func| {
565 /// to calculate the subsections we need to parse, as that data is not969 func.type_index = func_type.ptr(ss).*;
566 /// available within the section itparser.
567 fn parseMetadata(parser: *Parser, gpa: Allocator, payload_size: usize) !void {
568 var limited = std.io.limitedReader(parser.reader.reader(), payload_size);
569 const limited_reader = limited.reader();
570
571 const version = try leb.readUleb128(u32, limited_reader);
572 log.debug("Link meta data version: {d}", .{version});
573 if (version != 2) return error.UnsupportedVersion;
574
575 while (limited.bytes_left > 0) {
576 try parser.parseSubsection(gpa, limited_reader);
577 }
578 }970 }
579971
580 /// Parses a `spec.Subsection`.972 // Apply symbol table information.
581 /// The `reader` param for this is to provide a `LimitedReader`, which allows973 for (ss.symbol_table.items) |symbol| switch (symbol.pointee) {
582 /// us to only read until a max length.974 .function_import => |index| {
583 ///975 const ptr = index.ptr(ss);
584 /// `parser` is used to provide access to other sections that may be needed,976 const name = symbol.name.unwrap() orelse ptr.name;
585 /// such as access to the `import` section to find the name of a symbol.977 if (symbol.flags.binding == .local) {
586 fn parseSubsection(parser: *Parser, gpa: Allocator, reader: anytype) !void {978 diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
587 const wasm = parser.wasm;979 continue;
588 const sub_type = try leb.readUleb128(u8, reader);980 }
589 log.debug("Found subsection: {s}", .{@tagName(@as(Wasm.SubsectionType, @enumFromInt(sub_type)))});981 const gop = try wasm.object_function_imports.getOrPut(gpa, name);
590 const payload_len = try leb.readUleb128(u32, reader);982 const fn_ty_index = ptr.function_index.ptr(ss).*;
591 if (payload_len == 0) return;983 if (gop.found_existing) {
592984 if (gop.value_ptr.type != fn_ty_index) {
593 var limited = std.io.limitedReader(reader, payload_len);985 var err = try diags.addErrorWithNotes(2);
594 const limited_reader = limited.reader();986 try err.addMsg("symbol '{s}' mismatching function signatures", .{name.slice(wasm)});
595987 gop.value_ptr.source_location.addNote(&err, "imported as {} here", .{
596 // every subsection contains a 'count' field988 gop.value_ptr.type.fmt(wasm),
597 const count = try leb.readUleb128(u32, limited_reader);
598
599 switch (@as(Wasm.SubsectionType, @enumFromInt(sub_type))) {
600 .WASM_SEGMENT_INFO => {
601 const segments = try gpa.alloc(Wasm.NamedSegment, count);
602 errdefer gpa.free(segments);
603 for (segments) |*segment| {
604 const name_len = try leb.readUleb128(u32, reader);
605 const name = try gpa.alloc(u8, name_len);
606 errdefer gpa.free(name);
607 try reader.readNoEof(name);
608 segment.* = .{
609 .name = name,
610 .alignment = @enumFromInt(try leb.readUleb128(u32, reader)),
611 .flags = try leb.readUleb128(u32, reader),
612 };
613 log.debug("Found segment: {s} align({d}) flags({b})", .{
614 segment.name,
615 segment.alignment,
616 segment.flags,
617 });989 });
618990 source_location.addNote(&err, "imported as {} here", .{fn_ty_index.fmt(wasm)});
619 // support legacy object files that specified being TLS by the name instead of the TLS flag.991 continue;
620 if (!segment.isTLS() and (std.mem.startsWith(u8, segment.name, ".tdata") or std.mem.startsWith(u8, segment.name, ".tbss"))) {992 }
621 // set the flag so we can simply check for the flag in the rest of the linker.993 if (gop.value_ptr.module_name != ptr.module_name.toOptional()) {
622 segment.flags |= @intFromEnum(Wasm.NamedSegment.Flags.WASM_SEG_FLAG_TLS);994 var err = try diags.addErrorWithNotes(2);
995 try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)});
996 if (gop.value_ptr.module_name.slice(wasm)) |module_name| {
997 gop.value_ptr.source_location.addNote(&err, "module '{s}' here", .{module_name});
998 } else {
999 gop.value_ptr.source_location.addNote(&err, "no module here", .{});
623 }1000 }
1001 source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
1002 continue;
624 }1003 }
625 parser.object.segment_info = segments;1004 if (gop.value_ptr.name != ptr.name) {
626 },1005 var err = try diags.addErrorWithNotes(2);
627 .WASM_INIT_FUNCS => {1006 try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)});
628 const funcs = try gpa.alloc(Wasm.InitFunc, count);1007 gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)});
629 errdefer gpa.free(funcs);1008 source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)});
630 for (funcs) |*func| {1009 continue;
631 func.* = .{
632 .priority = try leb.readUleb128(u32, reader),
633 .symbol_index = try leb.readUleb128(u32, reader),
634 };
635 log.debug("Found function - prio: {d}, index: {d}", .{ func.priority, func.symbol_index });
636 }1010 }
637 parser.object.init_funcs = funcs;1011 } else {
638 },1012 gop.value_ptr.* = .{
639 .WASM_COMDAT_INFO => {1013 .flags = symbol.flags,
640 const comdats = try gpa.alloc(Wasm.Comdat, count);1014 .module_name = ptr.module_name.toOptional(),
641 errdefer gpa.free(comdats);1015 .name = ptr.name,
642 for (comdats) |*comdat| {1016 .source_location = source_location,
643 const name_len = try leb.readUleb128(u32, reader);1017 .resolution = .unresolved,
644 const name = try gpa.alloc(u8, name_len);1018 .type = fn_ty_index,
645 errdefer gpa.free(name);1019 };
646 try reader.readNoEof(name);1020 }
6471021 },
648 const flags = try leb.readUleb128(u32, reader);1022 .global_import => |index| {
649 if (flags != 0) {1023 const ptr = index.ptr(ss);
650 return error.UnexpectedValue;1024 const name = symbol.name.unwrap() orelse ptr.name;
651 }1025 if (symbol.flags.binding == .local) {
6521026 diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
653 const symbol_count = try leb.readUleb128(u32, reader);1027 continue;
654 const symbols = try gpa.alloc(Wasm.ComdatSym, symbol_count);1028 }
655 errdefer gpa.free(symbols);1029 const gop = try wasm.object_global_imports.getOrPut(gpa, name);
656 for (symbols) |*symbol| {1030 if (gop.found_existing) {
657 symbol.* = .{1031 const existing_ty = gop.value_ptr.type();
658 .kind = @as(Wasm.ComdatSym.Type, @enumFromInt(try leb.readUleb128(u8, reader))),1032 if (ptr.valtype != existing_ty.valtype) {
659 .index = try leb.readUleb128(u32, reader),1033 var err = try diags.addErrorWithNotes(2);
660 };1034 try err.addMsg("symbol '{s}' mismatching global types", .{name.slice(wasm)});
1035 gop.value_ptr.source_location.addNote(&err, "type {s} here", .{@tagName(existing_ty.valtype)});
1036 source_location.addNote(&err, "type {s} here", .{@tagName(ptr.valtype)});
1037 continue;
1038 }
1039 if (ptr.mutable != existing_ty.mutable) {
1040 var err = try diags.addErrorWithNotes(2);
1041 try err.addMsg("symbol '{s}' mismatching global mutability", .{name.slice(wasm)});
1042 gop.value_ptr.source_location.addNote(&err, "{s} here", .{
1043 if (existing_ty.mutable) "mutable" else "not mutable",
1044 });
1045 source_location.addNote(&err, "{s} here", .{
1046 if (ptr.mutable) "mutable" else "not mutable",
1047 });
1048 continue;
1049 }
1050 if (gop.value_ptr.module_name != ptr.module_name.toOptional()) {
1051 var err = try diags.addErrorWithNotes(2);
1052 try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)});
1053 if (gop.value_ptr.module_name.slice(wasm)) |module_name| {
1054 gop.value_ptr.source_location.addNote(&err, "module '{s}' here", .{module_name});
1055 } else {
1056 gop.value_ptr.source_location.addNote(&err, "no module here", .{});
661 }1057 }
6621058 source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
663 comdat.* = .{1059 continue;
664 .name = name,
665 .flags = flags,
666 .symbols = symbols,
667 };
668 }1060 }
6691061 if (gop.value_ptr.name != ptr.name) {
670 parser.object.comdat_info = comdats;1062 var err = try diags.addErrorWithNotes(2);
671 },1063 try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)});
672 .WASM_SYMBOL_TABLE => {1064 gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)});
673 var symbols = try std.ArrayList(Symbol).initCapacity(gpa, count);1065 source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)});
6741066 continue;
675 var i: usize = 0;1067 }
676 while (i < count) : (i += 1) {1068 } else {
677 const symbol = symbols.addOneAssumeCapacity();1069 gop.value_ptr.* = .{
678 symbol.* = try parser.parseSymbol(gpa, reader);1070 .flags = symbol.flags,
679 log.debug("Found symbol: type({s}) name({s}) flags(0b{b:0>8})", .{1071 .module_name = ptr.module_name.toOptional(),
680 @tagName(symbol.tag),1072 .name = ptr.name,
681 wasm.stringSlice(symbol.name),1073 .source_location = source_location,
682 symbol.flags,1074 .resolution = .unresolved,
1075 };
1076 gop.value_ptr.flags.global_type = .{
1077 .valtype = .from(ptr.valtype),
1078 .mutable = ptr.mutable,
1079 };
1080 }
1081 },
1082 .table_import => |index| {
1083 const ptr = index.ptr(ss);
1084 const name = symbol.name.unwrap() orelse ptr.name;
1085 if (symbol.flags.binding == .local) {
1086 diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
1087 continue;
1088 }
1089 const gop = try wasm.object_table_imports.getOrPut(gpa, name);
1090 if (gop.found_existing) {
1091 const existing_reftype = gop.value_ptr.flags.ref_type.to();
1092 if (ptr.ref_type != existing_reftype) {
1093 var err = try diags.addErrorWithNotes(2);
1094 try err.addMsg("symbol '{s}' mismatching table reftypes", .{name.slice(wasm)});
1095 gop.value_ptr.source_location.addNote(&err, "{s} here", .{@tagName(existing_reftype)});
1096 source_location.addNote(&err, "{s} here", .{@tagName(ptr.ref_type)});
1097 continue;
1098 }
1099 if (gop.value_ptr.module_name != ptr.module_name) {
1100 var err = try diags.addErrorWithNotes(2);
1101 try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)});
1102 gop.value_ptr.source_location.addNote(&err, "module '{s}' here", .{
1103 gop.value_ptr.module_name.slice(wasm),
683 });1104 });
1105 source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
1106 continue;
684 }1107 }
6851108 if (gop.value_ptr.name != ptr.name) {
686 // we found all symbols, check for indirect function table1109 var err = try diags.addErrorWithNotes(2);
687 // in case of an MVP object file1110 try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)});
688 if (try parser.object.checkLegacyIndirectFunctionTable(parser.wasm)) |symbol| {1111 gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)});
689 try symbols.append(symbol);1112 source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)});
690 log.debug("Found legacy indirect function table. Created symbol", .{});1113 continue;
691 }1114 }
6921115 if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong;
693 // Not all debug sections may be represented by a symbol, for those sections1116 if (!symbol.flags.visibility_hidden) gop.value_ptr.flags.visibility_hidden = false;
694 // we manually create a symbol.1117 if (symbol.flags.no_strip) gop.value_ptr.flags.no_strip = true;
695 if (parser.object.relocatable_data.get(.custom)) |custom_sections| {1118 } else {
696 for (custom_sections) |*data| {1119 gop.value_ptr.* = .{
697 if (!data.represented) {1120 .flags = symbol.flags,
698 const name = wasm.castToString(data.index);1121 .module_name = ptr.module_name,
699 try symbols.append(.{1122 .name = ptr.name,
700 .name = name,1123 .source_location = source_location,
701 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),1124 .resolution = .unresolved,
702 .tag = .section,1125 .limits_min = ptr.limits_min,
703 .virtual_address = 0,1126 .limits_max = ptr.limits_max,
704 .index = data.section_index,1127 };
705 });1128 gop.value_ptr.flags.limits_has_max = ptr.limits_has_max;
706 data.represented = true;1129 gop.value_ptr.flags.limits_is_shared = ptr.limits_is_shared;
707 log.debug("Created synthetic custom section symbol for '{s}'", .{1130 gop.value_ptr.flags.ref_type = .from(ptr.ref_type);
708 wasm.stringSlice(name),1131 }
709 });1132 },
710 }1133 .data_import => {
711 }1134 const name = symbol.name.unwrap().?;
1135 if (symbol.flags.binding == .local) {
1136 diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
1137 continue;
1138 }
1139 const gop = try wasm.object_data_imports.getOrPut(gpa, name);
1140 if (!gop.found_existing) gop.value_ptr.* = .{
1141 .flags = symbol.flags,
1142 .source_location = source_location,
1143 .resolution = .unresolved,
1144 };
1145 },
1146 .function => |index| {
1147 assert(!symbol.flags.undefined);
1148 const ptr = index.ptr(wasm);
1149 ptr.name = symbol.name;
1150 ptr.flags = symbol.flags;
1151 if (symbol.flags.binding == .local) continue; // No participation in symbol resolution.
1152 const name = symbol.name.unwrap().?;
1153 const gop = try wasm.object_function_imports.getOrPut(gpa, name);
1154 if (gop.found_existing) {
1155 if (gop.value_ptr.type != ptr.type_index) {
1156 var err = try diags.addErrorWithNotes(2);
1157 try err.addMsg("function signature mismatch: {s}", .{name.slice(wasm)});
1158 gop.value_ptr.source_location.addNote(&err, "exported as {} here", .{
1159 ptr.type_index.fmt(wasm),
1160 });
1161 const word = if (gop.value_ptr.resolution == .unresolved) "imported" else "exported";
1162 source_location.addNote(&err, "{s} as {} here", .{ word, gop.value_ptr.type.fmt(wasm) });
1163 continue;
712 }1164 }
1165 if (gop.value_ptr.resolution == .unresolved or gop.value_ptr.flags.binding == .weak) {
1166 // Intentional: if they're both weak, take the last one.
1167 gop.value_ptr.source_location = source_location;
1168 gop.value_ptr.module_name = host_name;
1169 gop.value_ptr.resolution = .fromObjectFunction(wasm, index);
1170 gop.value_ptr.flags = symbol.flags;
1171 continue;
1172 }
1173 if (ptr.flags.binding == .weak) {
1174 // Keep the existing one.
1175 continue;
1176 }
1177 var err = try diags.addErrorWithNotes(2);
1178 try err.addMsg("symbol collision: {s}", .{name.slice(wasm)});
1179 gop.value_ptr.source_location.addNote(&err, "exported as {} here", .{ptr.type_index.fmt(wasm)});
1180 source_location.addNote(&err, "exported as {} here", .{gop.value_ptr.type.fmt(wasm)});
1181 continue;
1182 } else {
1183 gop.value_ptr.* = .{
1184 .flags = symbol.flags,
1185 .module_name = host_name,
1186 .name = name,
1187 .source_location = source_location,
1188 .resolution = .fromObjectFunction(wasm, index),
1189 .type = ptr.type_index,
1190 };
1191 }
1192 },
1193 .global => |index| {
1194 assert(!symbol.flags.undefined);
1195 const ptr = index.ptr(wasm);
1196 ptr.name = symbol.name;
1197 ptr.flags = symbol.flags;
1198 if (symbol.flags.binding == .local) continue; // No participation in symbol resolution.
1199 const name = symbol.name.unwrap().?;
1200 const new_ty = ptr.type();
1201 const gop = try wasm.object_global_imports.getOrPut(gpa, name);
1202 if (gop.found_existing) {
1203 const existing_ty = gop.value_ptr.type();
1204 if (new_ty.valtype != existing_ty.valtype) {
1205 var err = try diags.addErrorWithNotes(2);
1206 try err.addMsg("symbol '{s}' mismatching global types", .{name.slice(wasm)});
1207 gop.value_ptr.source_location.addNote(&err, "type {s} here", .{@tagName(existing_ty.valtype)});
1208 source_location.addNote(&err, "type {s} here", .{@tagName(new_ty.valtype)});
1209 continue;
1210 }
1211 if (new_ty.mutable != existing_ty.mutable) {
1212 var err = try diags.addErrorWithNotes(2);
1213 try err.addMsg("symbol '{s}' mismatching global mutability", .{name.slice(wasm)});
1214 gop.value_ptr.source_location.addNote(&err, "{s} here", .{
1215 if (existing_ty.mutable) "mutable" else "not mutable",
1216 });
1217 source_location.addNote(&err, "{s} here", .{
1218 if (new_ty.mutable) "mutable" else "not mutable",
1219 });
1220 continue;
1221 }
1222 if (gop.value_ptr.resolution == .unresolved or gop.value_ptr.flags.binding == .weak) {
1223 // Intentional: if they're both weak, take the last one.
1224 gop.value_ptr.source_location = source_location;
1225 gop.value_ptr.module_name = host_name;
1226 gop.value_ptr.resolution = .fromObjectGlobal(wasm, index);
1227 gop.value_ptr.flags = symbol.flags;
1228 continue;
1229 }
1230 if (ptr.flags.binding == .weak) {
1231 // Keep the existing one.
1232 continue;
1233 }
1234 var err = try diags.addErrorWithNotes(2);
1235 try err.addMsg("symbol collision: {s}", .{name.slice(wasm)});
1236 gop.value_ptr.source_location.addNote(&err, "exported as {s} here", .{@tagName(existing_ty.valtype)});
1237 source_location.addNote(&err, "exported as {s} here", .{@tagName(new_ty.valtype)});
1238 continue;
1239 } else {
1240 gop.value_ptr.* = .{
1241 .flags = symbol.flags,
1242 .module_name = .none,
1243 .name = name,
1244 .source_location = source_location,
1245 .resolution = .fromObjectGlobal(wasm, index),
1246 };
1247 gop.value_ptr.flags.global_type = .{
1248 .valtype = .from(new_ty.valtype),
1249 .mutable = new_ty.mutable,
1250 };
1251 }
1252 },
1253 .table => |i| {
1254 assert(!symbol.flags.undefined);
1255 const ptr = i.ptr(wasm);
1256 ptr.name = symbol.name;
1257 ptr.flags = symbol.flags;
1258 },
1259 .data => |index| {
1260 assert(!symbol.flags.undefined);
1261 const ptr = index.ptr(wasm);
1262 const name = ptr.name;
1263 assert(name.toOptional() == symbol.name);
1264 ptr.flags = symbol.flags;
1265 if (symbol.flags.binding == .local) continue; // No participation in symbol resolution.
1266 const gop = try wasm.object_data_imports.getOrPut(gpa, name);
1267 if (gop.found_existing) {
1268 if (gop.value_ptr.resolution == .unresolved or gop.value_ptr.flags.binding == .weak) {
1269 // Intentional: if they're both weak, take the last one.
1270 gop.value_ptr.source_location = source_location;
1271 gop.value_ptr.resolution = .fromObjectDataIndex(wasm, index);
1272 gop.value_ptr.flags = symbol.flags;
1273 continue;
1274 }
1275 if (ptr.flags.binding == .weak) {
1276 // Keep the existing one.
1277 continue;
1278 }
1279 var err = try diags.addErrorWithNotes(2);
1280 try err.addMsg("symbol collision: {s}", .{name.slice(wasm)});
1281 gop.value_ptr.source_location.addNote(&err, "exported here", .{});
1282 source_location.addNote(&err, "exported here", .{});
1283 continue;
1284 } else {
1285 gop.value_ptr.* = .{
1286 .flags = symbol.flags,
1287 .source_location = source_location,
1288 .resolution = .fromObjectDataIndex(wasm, index),
1289 };
1290 }
1291 },
1292 .section => |i| {
1293 // Name is provided by the section directly; symbol table does not have it.
1294 //const ptr = i.ptr(wasm);
1295 //ptr.flags = symbol.flags;
1296 _ = i;
1297 if (symbol.flags.undefined and symbol.flags.binding == .local) {
1298 const name = symbol.name.slice(wasm).?;
1299 diags.addParseError(path, "local symbol '{s}' references import", .{name});
1300 }
1301 },
1302 };
7131303
714 parser.object.symtable = try symbols.toOwnedSlice();1304 // Apply export section info. This is done after the symbol table above so
1305 // that the symbol table can take precedence, overriding the export name.
1306 for (ss.exports.items) |*exp| {
1307 switch (exp.pointee) {
1308 inline .function, .table, .memory, .global => |index| {
1309 const ptr = index.ptr(wasm);
1310 ptr.name = exp.name.toOptional();
1311 ptr.flags.exported = true;
715 },1312 },
716 }1313 }
717 }1314 }
7181315
719 /// Parses the symbol information based on its kind,1316 // Apply segment_info.
720 /// requires access to `Object` to find the name of a symbol when it's1317 const data_segments = wasm.object_data_segments.items[data_segment_start..];
721 /// an import and flag `WASM_SYM_EXPLICIT_NAME` is not set.1318 if (data_segments.len != ss.segment_info.items.len) {
722 fn parseSymbol(parser: *Parser, gpa: Allocator, reader: anytype) !Symbol {1319 return diags.failParse(path, "expected {d} segment_info entries; found {d}", .{
723 const wasm = parser.wasm;1320 data_segments.len, ss.segment_info.items.len,
724 const tag: Symbol.Tag = @enumFromInt(try leb.readUleb128(u8, reader));1321 });
725 const flags = try leb.readUleb128(u32, reader);1322 }
726 var symbol: Symbol = .{1323 for (data_segments, ss.segment_info.items) |*data, info| {
727 .flags = flags,1324 data.name = info.name.toOptional();
728 .tag = tag,1325 data.flags = .{
729 .name = undefined,1326 .is_passive = data.flags.is_passive,
730 .index = undefined,1327 .strings = info.flags.strings,
731 .virtual_address = undefined,1328 .tls = info.flags.tls,
1329 .retain = info.flags.retain,
1330 .alignment = info.flags.alignment,
732 };1331 };
1332 }
7331333
734 switch (tag) {1334 // Check for indirect function table in case of an MVP object file.
735 .data => {1335 legacy_indirect_function_table: {
736 const name_len = try leb.readUleb128(u32, reader);1336 // If there is a symbol for each import table, this is not a legacy object file.
737 const name = try gpa.alloc(u8, name_len);1337 if (ss.table_imports.items.len == table_import_symbol_count) break :legacy_indirect_function_table;
738 defer gpa.free(name);1338 if (table_import_symbol_count != 0) {
739 try reader.readNoEof(name);1339 return diags.failParse(path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
740 symbol.name = try wasm.internString(name);1340 ss.table_imports.items.len, table_import_symbol_count,
7411341 });
742 // Data symbols only have the following fields if the symbol is defined
743 if (symbol.isDefined()) {
744 symbol.index = try leb.readUleb128(u32, reader);
745 // @TODO: We should verify those values
746 _ = try leb.readUleb128(u32, reader);
747 _ = try leb.readUleb128(u32, reader);
748 }
749 },
750 .section => {
751 symbol.index = try leb.readUleb128(u32, reader);
752 const section_data = parser.object.relocatable_data.get(.custom).?;
753 for (section_data) |*data| {
754 if (data.section_index == symbol.index) {
755 symbol.name = wasm.castToString(data.index);
756 data.represented = true;
757 break;
758 }
759 }
760 },
761 else => {
762 symbol.index = try leb.readUleb128(u32, reader);
763 const is_undefined = symbol.isUndefined();
764 const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME);
765 symbol.name = if (!is_undefined or (is_undefined and explicit_name)) name: {
766 const name_len = try leb.readUleb128(u32, reader);
767 const name = try gpa.alloc(u8, name_len);
768 defer gpa.free(name);
769 try reader.readNoEof(name);
770 break :name try wasm.internString(name);
771 } else parser.object.findImport(symbol).name;
772 },
773 }1342 }
774 return symbol;1343 // MVP object files cannot have any table definitions, only imports
1344 // (for the indirect function table).
1345 const tables = wasm.object_tables.items[tables_start..];
1346 if (tables.len > 0) {
1347 return diags.failParse(path, "table definition without representing table symbols", .{});
1348 }
1349 if (ss.table_imports.items.len != 1) {
1350 return diags.failParse(path, "found more than one table import, but no representing table symbols", .{});
1351 }
1352 const table_import_name = ss.table_imports.items[0].name;
1353 if (table_import_name != wasm.preloaded_strings.__indirect_function_table) {
1354 return diags.failParse(path, "non-indirect function table import '{s}' is missing a corresponding symbol", .{
1355 table_import_name.slice(wasm),
1356 });
1357 }
1358 const ptr = wasm.object_table_imports.getPtr(table_import_name).?;
1359 ptr.flags = .{
1360 .undefined = true,
1361 .no_strip = true,
1362 };
1363 }
1364
1365 for (wasm.object_init_funcs.items[init_funcs_start..]) |init_func| {
1366 const func = init_func.function_index.ptr(wasm);
1367 const params = func.type_index.ptr(wasm).params.slice(wasm);
1368 if (params.len != 0) diags.addError("constructor function '{s}' has non-empty parameter list", .{
1369 func.name.slice(wasm).?,
1370 });
775 }1371 }
776};
7771372
778/// First reads the count from the reader and then allocate1373 const functions_len: u32 = @intCast(wasm.object_functions.items.len - functions_start);
779/// a slice of ptr child's element type.1374 if (functions_len > 0 and code_section_index == null)
780fn readVec(ptr: anytype, reader: anytype, gpa: Allocator) ![]ElementType(@TypeOf(ptr)) {1375 return diags.failParse(path, "code section missing ({d} functions)", .{functions_len});
781 const len = try readLeb(u32, reader);1376
782 const slice = try gpa.alloc(ElementType(@TypeOf(ptr)), len);1377 return .{
783 ptr.* = slice;1378 .version = version,
784 return slice;1379 .path = path,
1380 .archive_member_name = try wasm.internOptionalString(archive_member_name),
1381 .start_function = start_function,
1382 .features = features,
1383 .functions = .{
1384 .off = functions_start,
1385 .len = functions_len,
1386 },
1387 .function_imports = .{
1388 .off = function_imports_start,
1389 .len = @intCast(wasm.object_function_imports.entries.len - function_imports_start),
1390 },
1391 .global_imports = .{
1392 .off = global_imports_start,
1393 .len = @intCast(wasm.object_global_imports.entries.len - global_imports_start),
1394 },
1395 .table_imports = .{
1396 .off = table_imports_start,
1397 .len = @intCast(wasm.object_table_imports.entries.len - table_imports_start),
1398 },
1399 .data_imports = .{
1400 .off = data_imports_start,
1401 .len = @intCast(wasm.object_data_imports.entries.len - data_imports_start),
1402 },
1403 .init_funcs = .{
1404 .off = init_funcs_start,
1405 .len = @intCast(wasm.object_init_funcs.items.len - init_funcs_start),
1406 },
1407 .comdats = .{
1408 .off = comdats_start,
1409 .len = @intCast(wasm.object_comdats.items.len - comdats_start),
1410 },
1411 .custom_segments = .{
1412 .off = custom_segment_start,
1413 .len = @intCast(wasm.object_custom_segments.entries.len - custom_segment_start),
1414 },
1415 .code_section_index = code_section_index,
1416 .global_section_index = global_section_index,
1417 .data_section_index = data_section_index,
1418 .is_included = must_link,
1419 };
785}1420}
7861421
787fn ElementType(comptime ptr: type) type {1422/// Based on the "features" custom section, parses it into a list of
788 return meta.Elem(meta.Child(ptr));1423/// features that tell the linker what features were enabled and may be mandatory
1424/// to be able to link.
1425fn parseFeatures(
1426 wasm: *Wasm,
1427 bytes: []const u8,
1428 start_pos: usize,
1429 path: Path,
1430) error{ OutOfMemory, LinkFailure }!struct { Wasm.Feature.Set, usize } {
1431 const gpa = wasm.base.comp.gpa;
1432 const diags = &wasm.base.comp.link_diags;
1433 const features_len, var pos = readLeb(u32, bytes, start_pos);
1434 // This temporary allocation could be avoided by using the string_bytes buffer as a scratch space.
1435 const feature_buffer = try gpa.alloc(Wasm.Feature, features_len);
1436 defer gpa.free(feature_buffer);
1437 for (feature_buffer) |*feature| {
1438 const prefix: Wasm.Feature.Prefix = switch (bytes[pos]) {
1439 '-' => .@"-",
1440 '+' => .@"+",
1441 '=' => .@"=",
1442 else => |b| return diags.failParse(path, "invalid feature prefix: 0x{x}", .{b}),
1443 };
1444 pos += 1;
1445 const name, pos = readBytes(bytes, pos);
1446 const tag = std.meta.stringToEnum(Wasm.Feature.Tag, name) orelse {
1447 return diags.failParse(path, "unrecognized wasm feature in object: {s}", .{name});
1448 };
1449 feature.* = .{
1450 .prefix = prefix,
1451 .tag = tag,
1452 };
1453 }
1454 std.mem.sortUnstable(Wasm.Feature, feature_buffer, {}, Wasm.Feature.lessThan);
1455
1456 return .{
1457 .fromString(try wasm.internString(@ptrCast(feature_buffer))),
1458 pos,
1459 };
789}1460}
7901461
791/// Uses either `readIleb128` or `readUleb128` depending on the1462fn readLeb(comptime T: type, bytes: []const u8, pos: usize) struct { T, usize } {
792/// signedness of the given type `T`.1463 var fbr = std.io.fixedBufferStream(bytes[pos..]);
793/// Asserts `T` is an integer.1464 return .{
794fn readLeb(comptime T: type, reader: anytype) !T {1465 switch (@typeInfo(T).int.signedness) {
795 return switch (@typeInfo(T).int.signedness) {1466 .signed => std.leb.readIleb128(T, fbr.reader()) catch unreachable,
796 .signed => try leb.readIleb128(T, reader),1467 .unsigned => std.leb.readUleb128(T, fbr.reader()) catch unreachable,
797 .unsigned => try leb.readUleb128(T, reader),1468 },
1469 pos + fbr.pos,
798 };1470 };
799}1471}
8001472
801/// Reads an enum type from the given reader.1473fn readBytes(bytes: []const u8, start_pos: usize) struct { []const u8, usize } {
802/// Asserts `T` is an enum1474 const len, const pos = readLeb(u32, bytes, start_pos);
803fn readEnum(comptime T: type, reader: anytype) !T {1475 return .{
804 switch (@typeInfo(T)) {1476 bytes[pos..][0..len],
805 .@"enum" => |enum_type| return @as(T, @enumFromInt(try readLeb(enum_type.tag_type, reader))),1477 pos + len,
806 else => @compileError("T must be an enum. Instead was given type " ++ @typeName(T)),1478 };
807 }
808}1479}
8091480
810fn readLimits(reader: anytype) !std.wasm.Limits {1481fn readEnum(comptime T: type, bytes: []const u8, pos: usize) struct { T, usize } {
811 const flags = try reader.readByte();1482 const Tag = @typeInfo(T).@"enum".tag_type;
812 const min = try readLeb(u32, reader);1483 const int, const new_pos = readLeb(Tag, bytes, pos);
813 var limits: std.wasm.Limits = .{1484 return .{ @enumFromInt(int), new_pos };
1485}
1486
1487fn readLimits(bytes: []const u8, start_pos: usize) struct { std.wasm.Limits, usize } {
1488 const flags: std.wasm.Limits.Flags = @bitCast(bytes[start_pos]);
1489 const min, const max_pos = readLeb(u32, bytes, start_pos + 1);
1490 const max, const end_pos = if (flags.has_max) readLeb(u32, bytes, max_pos) else .{ 0, max_pos };
1491 return .{ .{
814 .flags = flags,1492 .flags = flags,
815 .min = min,1493 .min = min,
816 .max = undefined,1494 .max = max,
817 };1495 }, end_pos };
818 if (limits.hasFlag(.WASM_LIMITS_FLAG_HAS_MAX)) {
819 limits.max = try readLeb(u32, reader);
820 }
821 return limits;
822}1496}
8231497
824fn readInit(reader: anytype) !std.wasm.InitExpression {1498fn readInit(wasm: *Wasm, bytes: []const u8, pos: usize) !struct { Wasm.Expr, usize } {
825 const opcode = try reader.readByte();1499 const end_pos = try skipInit(bytes, pos); // one after the end opcode
826 const init_expr: std.wasm.InitExpression = switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) {1500 return .{ try wasm.addExpr(bytes[pos..end_pos]), end_pos };
827 .i32_const => .{ .i32_const = try readLeb(i32, reader) },1501}
828 .global_get => .{ .global_get = try readLeb(u32, reader) },
829 else => @panic("TODO: initexpression for other opcodes"),
830 };
8311502
832 if ((try readEnum(std.wasm.Opcode, reader)) != .end) return error.MissingEndForExpression;1503pub fn exprEndPos(bytes: []const u8, pos: usize) error{InvalidInitOpcode}!usize {
833 return init_expr;1504 const opcode = bytes[pos];
1505 return switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) {
1506 .i32_const => readLeb(i32, bytes, pos + 1)[1],
1507 .i64_const => readLeb(i64, bytes, pos + 1)[1],
1508 .f32_const => pos + 5,
1509 .f64_const => pos + 9,
1510 .global_get => readLeb(u32, bytes, pos + 1)[1],
1511 else => return error.InvalidInitOpcode,
1512 };
834}1513}
8351514
836fn assertEnd(reader: anytype) !void {1515fn skipInit(bytes: []const u8, pos: usize) !usize {
837 var buf: [1]u8 = undefined;1516 const end_pos = try exprEndPos(bytes, pos);
838 const len = try reader.read(&buf);1517 const op, const final_pos = readEnum(std.wasm.Opcode, bytes, end_pos);
839 if (len != 0) return error.MalformedSection;1518 if (op != .end) return error.InitExprMissingEnd;
840 if (reader.context.bytes_left != 0) return error.MalformedSection;1519 return final_pos;
841}1520}
src/link/Wasm/Symbol.zig deleted-210
...@@ -1,210 +0,0 @@
1//! Represents a WebAssembly symbol. Containing all of its properties,
2//! as well as providing helper methods to determine its functionality
3//! and how it will/must be linked.
4//! The name of the symbol can be found by providing the offset, found
5//! on the `name` field, to a string table in the wasm binary or object file.
6
7/// Bitfield containings flags for a symbol
8/// Can contain any of the flags defined in `Flag`
9flags: u32,
10/// Symbol name, when the symbol is undefined the name will be taken from the import.
11/// Note: This is an index into the wasm string table.
12name: wasm.String,
13/// Index into the list of objects based on set `tag`
14/// NOTE: This will be set to `undefined` when `tag` is `data`
15/// and the symbol is undefined.
16index: u32,
17/// Represents the kind of the symbol, such as a function or global.
18tag: Tag,
19/// Contains the virtual address of the symbol, relative to the start of its section.
20/// This differs from the offset of an `Atom` which is relative to the start of a segment.
21virtual_address: u32,
22
23/// Represents a symbol index where `null` represents an invalid index.
24pub const Index = enum(u32) {
25 null,
26 _,
27};
28
29pub const Tag = enum {
30 function,
31 data,
32 global,
33 section,
34 event,
35 table,
36 /// synthetic kind used by the wasm linker during incremental compilation
37 /// to notate a symbol has been freed, but still lives in the symbol list.
38 dead,
39 undefined,
40
41 /// From a given symbol tag, returns the `ExternalType`
42 /// Asserts the given tag can be represented as an external type.
43 pub fn externalType(tag: Tag) std.wasm.ExternalKind {
44 return switch (tag) {
45 .function => .function,
46 .global => .global,
47 .data => unreachable, // Data symbols will generate a global
48 .section => unreachable, // Not an external type
49 .event => unreachable, // Not an external type
50 .dead => unreachable, // Dead symbols should not be referenced
51 .undefined => unreachable,
52 .table => .table,
53 };
54 }
55};
56
57pub const Flag = enum(u32) {
58 /// Indicates a weak symbol.
59 /// When linking multiple modules defining the same symbol, all weak definitions are discarded
60 /// in favourite of the strong definition. When no strong definition exists, all weak but one definition is discarded.
61 /// If multiple definitions remain, we get an error: symbol collision.
62 WASM_SYM_BINDING_WEAK = 0x1,
63 /// Indicates a local, non-exported, non-module-linked symbol.
64 /// The names of local symbols are not required to be unique, unlike non-local symbols.
65 WASM_SYM_BINDING_LOCAL = 0x2,
66 /// Represents the binding of a symbol, indicating if it's local or not, and weak or not.
67 WASM_SYM_BINDING_MASK = 0x3,
68 /// Indicates a hidden symbol. Hidden symbols will not be exported to the link result, but may
69 /// link to other modules.
70 WASM_SYM_VISIBILITY_HIDDEN = 0x4,
71 /// Indicates an undefined symbol. For non-data symbols, this must match whether the symbol is
72 /// an import or is defined. For data symbols however, determines whether a segment is specified.
73 WASM_SYM_UNDEFINED = 0x10,
74 /// Indicates a symbol of which its intention is to be exported from the wasm module to the host environment.
75 /// This differs from the visibility flag as this flag affects the static linker.
76 WASM_SYM_EXPORTED = 0x20,
77 /// Indicates the symbol uses an explicit symbol name, rather than reusing the name from a wasm import.
78 /// Allows remapping imports from foreign WASM modules into local symbols with a different name.
79 WASM_SYM_EXPLICIT_NAME = 0x40,
80 /// Indicates the symbol is to be included in the linker output, regardless of whether it is used or has any references to it.
81 WASM_SYM_NO_STRIP = 0x80,
82 /// Indicates a symbol is TLS
83 WASM_SYM_TLS = 0x100,
84 /// Zig specific flag. Uses the most significant bit of the flag to annotate whether a symbol is
85 /// alive or not. Dead symbols are allowed to be garbage collected.
86 alive = 0x80000000,
87};
88
89/// Verifies if the given symbol should be imported from the
90/// host environment or not
91pub fn requiresImport(symbol: Symbol) bool {
92 if (symbol.tag == .data) return false;
93 if (!symbol.isUndefined()) return false;
94 if (symbol.isWeak()) return false;
95 // if (symbol.isDefined() and symbol.isWeak()) return true; //TODO: Only when building shared lib
96
97 return true;
98}
99
100/// Marks a symbol as 'alive', ensuring the garbage collector will not collect the trash.
101pub fn mark(symbol: *Symbol) void {
102 symbol.flags |= @intFromEnum(Flag.alive);
103}
104
105pub fn unmark(symbol: *Symbol) void {
106 symbol.flags &= ~@intFromEnum(Flag.alive);
107}
108
109pub fn isAlive(symbol: Symbol) bool {
110 return symbol.flags & @intFromEnum(Flag.alive) != 0;
111}
112
113pub fn isDead(symbol: Symbol) bool {
114 return symbol.flags & @intFromEnum(Flag.alive) == 0;
115}
116
117pub fn isTLS(symbol: Symbol) bool {
118 return symbol.flags & @intFromEnum(Flag.WASM_SYM_TLS) != 0;
119}
120
121pub fn hasFlag(symbol: Symbol, flag: Flag) bool {
122 return symbol.flags & @intFromEnum(flag) != 0;
123}
124
125pub fn setFlag(symbol: *Symbol, flag: Flag) void {
126 symbol.flags |= @intFromEnum(flag);
127}
128
129pub fn isUndefined(symbol: Symbol) bool {
130 return symbol.flags & @intFromEnum(Flag.WASM_SYM_UNDEFINED) != 0;
131}
132
133pub fn setUndefined(symbol: *Symbol, is_undefined: bool) void {
134 if (is_undefined) {
135 symbol.setFlag(.WASM_SYM_UNDEFINED);
136 } else {
137 symbol.flags &= ~@intFromEnum(Flag.WASM_SYM_UNDEFINED);
138 }
139}
140
141pub fn setGlobal(symbol: *Symbol, is_global: bool) void {
142 if (is_global) {
143 symbol.flags &= ~@intFromEnum(Flag.WASM_SYM_BINDING_LOCAL);
144 } else {
145 symbol.setFlag(.WASM_SYM_BINDING_LOCAL);
146 }
147}
148
149pub fn isDefined(symbol: Symbol) bool {
150 return !symbol.isUndefined();
151}
152
153pub fn isVisible(symbol: Symbol) bool {
154 return symbol.flags & @intFromEnum(Flag.WASM_SYM_VISIBILITY_HIDDEN) == 0;
155}
156
157pub fn isLocal(symbol: Symbol) bool {
158 return symbol.flags & @intFromEnum(Flag.WASM_SYM_BINDING_LOCAL) != 0;
159}
160
161pub fn isGlobal(symbol: Symbol) bool {
162 return symbol.flags & @intFromEnum(Flag.WASM_SYM_BINDING_LOCAL) == 0;
163}
164
165pub fn isHidden(symbol: Symbol) bool {
166 return symbol.flags & @intFromEnum(Flag.WASM_SYM_VISIBILITY_HIDDEN) != 0;
167}
168
169pub fn isNoStrip(symbol: Symbol) bool {
170 return symbol.flags & @intFromEnum(Flag.WASM_SYM_NO_STRIP) != 0;
171}
172
173pub fn isExported(symbol: Symbol, is_dynamic: bool) bool {
174 if (symbol.isUndefined() or symbol.isLocal()) return false;
175 if (is_dynamic and symbol.isVisible()) return true;
176 return symbol.hasFlag(.WASM_SYM_EXPORTED);
177}
178
179pub fn isWeak(symbol: Symbol) bool {
180 return symbol.flags & @intFromEnum(Flag.WASM_SYM_BINDING_WEAK) != 0;
181}
182
183/// Formats the symbol into human-readable text
184pub fn format(symbol: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
185 _ = fmt;
186 _ = options;
187
188 const kind_fmt: u8 = switch (symbol.tag) {
189 .function => 'F',
190 .data => 'D',
191 .global => 'G',
192 .section => 'S',
193 .event => 'E',
194 .table => 'T',
195 .dead => '-',
196 .undefined => unreachable,
197 };
198 const visible: []const u8 = if (symbol.isVisible()) "yes" else "no";
199 const binding: []const u8 = if (symbol.isLocal()) "local" else "global";
200 const undef: []const u8 = if (symbol.isUndefined()) "undefined" else "";
201
202 try writer.print(
203 "{c} binding={s} visible={s} id={d} name_offset={d} {s}",
204 .{ kind_fmt, binding, visible, symbol.index, symbol.name, undef },
205 );
206}
207
208const std = @import("std");
209const Symbol = @This();
210const wasm = @import("../Wasm.zig");
src/link/Wasm/ZigObject.zig deleted-1229
...@@ -1,1229 +0,0 @@
1//! ZigObject encapsulates the state of the incrementally compiled Zig module.
2//! It stores the associated input local and global symbols, allocated atoms,
3//! and any relocations that may have been emitted.
4
5/// For error reporting purposes only.
6path: Path,
7/// Map of all `Nav` that are currently alive.
8/// Each index maps to the corresponding `NavInfo`.
9navs: std.AutoHashMapUnmanaged(InternPool.Nav.Index, NavInfo) = .empty,
10/// List of function type signatures for this Zig module.
11func_types: std.ArrayListUnmanaged(std.wasm.Type) = .empty,
12/// List of `std.wasm.Func`. Each entry contains the function signature,
13/// rather than the actual body.
14functions: std.ArrayListUnmanaged(std.wasm.Func) = .empty,
15/// List of indexes pointing to an entry within the `functions` list which has been removed.
16functions_free_list: std.ArrayListUnmanaged(u32) = .empty,
17/// Map of symbol locations, represented by its `Wasm.Import`.
18imports: std.AutoHashMapUnmanaged(Symbol.Index, Wasm.Import) = .empty,
19/// List of WebAssembly globals.
20globals: std.ArrayListUnmanaged(std.wasm.Global) = .empty,
21/// Mapping between an `Atom` and its type index representing the Wasm
22/// type of the function signature.
23atom_types: std.AutoHashMapUnmanaged(Atom.Index, u32) = .empty,
24/// List of all symbols generated by Zig code.
25symbols: std.ArrayListUnmanaged(Symbol) = .empty,
26/// Map from symbol name to their index into the `symbols` list.
27global_syms: std.AutoHashMapUnmanaged(Wasm.String, Symbol.Index) = .empty,
28/// List of symbol indexes which are free to be used.
29symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .empty,
30/// Extra metadata about the linking section, such as alignment of segments and their name.
31segment_info: std.ArrayListUnmanaged(Wasm.NamedSegment) = .empty,
32/// List of indexes which contain a free slot in the `segment_info` list.
33segment_free_list: std.ArrayListUnmanaged(u32) = .empty,
34/// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index.
35uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .empty,
36/// List of atom indexes of functions that are generated by the backend.
37synthetic_functions: std.ArrayListUnmanaged(Atom.Index) = .empty,
38/// Represents the symbol index of the error name table
39/// When this is `null`, no code references an error using runtime `@errorName`.
40/// During initializion, a symbol with corresponding atom will be created that is
41/// used to perform relocations to the pointer of this table.
42/// The actual table is populated during `flush`.
43error_table_symbol: Symbol.Index = .null,
44/// Atom index of the table of symbol names. This is stored so we can clean up the atom.
45error_names_atom: Atom.Index = .null,
46/// Amount of functions in the `import` sections.
47imported_functions_count: u32 = 0,
48/// Amount of globals in the `import` section.
49imported_globals_count: u32 = 0,
50/// Symbol index representing the stack pointer. This will be set upon initializion
51/// of a new `ZigObject`. Codegen will make calls into this to create relocations for
52/// this symbol each time the stack pointer is moved.
53stack_pointer_sym: Symbol.Index,
54/// Debug information for the Zig module.
55dwarf: ?Dwarf = null,
56// Debug section atoms. These are only set when the current compilation
57// unit contains Zig code. The lifetime of these atoms are extended
58// until the end of the compiler's lifetime. Meaning they're not freed
59// during `flush()` in incremental-mode.
60debug_info_atom: ?Atom.Index = null,
61debug_line_atom: ?Atom.Index = null,
62debug_loc_atom: ?Atom.Index = null,
63debug_ranges_atom: ?Atom.Index = null,
64debug_abbrev_atom: ?Atom.Index = null,
65debug_str_atom: ?Atom.Index = null,
66debug_pubnames_atom: ?Atom.Index = null,
67debug_pubtypes_atom: ?Atom.Index = null,
68/// The index of the segment representing the custom '.debug_info' section.
69debug_info_index: ?u32 = null,
70/// The index of the segment representing the custom '.debug_line' section.
71debug_line_index: ?u32 = null,
72/// The index of the segment representing the custom '.debug_loc' section.
73debug_loc_index: ?u32 = null,
74/// The index of the segment representing the custom '.debug_ranges' section.
75debug_ranges_index: ?u32 = null,
76/// The index of the segment representing the custom '.debug_pubnames' section.
77debug_pubnames_index: ?u32 = null,
78/// The index of the segment representing the custom '.debug_pubtypes' section.
79debug_pubtypes_index: ?u32 = null,
80/// The index of the segment representing the custom '.debug_pubtypes' section.
81debug_str_index: ?u32 = null,
82/// The index of the segment representing the custom '.debug_pubtypes' section.
83debug_abbrev_index: ?u32 = null,
84
85const NavInfo = struct {
86 atom: Atom.Index = .null,
87 exports: std.ArrayListUnmanaged(Symbol.Index) = .empty,
88
89 fn @"export"(ni: NavInfo, zo: *const ZigObject, name: Wasm.String) ?Symbol.Index {
90 for (ni.exports.items) |sym_index| {
91 if (zo.symbol(sym_index).name == name) return sym_index;
92 }
93 return null;
94 }
95
96 fn appendExport(ni: *NavInfo, gpa: std.mem.Allocator, sym_index: Symbol.Index) !void {
97 return ni.exports.append(gpa, sym_index);
98 }
99
100 fn deleteExport(ni: *NavInfo, sym_index: Symbol.Index) void {
101 for (ni.exports.items, 0..) |idx, index| {
102 if (idx == sym_index) {
103 _ = ni.exports.swapRemove(index);
104 return;
105 }
106 }
107 unreachable; // invalid sym_index
108 }
109};
110
111/// Initializes the `ZigObject` with initial symbols.
112pub fn init(zig_object: *ZigObject, wasm: *Wasm) !void {
113 // Initialize an undefined global with the name __stack_pointer. Codegen will use
114 // this to generate relocations when moving the stack pointer. This symbol will be
115 // resolved automatically by the final linking stage.
116 try zig_object.createStackPointer(wasm);
117
118 // TODO: Initialize debug information when we reimplement Dwarf support.
119}
120
121fn createStackPointer(zig_object: *ZigObject, wasm: *Wasm) !void {
122 const gpa = wasm.base.comp.gpa;
123 const sym_index = try zig_object.getGlobalSymbol(gpa, wasm.preloaded_strings.__stack_pointer);
124 const sym = zig_object.symbol(sym_index);
125 sym.index = zig_object.imported_globals_count;
126 sym.tag = .global;
127 const is_wasm32 = wasm.base.comp.root_mod.resolved_target.result.cpu.arch == .wasm32;
128 try zig_object.imports.putNoClobber(gpa, sym_index, .{
129 .name = sym.name,
130 .module_name = wasm.host_name,
131 .kind = .{ .global = .{ .valtype = if (is_wasm32) .i32 else .i64, .mutable = true } },
132 });
133 zig_object.imported_globals_count += 1;
134 zig_object.stack_pointer_sym = sym_index;
135}
136
137pub fn symbol(zig_object: *const ZigObject, index: Symbol.Index) *Symbol {
138 return &zig_object.symbols.items[@intFromEnum(index)];
139}
140
141/// Frees and invalidates all memory of the incrementally compiled Zig module.
142/// It is illegal behavior to access the `ZigObject` after calling `deinit`.
143pub fn deinit(zig_object: *ZigObject, wasm: *Wasm) void {
144 const gpa = wasm.base.comp.gpa;
145 for (zig_object.segment_info.items) |segment_info| {
146 gpa.free(segment_info.name);
147 }
148
149 {
150 var it = zig_object.navs.valueIterator();
151 while (it.next()) |nav_info| {
152 const atom = wasm.getAtomPtr(nav_info.atom);
153 for (atom.locals.items) |local_index| {
154 const local_atom = wasm.getAtomPtr(local_index);
155 local_atom.deinit(gpa);
156 }
157 atom.deinit(gpa);
158 nav_info.exports.deinit(gpa);
159 }
160 }
161 {
162 for (zig_object.uavs.values()) |atom_index| {
163 const atom = wasm.getAtomPtr(atom_index);
164 for (atom.locals.items) |local_index| {
165 const local_atom = wasm.getAtomPtr(local_index);
166 local_atom.deinit(gpa);
167 }
168 atom.deinit(gpa);
169 }
170 }
171 if (zig_object.global_syms.get(wasm.preloaded_strings.__zig_errors_len)) |sym_index| {
172 const atom_index = wasm.symbol_atom.get(.{ .file = .zig_object, .index = sym_index }).?;
173 wasm.getAtomPtr(atom_index).deinit(gpa);
174 }
175 if (wasm.symbol_atom.get(.{ .file = .zig_object, .index = zig_object.error_table_symbol })) |atom_index| {
176 const atom = wasm.getAtomPtr(atom_index);
177 atom.deinit(gpa);
178 }
179 for (zig_object.synthetic_functions.items) |atom_index| {
180 const atom = wasm.getAtomPtr(atom_index);
181 atom.deinit(gpa);
182 }
183 zig_object.synthetic_functions.deinit(gpa);
184 for (zig_object.func_types.items) |*ty| {
185 ty.deinit(gpa);
186 }
187 if (zig_object.error_names_atom != .null) {
188 const atom = wasm.getAtomPtr(zig_object.error_names_atom);
189 atom.deinit(gpa);
190 }
191 zig_object.global_syms.deinit(gpa);
192 zig_object.func_types.deinit(gpa);
193 zig_object.atom_types.deinit(gpa);
194 zig_object.functions.deinit(gpa);
195 zig_object.imports.deinit(gpa);
196 zig_object.navs.deinit(gpa);
197 zig_object.uavs.deinit(gpa);
198 zig_object.symbols.deinit(gpa);
199 zig_object.symbols_free_list.deinit(gpa);
200 zig_object.segment_info.deinit(gpa);
201 zig_object.segment_free_list.deinit(gpa);
202
203 if (zig_object.dwarf) |*dwarf| {
204 dwarf.deinit();
205 }
206 gpa.free(zig_object.path.sub_path);
207 zig_object.* = undefined;
208}
209
210/// Allocates a new symbol and returns its index.
211/// Will re-use slots when a symbol was freed at an earlier stage.
212pub fn allocateSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator) !Symbol.Index {
213 try zig_object.symbols.ensureUnusedCapacity(gpa, 1);
214 const sym: Symbol = .{
215 .name = undefined, // will be set after updateDecl as well as during atom creation for decls
216 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
217 .tag = .undefined, // will be set after updateDecl
218 .index = std.math.maxInt(u32), // will be set during atom parsing
219 .virtual_address = std.math.maxInt(u32), // will be set during atom allocation
220 };
221 if (zig_object.symbols_free_list.popOrNull()) |index| {
222 zig_object.symbols.items[@intFromEnum(index)] = sym;
223 return index;
224 }
225 const index: Symbol.Index = @enumFromInt(zig_object.symbols.items.len);
226 zig_object.symbols.appendAssumeCapacity(sym);
227 return index;
228}
229
230// Generate code for the `Nav`, storing it in memory to be later written to
231// the file on flush().
232pub fn updateNav(
233 zig_object: *ZigObject,
234 wasm: *Wasm,
235 pt: Zcu.PerThread,
236 nav_index: InternPool.Nav.Index,
237) !void {
238 const zcu = pt.zcu;
239 const ip = &zcu.intern_pool;
240 const nav = ip.getNav(nav_index);
241
242 const nav_val = zcu.navValue(nav_index);
243 const is_extern, const lib_name, const nav_init = switch (ip.indexToKey(nav_val.toIntern())) {
244 .variable => |variable| .{ false, .none, Value.fromInterned(variable.init) },
245 .func => return,
246 .@"extern" => |@"extern"| if (ip.isFunctionType(nav.typeOf(ip)))
247 return
248 else
249 .{ true, @"extern".lib_name, nav_val },
250 else => .{ false, .none, nav_val },
251 };
252
253 if (nav_init.typeOf(zcu).hasRuntimeBits(zcu)) {
254 const gpa = wasm.base.comp.gpa;
255 const atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
256 const atom = wasm.getAtomPtr(atom_index);
257 atom.clear();
258
259 if (is_extern)
260 return zig_object.addOrUpdateImport(wasm, nav.name.toSlice(ip), atom.sym_index, lib_name.toSlice(ip), null);
261
262 var code_writer = std.ArrayList(u8).init(gpa);
263 defer code_writer.deinit();
264
265 const res = try codegen.generateSymbol(
266 &wasm.base,
267 pt,
268 zcu.navSrcLoc(nav_index),
269 nav_init,
270 &code_writer,
271 .{ .atom_index = @intFromEnum(atom.sym_index) },
272 );
273
274 const code = switch (res) {
275 .ok => code_writer.items,
276 .fail => |em| {
277 try zcu.failed_codegen.put(zcu.gpa, nav_index, em);
278 return;
279 },
280 };
281
282 try zig_object.finishUpdateNav(wasm, pt, nav_index, code);
283 }
284}
285
286pub fn updateFunc(
287 zig_object: *ZigObject,
288 wasm: *Wasm,
289 pt: Zcu.PerThread,
290 func_index: InternPool.Index,
291 air: Air,
292 liveness: Liveness,
293) !void {
294 const zcu = pt.zcu;
295 const gpa = zcu.gpa;
296 const func = pt.zcu.funcInfo(func_index);
297 const atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, func.owner_nav);
298 const atom = wasm.getAtomPtr(atom_index);
299 atom.clear();
300
301 var code_writer = std.ArrayList(u8).init(gpa);
302 defer code_writer.deinit();
303 const result = try codegen.generateFunction(
304 &wasm.base,
305 pt,
306 zcu.navSrcLoc(func.owner_nav),
307 func_index,
308 air,
309 liveness,
310 &code_writer,
311 .none,
312 );
313
314 const code = switch (result) {
315 .ok => code_writer.items,
316 .fail => |em| {
317 try pt.zcu.failed_codegen.put(gpa, func.owner_nav, em);
318 return;
319 },
320 };
321
322 return zig_object.finishUpdateNav(wasm, pt, func.owner_nav, code);
323}
324
325fn finishUpdateNav(
326 zig_object: *ZigObject,
327 wasm: *Wasm,
328 pt: Zcu.PerThread,
329 nav_index: InternPool.Nav.Index,
330 code: []const u8,
331) !void {
332 const zcu = pt.zcu;
333 const ip = &zcu.intern_pool;
334 const gpa = zcu.gpa;
335 const nav = ip.getNav(nav_index);
336 const nav_val = zcu.navValue(nav_index);
337 const nav_info = zig_object.navs.get(nav_index).?;
338 const atom_index = nav_info.atom;
339 const atom = wasm.getAtomPtr(atom_index);
340 const sym = zig_object.symbol(atom.sym_index);
341 sym.name = try wasm.internString(nav.fqn.toSlice(ip));
342 try atom.code.appendSlice(gpa, code);
343 atom.size = @intCast(code.len);
344
345 if (ip.isFunctionType(nav.typeOf(ip))) {
346 sym.index = try zig_object.appendFunction(gpa, .{ .type_index = zig_object.atom_types.get(atom_index).? });
347 sym.tag = .function;
348 } else {
349 const is_const, const nav_init = switch (ip.indexToKey(nav_val.toIntern())) {
350 .variable => |variable| .{ false, variable.init },
351 .@"extern" => |@"extern"| .{ @"extern".is_const, .none },
352 else => .{ true, nav_val.toIntern() },
353 };
354 const segment_name = name: {
355 if (is_const) break :name ".rodata.";
356
357 if (nav_init != .none and Value.fromInterned(nav_init).isUndefDeep(zcu)) {
358 break :name switch (zcu.navFileScope(nav_index).mod.optimize_mode) {
359 .Debug, .ReleaseSafe => ".data.",
360 .ReleaseFast, .ReleaseSmall => ".bss.",
361 };
362 }
363 // when the decl is all zeroes, we store the atom in the bss segment,
364 // in all other cases it will be in the data segment.
365 for (atom.code.items) |byte| {
366 if (byte != 0) break :name ".data.";
367 }
368 break :name ".bss.";
369 };
370 if ((wasm.base.isObject() or wasm.base.comp.config.import_memory) and
371 std.mem.startsWith(u8, segment_name, ".bss"))
372 {
373 @memset(atom.code.items, 0);
374 }
375 // Will be freed upon freeing of decl or after cleanup of Wasm binary.
376 const full_segment_name = try std.mem.concat(gpa, u8, &.{
377 segment_name,
378 nav.fqn.toSlice(ip),
379 });
380 errdefer gpa.free(full_segment_name);
381 sym.tag = .data;
382 sym.index = try zig_object.createDataSegment(gpa, full_segment_name, pt.navAlignment(nav_index));
383 }
384 if (code.len == 0) return;
385 atom.alignment = pt.navAlignment(nav_index);
386}
387
388/// Creates and initializes a new segment in the 'Data' section.
389/// Reuses free slots in the list of segments and returns the index.
390fn createDataSegment(
391 zig_object: *ZigObject,
392 gpa: std.mem.Allocator,
393 name: []const u8,
394 alignment: InternPool.Alignment,
395) !u32 {
396 const segment_index: u32 = if (zig_object.segment_free_list.popOrNull()) |index|
397 index
398 else index: {
399 const idx: u32 = @intCast(zig_object.segment_info.items.len);
400 _ = try zig_object.segment_info.addOne(gpa);
401 break :index idx;
402 };
403 zig_object.segment_info.items[segment_index] = .{
404 .alignment = alignment,
405 .flags = 0,
406 .name = name,
407 };
408 return segment_index;
409}
410
411/// For a given `InternPool.Nav.Index` returns its corresponding `Atom.Index`.
412/// When the index was not found, a new `Atom` will be created, and its index will be returned.
413/// The newly created Atom is empty with default fields as specified by `Atom.empty`.
414pub fn getOrCreateAtomForNav(
415 zig_object: *ZigObject,
416 wasm: *Wasm,
417 pt: Zcu.PerThread,
418 nav_index: InternPool.Nav.Index,
419) !Atom.Index {
420 const ip = &pt.zcu.intern_pool;
421 const gpa = pt.zcu.gpa;
422 const gop = try zig_object.navs.getOrPut(gpa, nav_index);
423 if (!gop.found_existing) {
424 const sym_index = try zig_object.allocateSymbol(gpa);
425 gop.value_ptr.* = .{ .atom = try wasm.createAtom(sym_index, .zig_object) };
426 const nav = ip.getNav(nav_index);
427 const sym = zig_object.symbol(sym_index);
428 sym.name = try wasm.internString(nav.fqn.toSlice(ip));
429 }
430 return gop.value_ptr.atom;
431}
432
433pub fn lowerUav(
434 zig_object: *ZigObject,
435 wasm: *Wasm,
436 pt: Zcu.PerThread,
437 uav: InternPool.Index,
438 explicit_alignment: InternPool.Alignment,
439 src_loc: Zcu.LazySrcLoc,
440) !codegen.GenResult {
441 const gpa = wasm.base.comp.gpa;
442 const gop = try zig_object.uavs.getOrPut(gpa, uav);
443 if (!gop.found_existing) {
444 var name_buf: [32]u8 = undefined;
445 const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{
446 @intFromEnum(uav),
447 }) catch unreachable;
448
449 switch (try zig_object.lowerConst(wasm, pt, name, Value.fromInterned(uav), src_loc)) {
450 .ok => |atom_index| zig_object.uavs.values()[gop.index] = atom_index,
451 .fail => |em| return .{ .fail = em },
452 }
453 }
454
455 const atom = wasm.getAtomPtr(zig_object.uavs.values()[gop.index]);
456 atom.alignment = switch (atom.alignment) {
457 .none => explicit_alignment,
458 else => switch (explicit_alignment) {
459 .none => atom.alignment,
460 else => atom.alignment.maxStrict(explicit_alignment),
461 },
462 };
463 return .{ .mcv = .{ .load_symbol = @intFromEnum(atom.sym_index) } };
464}
465
466const LowerConstResult = union(enum) {
467 ok: Atom.Index,
468 fail: *Zcu.ErrorMsg,
469};
470
471fn lowerConst(
472 zig_object: *ZigObject,
473 wasm: *Wasm,
474 pt: Zcu.PerThread,
475 name: []const u8,
476 val: Value,
477 src_loc: Zcu.LazySrcLoc,
478) !LowerConstResult {
479 const gpa = wasm.base.comp.gpa;
480 const zcu = wasm.base.comp.zcu.?;
481
482 const ty = val.typeOf(zcu);
483
484 // Create and initialize a new local symbol and atom
485 const sym_index = try zig_object.allocateSymbol(gpa);
486 const atom_index = try wasm.createAtom(sym_index, .zig_object);
487 var value_bytes = std.ArrayList(u8).init(gpa);
488 defer value_bytes.deinit();
489
490 const code = code: {
491 const atom = wasm.getAtomPtr(atom_index);
492 atom.alignment = ty.abiAlignment(zcu);
493 const segment_name = try std.mem.concat(gpa, u8, &.{ ".rodata.", name });
494 errdefer gpa.free(segment_name);
495 zig_object.symbol(sym_index).* = .{
496 .name = try wasm.internString(name),
497 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
498 .tag = .data,
499 .index = try zig_object.createDataSegment(
500 gpa,
501 segment_name,
502 ty.abiAlignment(zcu),
503 ),
504 .virtual_address = undefined,
505 };
506
507 const result = try codegen.generateSymbol(
508 &wasm.base,
509 pt,
510 src_loc,
511 val,
512 &value_bytes,
513 .{ .atom_index = @intFromEnum(atom.sym_index) },
514 );
515 break :code switch (result) {
516 .ok => value_bytes.items,
517 .fail => |em| {
518 return .{ .fail = em };
519 },
520 };
521 };
522
523 const atom = wasm.getAtomPtr(atom_index);
524 atom.size = @intCast(code.len);
525 try atom.code.appendSlice(gpa, code);
526 return .{ .ok = atom_index };
527}
528
529/// Returns the symbol index of the error name table.
530///
531/// When the symbol does not yet exist, it will create a new one instead.
532pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm: *Wasm, pt: Zcu.PerThread) !Symbol.Index {
533 if (zig_object.error_table_symbol != .null) {
534 return zig_object.error_table_symbol;
535 }
536
537 // no error was referenced yet, so create a new symbol and atom for it
538 // and then return said symbol's index. The final table will be populated
539 // during `flush` when we know all possible error names.
540 const gpa = wasm.base.comp.gpa;
541 const sym_index = try zig_object.allocateSymbol(gpa);
542 const atom_index = try wasm.createAtom(sym_index, .zig_object);
543 const atom = wasm.getAtomPtr(atom_index);
544 const slice_ty = Type.slice_const_u8_sentinel_0;
545 atom.alignment = slice_ty.abiAlignment(pt.zcu);
546
547 const segment_name = try gpa.dupe(u8, ".rodata.__zig_err_name_table");
548 const sym = zig_object.symbol(sym_index);
549 sym.* = .{
550 .name = wasm.preloaded_strings.__zig_err_name_table,
551 .tag = .data,
552 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
553 .index = try zig_object.createDataSegment(gpa, segment_name, atom.alignment),
554 .virtual_address = undefined,
555 };
556
557 log.debug("Error name table was created with symbol index: ({d})", .{@intFromEnum(sym_index)});
558 zig_object.error_table_symbol = sym_index;
559 return sym_index;
560}
561
562/// Populates the error name table, when `error_table_symbol` is not null.
563///
564/// This creates a table that consists of pointers and length to each error name.
565/// The table is what is being pointed to within the runtime bodies that are generated.
566fn populateErrorNameTable(zig_object: *ZigObject, wasm: *Wasm, tid: Zcu.PerThread.Id) !void {
567 if (zig_object.error_table_symbol == .null) return;
568 const gpa = wasm.base.comp.gpa;
569 const atom_index = wasm.symbol_atom.get(.{ .file = .zig_object, .index = zig_object.error_table_symbol }).?;
570
571 // Rather than creating a symbol for each individual error name,
572 // we create a symbol for the entire region of error names. We then calculate
573 // the pointers into the list using addends which are appended to the relocation.
574 const names_sym_index = try zig_object.allocateSymbol(gpa);
575 const names_atom_index = try wasm.createAtom(names_sym_index, .zig_object);
576 const names_atom = wasm.getAtomPtr(names_atom_index);
577 names_atom.alignment = .@"1";
578 const segment_name = try gpa.dupe(u8, ".rodata.__zig_err_names");
579 const names_symbol = zig_object.symbol(names_sym_index);
580 names_symbol.* = .{
581 .name = wasm.preloaded_strings.__zig_err_names,
582 .tag = .data,
583 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
584 .index = try zig_object.createDataSegment(gpa, segment_name, names_atom.alignment),
585 .virtual_address = undefined,
586 };
587
588 log.debug("Populating error names", .{});
589
590 // Addend for each relocation to the table
591 var addend: u32 = 0;
592 const pt: Zcu.PerThread = .activate(wasm.base.comp.zcu.?, tid);
593 defer pt.deactivate();
594 const slice_ty = Type.slice_const_u8_sentinel_0;
595 const atom = wasm.getAtomPtr(atom_index);
596 {
597 // TODO: remove this unreachable entry
598 try atom.code.appendNTimes(gpa, 0, 4);
599 try atom.code.writer(gpa).writeInt(u32, 0, .little);
600 atom.size += @intCast(slice_ty.abiSize(pt.zcu));
601 addend += 1;
602
603 try names_atom.code.append(gpa, 0);
604 }
605 const ip = &pt.zcu.intern_pool;
606 for (ip.global_error_set.getNamesFromMainThread()) |error_name| {
607 const error_name_slice = error_name.toSlice(ip);
608 const len: u32 = @intCast(error_name_slice.len + 1); // names are 0-terminated
609
610 const offset = @as(u32, @intCast(atom.code.items.len));
611 // first we create the data for the slice of the name
612 try atom.code.appendNTimes(gpa, 0, 4); // ptr to name, will be relocated
613 try atom.code.writer(gpa).writeInt(u32, len - 1, .little);
614 // create relocation to the error name
615 try atom.relocs.append(gpa, .{
616 .index = @intFromEnum(names_atom.sym_index),
617 .relocation_type = .R_WASM_MEMORY_ADDR_I32,
618 .offset = offset,
619 .addend = @intCast(addend),
620 });
621 atom.size += @intCast(slice_ty.abiSize(pt.zcu));
622 addend += len;
623
624 // as we updated the error name table, we now store the actual name within the names atom
625 try names_atom.code.ensureUnusedCapacity(gpa, len);
626 names_atom.code.appendSliceAssumeCapacity(error_name_slice[0..len]);
627
628 log.debug("Populated error name: '{}'", .{error_name.fmt(ip)});
629 }
630 names_atom.size = addend;
631 zig_object.error_names_atom = names_atom_index;
632}
633
634/// Either creates a new import, or updates one if existing.
635/// When `type_index` is non-null, we assume an external function.
636/// In all other cases, a data-symbol will be created instead.
637pub fn addOrUpdateImport(
638 zig_object: *ZigObject,
639 wasm: *Wasm,
640 /// Name of the import
641 name: []const u8,
642 /// Symbol index that is external
643 symbol_index: Symbol.Index,
644 /// Optional library name (i.e. `extern "c" fn foo() void`
645 lib_name: ?[:0]const u8,
646 /// The index of the type that represents the function signature
647 /// when the extern is a function. When this is null, a data-symbol
648 /// is asserted instead.
649 type_index: ?u32,
650) !void {
651 const gpa = wasm.base.comp.gpa;
652 std.debug.assert(symbol_index != .null);
653 // For the import name, we use the decl's name, rather than the fully qualified name
654 // Also mangle the name when the lib name is set and not equal to "C" so imports with the same
655 // name but different module can be resolved correctly.
656 const mangle_name = if (lib_name) |n| !std.mem.eql(u8, n, "c") else false;
657 const full_name = if (mangle_name)
658 try std.fmt.allocPrint(gpa, "{s}|{s}", .{ name, lib_name.? })
659 else
660 name;
661 defer if (mangle_name) gpa.free(full_name);
662
663 const decl_name_index = try wasm.internString(full_name);
664 const sym: *Symbol = &zig_object.symbols.items[@intFromEnum(symbol_index)];
665 sym.setUndefined(true);
666 sym.setGlobal(true);
667 sym.name = decl_name_index;
668 if (mangle_name) {
669 // we specified a specific name for the symbol that does not match the import name
670 sym.setFlag(.WASM_SYM_EXPLICIT_NAME);
671 }
672
673 if (type_index) |ty_index| {
674 const gop = try zig_object.imports.getOrPut(gpa, symbol_index);
675 const module_name = if (lib_name) |n| try wasm.internString(n) else wasm.host_name;
676 if (!gop.found_existing) zig_object.imported_functions_count += 1;
677 gop.value_ptr.* = .{
678 .module_name = module_name,
679 .name = try wasm.internString(name),
680 .kind = .{ .function = ty_index },
681 };
682 sym.tag = .function;
683 } else {
684 sym.tag = .data;
685 }
686}
687
688/// Returns the symbol index from a symbol of which its flag is set global,
689/// such as an exported or imported symbol.
690/// If the symbol does not yet exist, creates a new one symbol instead
691/// and then returns the index to it.
692pub fn getGlobalSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator, name_index: Wasm.String) !Symbol.Index {
693 const gop = try zig_object.global_syms.getOrPut(gpa, name_index);
694 if (gop.found_existing) {
695 return gop.value_ptr.*;
696 }
697
698 var sym: Symbol = .{
699 .name = name_index,
700 .flags = 0,
701 .index = undefined, // index to type will be set after merging symbols
702 .tag = .function,
703 .virtual_address = std.math.maxInt(u32),
704 };
705 sym.setGlobal(true);
706 sym.setUndefined(true);
707
708 const sym_index = if (zig_object.symbols_free_list.popOrNull()) |index| index else blk: {
709 const index: Symbol.Index = @enumFromInt(zig_object.symbols.items.len);
710 try zig_object.symbols.ensureUnusedCapacity(gpa, 1);
711 zig_object.symbols.items.len += 1;
712 break :blk index;
713 };
714 zig_object.symbol(sym_index).* = sym;
715 gop.value_ptr.* = sym_index;
716 return sym_index;
717}
718
719/// For a given decl, find the given symbol index's atom, and create a relocation for the type.
720/// Returns the given pointer address
721pub fn getNavVAddr(
722 zig_object: *ZigObject,
723 wasm: *Wasm,
724 pt: Zcu.PerThread,
725 nav_index: InternPool.Nav.Index,
726 reloc_info: link.File.RelocInfo,
727) !u64 {
728 const zcu = pt.zcu;
729 const ip = &zcu.intern_pool;
730 const gpa = zcu.gpa;
731 const nav = ip.getNav(nav_index);
732 const target = &zcu.navFileScope(nav_index).mod.resolved_target.result;
733
734 const target_atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
735 const target_atom = wasm.getAtom(target_atom_index);
736 const target_symbol_index = @intFromEnum(target_atom.sym_index);
737 if (nav.getExtern(ip)) |@"extern"| {
738 try zig_object.addOrUpdateImport(
739 wasm,
740 nav.name.toSlice(ip),
741 target_atom.sym_index,
742 @"extern".lib_name.toSlice(ip),
743 null,
744 );
745 }
746
747 std.debug.assert(reloc_info.parent.atom_index != 0);
748 const atom_index = wasm.symbol_atom.get(.{
749 .file = .zig_object,
750 .index = @enumFromInt(reloc_info.parent.atom_index),
751 }).?;
752 const atom = wasm.getAtomPtr(atom_index);
753 const is_wasm32 = target.cpu.arch == .wasm32;
754 if (ip.isFunctionType(ip.getNav(nav_index).typeOf(ip))) {
755 std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations
756 try atom.relocs.append(gpa, .{
757 .index = target_symbol_index,
758 .offset = @intCast(reloc_info.offset),
759 .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
760 });
761 } else {
762 try atom.relocs.append(gpa, .{
763 .index = target_symbol_index,
764 .offset = @intCast(reloc_info.offset),
765 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
766 .addend = @intCast(reloc_info.addend),
767 });
768 }
769
770 // we do not know the final address at this point,
771 // as atom allocation will determine the address and relocations
772 // will calculate and rewrite this. Therefore, we simply return the symbol index
773 // that was targeted.
774 return target_symbol_index;
775}
776
777pub fn getUavVAddr(
778 zig_object: *ZigObject,
779 wasm: *Wasm,
780 uav: InternPool.Index,
781 reloc_info: link.File.RelocInfo,
782) !u64 {
783 const gpa = wasm.base.comp.gpa;
784 const target = wasm.base.comp.root_mod.resolved_target.result;
785 const atom_index = zig_object.uavs.get(uav).?;
786 const target_symbol_index = @intFromEnum(wasm.getAtom(atom_index).sym_index);
787
788 const parent_atom_index = wasm.symbol_atom.get(.{
789 .file = .zig_object,
790 .index = @enumFromInt(reloc_info.parent.atom_index),
791 }).?;
792 const parent_atom = wasm.getAtomPtr(parent_atom_index);
793 const is_wasm32 = target.cpu.arch == .wasm32;
794 const zcu = wasm.base.comp.zcu.?;
795 const ty = Type.fromInterned(zcu.intern_pool.typeOf(uav));
796 if (ty.zigTypeTag(zcu) == .@"fn") {
797 std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations
798 try parent_atom.relocs.append(gpa, .{
799 .index = target_symbol_index,
800 .offset = @intCast(reloc_info.offset),
801 .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
802 });
803 } else {
804 try parent_atom.relocs.append(gpa, .{
805 .index = target_symbol_index,
806 .offset = @intCast(reloc_info.offset),
807 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
808 .addend = @intCast(reloc_info.addend),
809 });
810 }
811
812 // we do not know the final address at this point,
813 // as atom allocation will determine the address and relocations
814 // will calculate and rewrite this. Therefore, we simply return the symbol index
815 // that was targeted.
816 return target_symbol_index;
817}
818
819pub fn deleteExport(
820 zig_object: *ZigObject,
821 wasm: *Wasm,
822 exported: Zcu.Exported,
823 name: InternPool.NullTerminatedString,
824) void {
825 const zcu = wasm.base.comp.zcu.?;
826 const nav_index = switch (exported) {
827 .nav => |nav_index| nav_index,
828 .uav => @panic("TODO: implement Wasm linker code for exporting a constant value"),
829 };
830 const nav_info = zig_object.navs.getPtr(nav_index) orelse return;
831 const name_interned = wasm.getExistingString(name.toSlice(&zcu.intern_pool)).?;
832 if (nav_info.@"export"(zig_object, name_interned)) |sym_index| {
833 const sym = zig_object.symbol(sym_index);
834 nav_info.deleteExport(sym_index);
835 std.debug.assert(zig_object.global_syms.remove(sym.name));
836 std.debug.assert(wasm.symbol_atom.remove(.{ .file = .zig_object, .index = sym_index }));
837 zig_object.symbols_free_list.append(wasm.base.comp.gpa, sym_index) catch {};
838 sym.tag = .dead;
839 }
840}
841
842pub fn updateExports(
843 zig_object: *ZigObject,
844 wasm: *Wasm,
845 pt: Zcu.PerThread,
846 exported: Zcu.Exported,
847 export_indices: []const u32,
848) !void {
849 const zcu = pt.zcu;
850 const ip = &zcu.intern_pool;
851 const nav_index = switch (exported) {
852 .nav => |nav| nav,
853 .uav => |uav| {
854 _ = uav;
855 @panic("TODO: implement Wasm linker code for exporting a constant value");
856 },
857 };
858 const nav = ip.getNav(nav_index);
859 const atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
860 const nav_info = zig_object.navs.getPtr(nav_index).?;
861 const atom = wasm.getAtom(atom_index);
862 const atom_sym = wasm.symbolLocSymbol(atom.symbolLoc()).*;
863 const gpa = zcu.gpa;
864 log.debug("Updating exports for decl '{}'", .{nav.name.fmt(ip)});
865
866 for (export_indices) |export_idx| {
867 const exp = zcu.all_exports.items[export_idx];
868 if (exp.opts.section.toSlice(ip)) |section| {
869 try zcu.failed_exports.putNoClobber(gpa, export_idx, try Zcu.ErrorMsg.create(
870 gpa,
871 zcu.navSrcLoc(nav_index),
872 "Unimplemented: ExportOptions.section '{s}'",
873 .{section},
874 ));
875 continue;
876 }
877
878 const export_name = try wasm.internString(exp.opts.name.toSlice(ip));
879 const sym_index = if (nav_info.@"export"(zig_object, export_name)) |idx| idx else index: {
880 const sym_index = try zig_object.allocateSymbol(gpa);
881 try nav_info.appendExport(gpa, sym_index);
882 break :index sym_index;
883 };
884
885 const sym = zig_object.symbol(sym_index);
886 sym.setGlobal(true);
887 sym.setUndefined(false);
888 sym.index = atom_sym.index;
889 sym.tag = atom_sym.tag;
890 sym.name = export_name;
891
892 switch (exp.opts.linkage) {
893 .internal => {
894 sym.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
895 },
896 .weak => {
897 sym.setFlag(.WASM_SYM_BINDING_WEAK);
898 },
899 .strong => {}, // symbols are strong by default
900 .link_once => {
901 try zcu.failed_exports.putNoClobber(gpa, export_idx, try Zcu.ErrorMsg.create(
902 gpa,
903 zcu.navSrcLoc(nav_index),
904 "Unimplemented: LinkOnce",
905 .{},
906 ));
907 continue;
908 },
909 }
910 if (exp.opts.visibility == .hidden) {
911 sym.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
912 }
913 log.debug(" with name '{s}' - {}", .{ wasm.stringSlice(export_name), sym });
914 try zig_object.global_syms.put(gpa, export_name, sym_index);
915 try wasm.symbol_atom.put(gpa, .{ .file = .zig_object, .index = sym_index }, atom_index);
916 }
917}
918
919pub fn freeNav(zig_object: *ZigObject, wasm: *Wasm, nav_index: InternPool.Nav.Index) void {
920 const gpa = wasm.base.comp.gpa;
921 const zcu = wasm.base.comp.zcu.?;
922 const ip = &zcu.intern_pool;
923 const nav_info = zig_object.navs.getPtr(nav_index).?;
924 const atom_index = nav_info.atom;
925 const atom = wasm.getAtomPtr(atom_index);
926 zig_object.symbols_free_list.append(gpa, atom.sym_index) catch {};
927 for (nav_info.exports.items) |exp_sym_index| {
928 const exp_sym = zig_object.symbol(exp_sym_index);
929 exp_sym.tag = .dead;
930 zig_object.symbols_free_list.append(exp_sym_index) catch {};
931 }
932 nav_info.exports.deinit(gpa);
933 std.debug.assert(zig_object.navs.remove(nav_index));
934 const sym = &zig_object.symbols.items[atom.sym_index];
935 for (atom.locals.items) |local_atom_index| {
936 const local_atom = wasm.getAtom(local_atom_index);
937 const local_symbol = &zig_object.symbols.items[local_atom.sym_index];
938 std.debug.assert(local_symbol.tag == .data);
939 zig_object.symbols_free_list.append(gpa, local_atom.sym_index) catch {};
940 std.debug.assert(wasm.symbol_atom.remove(local_atom.symbolLoc()));
941 local_symbol.tag = .dead; // also for any local symbol
942 const segment = &zig_object.segment_info.items[local_atom.sym_index];
943 gpa.free(segment.name);
944 segment.name = &.{}; // Ensure no accidental double free
945 }
946
947 const nav = ip.getNav(nav_index);
948 if (nav.getExtern(ip) != null) {
949 std.debug.assert(zig_object.imports.remove(atom.sym_index));
950 }
951 std.debug.assert(wasm.symbol_atom.remove(atom.symbolLoc()));
952
953 // if (wasm.dwarf) |*dwarf| {
954 // dwarf.freeDecl(decl_index);
955 // }
956
957 atom.prev = null;
958 sym.tag = .dead;
959 if (sym.isGlobal()) {
960 std.debug.assert(zig_object.global_syms.remove(atom.sym_index));
961 }
962 if (ip.isFunctionType(nav.typeOf(ip))) {
963 zig_object.functions_free_list.append(gpa, sym.index) catch {};
964 std.debug.assert(zig_object.atom_types.remove(atom_index));
965 } else {
966 zig_object.segment_free_list.append(gpa, sym.index) catch {};
967 const segment = &zig_object.segment_info.items[sym.index];
968 gpa.free(segment.name);
969 segment.name = &.{}; // Prevent accidental double free
970 }
971}
972
973fn getTypeIndex(zig_object: *const ZigObject, func_type: std.wasm.Type) ?u32 {
974 var index: u32 = 0;
975 while (index < zig_object.func_types.items.len) : (index += 1) {
976 if (zig_object.func_types.items[index].eql(func_type)) return index;
977 }
978 return null;
979}
980
981/// Searches for a matching function signature. When no matching signature is found,
982/// a new entry will be made. The value returned is the index of the type within `wasm.func_types`.
983pub fn putOrGetFuncType(zig_object: *ZigObject, gpa: std.mem.Allocator, func_type: std.wasm.Type) !u32 {
984 if (zig_object.getTypeIndex(func_type)) |index| {
985 return index;
986 }
987
988 // functype does not exist.
989 const index: u32 = @intCast(zig_object.func_types.items.len);
990 const params = try gpa.dupe(std.wasm.Valtype, func_type.params);
991 errdefer gpa.free(params);
992 const returns = try gpa.dupe(std.wasm.Valtype, func_type.returns);
993 errdefer gpa.free(returns);
994 try zig_object.func_types.append(gpa, .{
995 .params = params,
996 .returns = returns,
997 });
998 return index;
999}
1000
1001/// Generates an atom containing the global error set' size.
1002/// This will only be generated if the symbol exists.
1003fn setupErrorsLen(zig_object: *ZigObject, wasm: *Wasm) !void {
1004 const gpa = wasm.base.comp.gpa;
1005 const sym_index = zig_object.global_syms.get(wasm.preloaded_strings.__zig_errors_len) orelse return;
1006
1007 const errors_len = 1 + wasm.base.comp.zcu.?.intern_pool.global_error_set.getNamesFromMainThread().len;
1008 // overwrite existing atom if it already exists (maybe the error set has increased)
1009 // if not, allocate a new atom.
1010 const atom_index = if (wasm.symbol_atom.get(.{ .file = .zig_object, .index = sym_index })) |index| blk: {
1011 const atom = wasm.getAtomPtr(index);
1012 atom.prev = .null;
1013 atom.deinit(gpa);
1014 break :blk index;
1015 } else idx: {
1016 // We found a call to __zig_errors_len so make the symbol a local symbol
1017 // and define it, so the final binary or resulting object file will not attempt
1018 // to resolve it.
1019 const sym = zig_object.symbol(sym_index);
1020 sym.setGlobal(false);
1021 sym.setUndefined(false);
1022 sym.tag = .data;
1023 const segment_name = try gpa.dupe(u8, ".rodata.__zig_errors_len");
1024 sym.index = try zig_object.createDataSegment(gpa, segment_name, .@"2");
1025 break :idx try wasm.createAtom(sym_index, .zig_object);
1026 };
1027
1028 const atom = wasm.getAtomPtr(atom_index);
1029 atom.code.clearRetainingCapacity();
1030 atom.sym_index = sym_index;
1031 atom.size = 2;
1032 atom.alignment = .@"2";
1033 try atom.code.writer(gpa).writeInt(u16, @intCast(errors_len), .little);
1034}
1035
1036/// Initializes symbols and atoms for the debug sections
1037/// Initialization is only done when compiling Zig code.
1038/// When Zig is invoked as a linker instead, the atoms
1039/// and symbols come from the object files instead.
1040pub fn initDebugSections(zig_object: *ZigObject) !void {
1041 if (zig_object.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections
1042 std.debug.assert(zig_object.debug_info_index == null);
1043 // this will create an Atom and set the index for us.
1044 zig_object.debug_info_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_info_index, ".debug_info");
1045 zig_object.debug_line_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_line_index, ".debug_line");
1046 zig_object.debug_loc_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_loc_index, ".debug_loc");
1047 zig_object.debug_abbrev_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_abbrev_index, ".debug_abbrev");
1048 zig_object.debug_ranges_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_ranges_index, ".debug_ranges");
1049 zig_object.debug_str_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_str_index, ".debug_str");
1050 zig_object.debug_pubnames_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_pubnames_index, ".debug_pubnames");
1051 zig_object.debug_pubtypes_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_pubtypes_index, ".debug_pubtypes");
1052}
1053
1054/// From a given index variable, creates a new debug section.
1055/// This initializes the index, appends a new segment,
1056/// and finally, creates a managed `Atom`.
1057pub fn createDebugSectionForIndex(zig_object: *ZigObject, wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index {
1058 const gpa = wasm.base.comp.gpa;
1059 const new_index: u32 = @intCast(zig_object.segments.items.len);
1060 index.* = new_index;
1061 try zig_object.appendDummySegment();
1062
1063 const sym_index = try zig_object.allocateSymbol(gpa);
1064 const atom_index = try wasm.createAtom(sym_index, .zig_object);
1065 const atom = wasm.getAtomPtr(atom_index);
1066 zig_object.symbols.items[sym_index] = .{
1067 .tag = .section,
1068 .name = try wasm.internString(name),
1069 .index = 0,
1070 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1071 };
1072
1073 atom.alignment = .@"1"; // debug sections are always 1-byte-aligned
1074 return atom_index;
1075}
1076
1077pub fn updateLineNumber(zig_object: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
1078 if (zig_object.dwarf) |*dw| {
1079 try dw.updateLineNumber(pt.zcu, ti_id);
1080 }
1081}
1082
1083/// Allocates debug atoms into their respective debug sections
1084/// to merge them with maybe-existing debug atoms from object files.
1085fn allocateDebugAtoms(zig_object: *ZigObject) !void {
1086 if (zig_object.dwarf == null) return;
1087
1088 const allocAtom = struct {
1089 fn f(ctx: *ZigObject, maybe_index: *?u32, atom_index: Atom.Index) !void {
1090 const index = maybe_index.* orelse idx: {
1091 const index = @as(u32, @intCast(ctx.segments.items.len));
1092 try ctx.appendDummySegment();
1093 maybe_index.* = index;
1094 break :idx index;
1095 };
1096 const atom = ctx.getAtomPtr(atom_index);
1097 atom.size = @as(u32, @intCast(atom.code.items.len));
1098 ctx.symbols.items[atom.sym_index].index = index;
1099 try ctx.appendAtomAtIndex(index, atom_index);
1100 }
1101 }.f;
1102
1103 try allocAtom(zig_object, &zig_object.debug_info_index, zig_object.debug_info_atom.?);
1104 try allocAtom(zig_object, &zig_object.debug_line_index, zig_object.debug_line_atom.?);
1105 try allocAtom(zig_object, &zig_object.debug_loc_index, zig_object.debug_loc_atom.?);
1106 try allocAtom(zig_object, &zig_object.debug_str_index, zig_object.debug_str_atom.?);
1107 try allocAtom(zig_object, &zig_object.debug_ranges_index, zig_object.debug_ranges_atom.?);
1108 try allocAtom(zig_object, &zig_object.debug_abbrev_index, zig_object.debug_abbrev_atom.?);
1109 try allocAtom(zig_object, &zig_object.debug_pubnames_index, zig_object.debug_pubnames_atom.?);
1110 try allocAtom(zig_object, &zig_object.debug_pubtypes_index, zig_object.debug_pubtypes_atom.?);
1111}
1112
1113/// For the given `decl_index`, stores the corresponding type representing the function signature.
1114/// Asserts declaration has an associated `Atom`.
1115/// Returns the index into the list of types.
1116pub fn storeDeclType(zig_object: *ZigObject, gpa: std.mem.Allocator, nav_index: InternPool.Nav.Index, func_type: std.wasm.Type) !u32 {
1117 const nav_info = zig_object.navs.get(nav_index).?;
1118 const index = try zig_object.putOrGetFuncType(gpa, func_type);
1119 try zig_object.atom_types.put(gpa, nav_info.atom, index);
1120 return index;
1121}
1122
1123/// The symbols in ZigObject are already represented by an atom as we need to store its data.
1124/// So rather than creating a new Atom and returning its index, we use this opportunity to scan
1125/// its relocations and create any GOT symbols or function table indexes it may require.
1126pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm: *Wasm, index: Symbol.Index) !Atom.Index {
1127 const gpa = wasm.base.comp.gpa;
1128 const loc: Wasm.SymbolLoc = .{ .file = .zig_object, .index = index };
1129 const atom_index = wasm.symbol_atom.get(loc).?;
1130 const final_index = try wasm.getMatchingSegment(.zig_object, index);
1131 try wasm.appendAtomAtIndex(final_index, atom_index);
1132 const atom = wasm.getAtom(atom_index);
1133 for (atom.relocs.items) |reloc| {
1134 const reloc_index: Symbol.Index = @enumFromInt(reloc.index);
1135 switch (reloc.relocation_type) {
1136 .R_WASM_TABLE_INDEX_I32,
1137 .R_WASM_TABLE_INDEX_I64,
1138 .R_WASM_TABLE_INDEX_SLEB,
1139 .R_WASM_TABLE_INDEX_SLEB64,
1140 => {
1141 try wasm.function_table.put(gpa, .{
1142 .file = .zig_object,
1143 .index = reloc_index,
1144 }, 0);
1145 },
1146 .R_WASM_GLOBAL_INDEX_I32,
1147 .R_WASM_GLOBAL_INDEX_LEB,
1148 => {
1149 const sym = zig_object.symbol(reloc_index);
1150 if (sym.tag != .global) {
1151 try wasm.got_symbols.append(gpa, .{
1152 .file = .zig_object,
1153 .index = reloc_index,
1154 });
1155 }
1156 },
1157 else => {},
1158 }
1159 }
1160 return atom_index;
1161}
1162
1163/// Creates a new Wasm function with a given symbol name and body.
1164/// Returns the symbol index of the new function.
1165pub fn createFunction(
1166 zig_object: *ZigObject,
1167 wasm: *Wasm,
1168 symbol_name: []const u8,
1169 func_ty: std.wasm.Type,
1170 function_body: *std.ArrayList(u8),
1171 relocations: *std.ArrayList(Wasm.Relocation),
1172) !Symbol.Index {
1173 const gpa = wasm.base.comp.gpa;
1174 const sym_index = try zig_object.allocateSymbol(gpa);
1175 const sym = zig_object.symbol(sym_index);
1176 sym.tag = .function;
1177 sym.name = try wasm.internString(symbol_name);
1178 const type_index = try zig_object.putOrGetFuncType(gpa, func_ty);
1179 sym.index = try zig_object.appendFunction(gpa, .{ .type_index = type_index });
1180
1181 const atom_index = try wasm.createAtom(sym_index, .zig_object);
1182 const atom = wasm.getAtomPtr(atom_index);
1183 atom.size = @intCast(function_body.items.len);
1184 atom.code = function_body.moveToUnmanaged();
1185 atom.relocs = relocations.moveToUnmanaged();
1186
1187 try zig_object.synthetic_functions.append(gpa, atom_index);
1188 return sym_index;
1189}
1190
1191/// Appends a new `std.wasm.Func` to the list of functions and returns its index.
1192fn appendFunction(zig_object: *ZigObject, gpa: std.mem.Allocator, func: std.wasm.Func) !u32 {
1193 const index: u32 = if (zig_object.functions_free_list.popOrNull()) |idx|
1194 idx
1195 else idx: {
1196 const len: u32 = @intCast(zig_object.functions.items.len);
1197 _ = try zig_object.functions.addOne(gpa);
1198 break :idx len;
1199 };
1200 zig_object.functions.items[index] = func;
1201
1202 return index;
1203}
1204
1205pub fn flushModule(zig_object: *ZigObject, wasm: *Wasm, tid: Zcu.PerThread.Id) !void {
1206 try zig_object.populateErrorNameTable(wasm, tid);
1207 try zig_object.setupErrorsLen(wasm);
1208}
1209
1210const build_options = @import("build_options");
1211const builtin = @import("builtin");
1212const codegen = @import("../../codegen.zig");
1213const link = @import("../../link.zig");
1214const log = std.log.scoped(.zig_object);
1215const std = @import("std");
1216const Path = std.Build.Cache.Path;
1217
1218const Air = @import("../../Air.zig");
1219const Atom = Wasm.Atom;
1220const Dwarf = @import("../Dwarf.zig");
1221const InternPool = @import("../../InternPool.zig");
1222const Liveness = @import("../../Liveness.zig");
1223const Zcu = @import("../../Zcu.zig");
1224const Symbol = @import("Symbol.zig");
1225const Type = @import("../../Type.zig");
1226const Value = @import("../../Value.zig");
1227const Wasm = @import("../Wasm.zig");
1228const AnalUnit = InternPool.AnalUnit;
1229const ZigObject = @This();
src/main.zig+4
...@@ -75,6 +75,10 @@ pub fn fatal(comptime format: []const u8, args: anytype) noreturn {...@@ -75,6 +75,10 @@ pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
75 process.exit(1);75 process.exit(1);
76}76}
7777
78/// Shaming all the locations that inappropriately use an O(N) search algorithm.
79/// Please delete this and fix the compilation errors!
80pub const @"bad O(N)" = void;
81
78const normal_usage =82const normal_usage =
79 \\Usage: zig [command] [options]83 \\Usage: zig [command] [options]
80 \\84 \\
src/register_manager.zig+9-14
...@@ -14,19 +14,14 @@ const link = @import("link.zig");...@@ -14,19 +14,14 @@ const link = @import("link.zig");
1414
15const log = std.log.scoped(.register_manager);15const log = std.log.scoped(.register_manager);
1616
17pub const AllocateRegistersError = error{17pub const AllocationError = error{
18 /// No registers are available anymore
19 OutOfRegisters,18 OutOfRegisters,
20 /// Can happen when spilling an instruction in codegen runs out of
21 /// memory, so we propagate that error
22 OutOfMemory,19 OutOfMemory,
23 /// Can happen when spilling an instruction in codegen triggers integer20 /// Compiler was asked to operate on a number larger than supported.
24 /// overflow, so we propagate that error
25 Overflow,21 Overflow,
26 /// Can happen when spilling an instruction triggers a codegen22 /// Indicates the error is already stored in `failed_codegen` on the Zcu.
27 /// error, so we propagate that error
28 CodegenFail,23 CodegenFail,
29} || link.File.UpdateDebugInfoError;24};
3025
31pub fn RegisterManager(26pub fn RegisterManager(
32 comptime Function: type,27 comptime Function: type,
...@@ -281,7 +276,7 @@ pub fn RegisterManager(...@@ -281,7 +276,7 @@ pub fn RegisterManager(
281 comptime count: comptime_int,276 comptime count: comptime_int,
282 insts: [count]?Air.Inst.Index,277 insts: [count]?Air.Inst.Index,
283 register_class: RegisterBitSet,278 register_class: RegisterBitSet,
284 ) AllocateRegistersError![count]Register {279 ) AllocationError![count]Register {
285 comptime assert(count > 0 and count <= tracked_registers.len);280 comptime assert(count > 0 and count <= tracked_registers.len);
286281
287 var locked_registers = self.locked_registers;282 var locked_registers = self.locked_registers;
...@@ -338,7 +333,7 @@ pub fn RegisterManager(...@@ -338,7 +333,7 @@ pub fn RegisterManager(
338 self: *Self,333 self: *Self,
339 inst: ?Air.Inst.Index,334 inst: ?Air.Inst.Index,
340 register_class: RegisterBitSet,335 register_class: RegisterBitSet,
341 ) AllocateRegistersError!Register {336 ) AllocationError!Register {
342 return (try self.allocRegs(1, .{inst}, register_class))[0];337 return (try self.allocRegs(1, .{inst}, register_class))[0];
343 }338 }
344339
...@@ -349,7 +344,7 @@ pub fn RegisterManager(...@@ -349,7 +344,7 @@ pub fn RegisterManager(
349 self: *Self,344 self: *Self,
350 tracked_index: TrackedIndex,345 tracked_index: TrackedIndex,
351 inst: ?Air.Inst.Index,346 inst: ?Air.Inst.Index,
352 ) AllocateRegistersError!void {347 ) AllocationError!void {
353 log.debug("getReg {} for inst {?}", .{ regAtTrackedIndex(tracked_index), inst });348 log.debug("getReg {} for inst {?}", .{ regAtTrackedIndex(tracked_index), inst });
354 if (!self.isRegIndexFree(tracked_index)) {349 if (!self.isRegIndexFree(tracked_index)) {
355 // Move the instruction that was previously there to a350 // Move the instruction that was previously there to a
...@@ -362,7 +357,7 @@ pub fn RegisterManager(...@@ -362,7 +357,7 @@ pub fn RegisterManager(
362 }357 }
363 self.getRegIndexAssumeFree(tracked_index, inst);358 self.getRegIndexAssumeFree(tracked_index, inst);
364 }359 }
365 pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void {360 pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocationError!void {
366 log.debug("getting reg: {}", .{reg});361 log.debug("getting reg: {}", .{reg});
367 return self.getRegIndex(indexOfRegIntoTracked(reg) orelse return, inst);362 return self.getRegIndex(indexOfRegIntoTracked(reg) orelse return, inst);
368 }363 }
...@@ -370,7 +365,7 @@ pub fn RegisterManager(...@@ -370,7 +365,7 @@ pub fn RegisterManager(
370 self: *Self,365 self: *Self,
371 comptime reg: Register,366 comptime reg: Register,
372 inst: ?Air.Inst.Index,367 inst: ?Air.Inst.Index,
373 ) AllocateRegistersError!void {368 ) AllocationError!void {
374 return self.getRegIndex((comptime indexOfRegIntoTracked(reg)) orelse return, inst);369 return self.getRegIndex((comptime indexOfRegIntoTracked(reg)) orelse return, inst);
375 }370 }
376371
test/behavior.zig+10-3
...@@ -31,8 +31,6 @@ test {...@@ -31,8 +31,6 @@ test {
31 _ = @import("behavior/error.zig");31 _ = @import("behavior/error.zig");
32 _ = @import("behavior/eval.zig");32 _ = @import("behavior/eval.zig");
33 _ = @import("behavior/export_builtin.zig");33 _ = @import("behavior/export_builtin.zig");
34 _ = @import("behavior/export_self_referential_type_info.zig");
35 _ = @import("behavior/extern.zig");
36 _ = @import("behavior/field_parent_ptr.zig");34 _ = @import("behavior/field_parent_ptr.zig");
37 _ = @import("behavior/floatop.zig");35 _ = @import("behavior/floatop.zig");
38 _ = @import("behavior/fn.zig");36 _ = @import("behavior/fn.zig");
...@@ -45,7 +43,6 @@ test {...@@ -45,7 +43,6 @@ test {
45 _ = @import("behavior/hasfield.zig");43 _ = @import("behavior/hasfield.zig");
46 _ = @import("behavior/if.zig");44 _ = @import("behavior/if.zig");
47 _ = @import("behavior/import.zig");45 _ = @import("behavior/import.zig");
48 _ = @import("behavior/import_c_keywords.zig");
49 _ = @import("behavior/incomplete_struct_param_tld.zig");46 _ = @import("behavior/incomplete_struct_param_tld.zig");
50 _ = @import("behavior/inline_switch.zig");47 _ = @import("behavior/inline_switch.zig");
51 _ = @import("behavior/int128.zig");48 _ = @import("behavior/int128.zig");
...@@ -127,6 +124,16 @@ test {...@@ -127,6 +124,16 @@ test {
127 {124 {
128 _ = @import("behavior/export_keyword.zig");125 _ = @import("behavior/export_keyword.zig");
129 }126 }
127
128 if (!builtin.cpu.arch.isWasm()) {
129 // Due to lack of import/export of global support
130 // (https://github.com/ziglang/zig/issues/4866), these tests correctly
131 // cause linker errors, since a data symbol cannot be exported when
132 // building an executable.
133 _ = @import("behavior/export_self_referential_type_info.zig");
134 _ = @import("behavior/extern.zig");
135 _ = @import("behavior/import_c_keywords.zig");
136 }
130}137}
131138
132// This bug only repros in the root file139// This bug only repros in the root file
test/behavior/export_builtin.zig+15-1
...@@ -6,6 +6,11 @@ test "exporting enum value" {...@@ -6,6 +6,11 @@ test "exporting enum value" {
6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
88
9 if (builtin.cpu.arch.isWasm()) {
10 // https://github.com/ziglang/zig/issues/4866
11 return error.SkipZigTest;
12 }
13
9 const S = struct {14 const S = struct {
10 const E = enum(c_int) { one, two };15 const E = enum(c_int) { one, two };
11 const e: E = .two;16 const e: E = .two;
...@@ -33,6 +38,11 @@ test "exporting using namespace access" {...@@ -33,6 +38,11 @@ test "exporting using namespace access" {
33 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;38 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
34 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;39 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
3540
41 if (builtin.cpu.arch.isWasm()) {
42 // https://github.com/ziglang/zig/issues/4866
43 return error.SkipZigTest;
44 }
45
36 const S = struct {46 const S = struct {
37 const Inner = struct {47 const Inner = struct {
38 const x: u32 = 5;48 const x: u32 = 5;
...@@ -46,7 +56,6 @@ test "exporting using namespace access" {...@@ -46,7 +56,6 @@ test "exporting using namespace access" {
46}56}
4757
48test "exporting comptime-known value" {58test "exporting comptime-known value" {
49 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
50 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;59 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
51 if (builtin.zig_backend == .stage2_x86_64 and60 if (builtin.zig_backend == .stage2_x86_64 and
52 (builtin.target.ofmt != .elf and61 (builtin.target.ofmt != .elf and
...@@ -56,6 +65,11 @@ test "exporting comptime-known value" {...@@ -56,6 +65,11 @@ test "exporting comptime-known value" {
56 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;65 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
57 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;66 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
5867
68 if (builtin.cpu.arch.isWasm()) {
69 // https://github.com/ziglang/zig/issues/4866
70 return error.SkipZigTest;
71 }
72
59 const x: u32 = 10;73 const x: u32 = 10;
60 @export(&x, .{ .name = "exporting_comptime_known_value_foo" });74 @export(&x, .{ .name = "exporting_comptime_known_value_foo" });
61 const S = struct {75 const S = struct {
test/incremental/add_decl+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6const std = @import("std");7const std = @import("std");
test/incremental/add_decl_namespaced+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6const std = @import("std");7const std = @import("std");
test/incremental/change_generic_line_number+1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=wasm32-wasi-selfhosted
2#update=initial version3#update=initial version
3#file=main.zig4#file=main.zig
4const std = @import("std");5const std = @import("std");
test/incremental/change_line_number+1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=wasm32-wasi-selfhosted
2#update=initial version3#update=initial version
3#file=main.zig4#file=main.zig
4const std = @import("std");5const std = @import("std");
test/incremental/change_shift_op+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6pub fn main() !void {7pub fn main() !void {
test/incremental/change_struct_same_fields+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6const S = extern struct { x: u8, y: u8 };7const S = extern struct { x: u8, y: u8 };
test/incremental/compile_error_then_log+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version with compile error5#update=initial version with compile error
5#file=main.zig6#file=main.zig
6comptime {7comptime {
test/incremental/delete_comptime_decls+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6pub fn main() void {}7pub fn main() void {}
test/incremental/fix_astgen_failure+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version with error5#update=initial version with error
5#file=main.zig6#file=main.zig
6pub fn main() !void {7pub fn main() !void {
test/incremental/hello+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6const std = @import("std");7const std = @import("std");
test/incremental/modify_inline_fn+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6const std = @import("std");7const std = @import("std");
test/incremental/move_src+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6const std = @import("std");7const std = @import("std");
test/incremental/recursive_function_becomes_non_recursive+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6pub fn main() !void {7pub fn main() !void {
test/incremental/remove_enum_field+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6const MyEnum = enum(u8) {7const MyEnum = enum(u8) {
test/incremental/remove_invalid_union_backing_enum+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6const E = enum { a, b, c };7const E = enum { a, b, c };
test/incremental/temporary_parse_error+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6const std = @import("std");7const std = @import("std");
test/incremental/type_becomes_comptime_only+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6const SomeType = u32;7const SomeType = u32;
test/incremental/unreferenced_error+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1#target=x86_64-linux-selfhosted1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe3#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
4#update=initial version5#update=initial version
5#file=main.zig6#file=main.zig
6const std = @import("std");7const std = @import("std");
test/link/build.zig.zon-6
...@@ -24,9 +24,6 @@...@@ -24,9 +24,6 @@
24 .wasm_basic_features = .{24 .wasm_basic_features = .{
25 .path = "wasm/basic-features",25 .path = "wasm/basic-features",
26 },26 },
27 .wasm_bss = .{
28 .path = "wasm/bss",
29 },
30 .wasm_export = .{27 .wasm_export = .{
31 .path = "wasm/export",28 .path = "wasm/export",
32 },29 },
...@@ -48,9 +45,6 @@...@@ -48,9 +45,6 @@
48 .wasm_producers = .{45 .wasm_producers = .{
49 .path = "wasm/producers",46 .path = "wasm/producers",
50 },47 },
51 .wasm_segments = .{
52 .path = "wasm/segments",
53 },
54 .wasm_shared_memory = .{48 .wasm_shared_memory = .{
55 .path = "wasm/shared-memory",49 .path = "wasm/shared-memory",
56 },50 },
test/link/wasm/archive/build.zig-2
...@@ -1,7 +1,5 @@...@@ -1,7 +1,5 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
5pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test it");4 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;5 b.default_step = test_step;
test/link/wasm/basic-features/build.zig-2
...@@ -1,7 +1,5 @@...@@ -1,7 +1,5 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
5pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
6 // Library with explicitly set cpu features4 // Library with explicitly set cpu features
7 const lib = b.addExecutable(.{5 const lib = b.addExecutable(.{
test/link/wasm/bss/build.zig deleted-95
...@@ -1,95 +0,0 @@
1const std = @import("std");
2
3pub const requires_stage2 = true;
4
5pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test");
7 b.default_step = test_step;
8
9 add(b, test_step, .Debug, true);
10 add(b, test_step, .ReleaseFast, false);
11 add(b, test_step, .ReleaseSmall, false);
12 add(b, test_step, .ReleaseSafe, true);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode, is_safe: bool) void {
16 {
17 const lib = b.addExecutable(.{
18 .name = "lib",
19 .root_module = b.createModule(.{
20 .root_source_file = b.path("lib.zig"),
21 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
22 .optimize = optimize_mode,
23 .strip = false,
24 }),
25 });
26 lib.entry = .disabled;
27 lib.use_llvm = false;
28 lib.use_lld = false;
29 // to make sure the bss segment is emitted, we must import memory
30 lib.import_memory = true;
31 lib.link_gc_sections = false;
32
33 const check_lib = lib.checkObject();
34
35 // since we import memory, make sure it exists with the correct naming
36 check_lib.checkInHeaders();
37 check_lib.checkExact("Section import");
38 check_lib.checkExact("entries 1");
39 check_lib.checkExact("module env"); // default module name is "env"
40 check_lib.checkExact("name memory"); // as per linker specification
41
42 // since we are importing memory, ensure it's not exported
43 check_lib.checkInHeaders();
44 check_lib.checkNotPresent("Section export");
45
46 // validate the name of the stack pointer
47 check_lib.checkInHeaders();
48 check_lib.checkExact("Section custom");
49 check_lib.checkExact("type data_segment");
50 check_lib.checkExact("names 2");
51 check_lib.checkExact("index 0");
52 check_lib.checkExact("name .rodata");
53 // for safe optimization modes `undefined` is stored in data instead of bss.
54 if (is_safe) {
55 check_lib.checkExact("index 1");
56 check_lib.checkExact("name .data");
57 check_lib.checkNotPresent("name .bss");
58 } else {
59 check_lib.checkExact("index 1"); // bss section always last
60 check_lib.checkExact("name .bss");
61 }
62 test_step.dependOn(&check_lib.step);
63 }
64
65 // verify zero'd declaration is stored in bss for all optimization modes.
66 {
67 const lib = b.addExecutable(.{
68 .name = "lib",
69 .root_module = b.createModule(.{
70 .root_source_file = b.path("lib2.zig"),
71 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
72 .optimize = optimize_mode,
73 .strip = false,
74 }),
75 });
76 lib.entry = .disabled;
77 lib.use_llvm = false;
78 lib.use_lld = false;
79 // to make sure the bss segment is emitted, we must import memory
80 lib.import_memory = true;
81 lib.link_gc_sections = false;
82
83 const check_lib = lib.checkObject();
84 check_lib.checkInHeaders();
85 check_lib.checkExact("Section custom");
86 check_lib.checkExact("type data_segment");
87 check_lib.checkExact("names 2");
88 check_lib.checkExact("index 0");
89 check_lib.checkExact("name .rodata");
90 check_lib.checkExact("index 1");
91 check_lib.checkExact("name .bss");
92
93 test_step.dependOn(&check_lib.step);
94 }
95}
test/link/wasm/bss/lib.zig deleted-5
...@@ -1,5 +0,0 @@
1pub var bss: u32 = undefined;
2
3export fn foo() void {
4 _ = bss;
5}
test/link/wasm/bss/lib2.zig deleted-5
...@@ -1,5 +0,0 @@
1pub var bss: u32 = 0;
2
3export fn foo() void {
4 _ = bss;
5}
test/link/wasm/export-data/build.zig+9-33
...@@ -4,48 +4,24 @@ pub fn build(b: *std.Build) void {...@@ -4,48 +4,24 @@ pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");4 const test_step = b.step("test", "Test");
5 b.default_step = test_step;5 b.default_step = test_step;
66
7 if (@import("builtin").os.tag == .windows) {
8 // TODO: Fix open handle in wasm-linker refraining rename from working on Windows.
9 return;
10 }
11
12 const lib = b.addExecutable(.{7 const lib = b.addExecutable(.{
13 .name = "lib",8 .name = "lib",
14 .root_module = b.createModule(.{9 .root_module = b.createModule(.{
15 .root_source_file = b.path("lib.zig"),10 .root_source_file = b.path("lib.zig"),
16 .optimize = .ReleaseSafe, // to make the output deterministic in address positions11 .optimize = .Debug,
17 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),12 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
18 }),13 }),
19 });14 });
20 lib.entry = .disabled;15 lib.entry = .disabled;
21 lib.use_lld = false;16 lib.use_lld = false;
22 lib.root_module.export_symbol_names = &.{ "foo", "bar" };17 lib.root_module.export_symbol_names = &.{ "foo", "bar" };
23 lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse18 // Object being linked has neither functions nor globals named "foo" or "bar" and
2419 // so these names correctly fail to be exported when creating an executable.
25 const check_lib = lib.checkObject();20 lib.expect_errors = .{ .exact = &.{
2621 "error: manually specified export name 'foo' undefined",
27 check_lib.checkInHeaders();22 "error: manually specified export name 'bar' undefined",
28 check_lib.checkExact("Section global");23 } };
29 check_lib.checkExact("entries 3");24 _ = lib.getEmittedBin();
30 check_lib.checkExact("type i32"); // stack pointer so skip other fields
31 check_lib.checkExact("type i32");
32 check_lib.checkExact("mutable false");
33 check_lib.checkExtract("i32.const {foo_address}");
34 check_lib.checkExact("type i32");
35 check_lib.checkExact("mutable false");
36 check_lib.checkExtract("i32.const {bar_address}");
37 check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 4 } });
38 check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 0 } });
39
40 check_lib.checkInHeaders();
41 check_lib.checkExact("Section export");
42 check_lib.checkExact("entries 3");
43 check_lib.checkExact("name foo");
44 check_lib.checkExact("kind global");
45 check_lib.checkExact("index 1");
46 check_lib.checkExact("name bar");
47 check_lib.checkExact("kind global");
48 check_lib.checkExact("index 2");
4925
50 test_step.dependOn(&check_lib.step);26 test_step.dependOn(&lib.step);
51}27}
test/link/wasm/export/build.zig+2-7
...@@ -1,22 +1,17 @@...@@ -1,22 +1,17 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
5pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test it");4 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;5 b.default_step = test_step;
86
9 add(b, test_step, .Debug);7 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}8}
149
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {10fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const no_export = b.addExecutable(.{11 const no_export = b.addExecutable(.{
17 .name = "no-export",12 .name = "no-export",
18 .root_module = b.createModule(.{13 .root_module = b.createModule(.{
19 .root_source_file = b.path("main.zig"),14 .root_source_file = b.path("main-hidden.zig"),
20 .optimize = optimize,15 .optimize = optimize,
21 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),16 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
22 }),17 }),
...@@ -41,7 +36,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize...@@ -41,7 +36,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
41 const force_export = b.addExecutable(.{36 const force_export = b.addExecutable(.{
42 .name = "force",37 .name = "force",
43 .root_module = b.createModule(.{38 .root_module = b.createModule(.{
44 .root_source_file = b.path("main.zig"),39 .root_source_file = b.path("main-hidden.zig"),
45 .optimize = optimize,40 .optimize = optimize,
46 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),41 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
47 }),42 }),
test/link/wasm/export/main-hidden.zig created+4
...@@ -0,0 +1,4 @@
1fn foo() callconv(.c) void {}
2comptime {
3 @export(&foo, .{ .name = "foo", .visibility = .hidden });
4}
test/link/wasm/extern/build.zig-5
...@@ -1,15 +1,10 @@...@@ -1,15 +1,10 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
5pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test it");4 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;5 b.default_step = test_step;
86
9 add(b, test_step, .Debug);7 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}8}
149
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {10fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
test/link/wasm/function-table/build.zig+1-33
...@@ -1,32 +1,13 @@...@@ -1,32 +1,13 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
5pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test it");4 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;5 b.default_step = test_step;
86
9 add(b, test_step, .Debug);7 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}8}
149
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {10fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const import_table = b.addExecutable(.{
17 .name = "import_table",
18 .root_module = b.createModule(.{
19 .root_source_file = b.path("lib.zig"),
20 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
21 .optimize = optimize,
22 }),
23 });
24 import_table.entry = .disabled;
25 import_table.use_llvm = false;
26 import_table.use_lld = false;
27 import_table.import_table = true;
28 import_table.link_gc_sections = false;
29
30 const export_table = b.addExecutable(.{11 const export_table = b.addExecutable(.{
31 .name = "export_table",12 .name = "export_table",
32 .root_module = b.createModule(.{13 .root_module = b.createModule(.{
...@@ -54,24 +35,12 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize...@@ -54,24 +35,12 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
54 regular_table.use_lld = false;35 regular_table.use_lld = false;
55 regular_table.link_gc_sections = false; // Ensure function table is not empty36 regular_table.link_gc_sections = false; // Ensure function table is not empty
5637
57 const check_import = import_table.checkObject();
58 const check_export = export_table.checkObject();38 const check_export = export_table.checkObject();
59 const check_regular = regular_table.checkObject();39 const check_regular = regular_table.checkObject();
6040
61 check_import.checkInHeaders();
62 check_import.checkExact("Section import");
63 check_import.checkExact("entries 1");
64 check_import.checkExact("module env");
65 check_import.checkExact("name __indirect_function_table");
66 check_import.checkExact("kind table");
67 check_import.checkExact("type funcref");
68 check_import.checkExact("min 1"); // 1 function pointer
69 check_import.checkNotPresent("max"); // when importing, we do not provide a max
70 check_import.checkNotPresent("Section table"); // we're importing it
71
72 check_export.checkInHeaders();41 check_export.checkInHeaders();
73 check_export.checkExact("Section export");42 check_export.checkExact("Section export");
74 check_export.checkExact("entries 2");43 check_export.checkExact("entries 3");
75 check_export.checkExact("name __indirect_function_table"); // as per linker specification44 check_export.checkExact("name __indirect_function_table"); // as per linker specification
76 check_export.checkExact("kind table");45 check_export.checkExact("kind table");
7746
...@@ -89,7 +58,6 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize...@@ -89,7 +58,6 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
89 check_regular.checkExact("i32.const 1"); // we want to start function indexes at 158 check_regular.checkExact("i32.const 1"); // we want to start function indexes at 1
90 check_regular.checkExact("indexes 1"); // 1 function pointer59 check_regular.checkExact("indexes 1"); // 1 function pointer
9160
92 test_step.dependOn(&check_import.step);
93 test_step.dependOn(&check_export.step);61 test_step.dependOn(&check_export.step);
94 test_step.dependOn(&check_regular.step);62 test_step.dependOn(&check_regular.step);
95}63}
test/link/wasm/infer-features/build.zig+3-22
...@@ -1,7 +1,5 @@...@@ -1,7 +1,5 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
5pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
6 // Wasm Object file which we will use to infer the features from4 // Wasm Object file which we will use to infer the features from
7 const c_obj = b.addObject(.{5 const c_obj = b.addObject(.{
...@@ -37,27 +35,10 @@ pub fn build(b: *std.Build) void {...@@ -37,27 +35,10 @@ pub fn build(b: *std.Build) void {
37 lib.use_lld = false;35 lib.use_lld = false;
38 lib.root_module.addObject(c_obj);36 lib.root_module.addObject(c_obj);
3937
40 // Verify the result contains the features from the C Object file.38 lib.expect_errors = .{ .contains = "error: object requires atomics but specified target features exclude atomics" };
41 const check = lib.checkObject();39 _ = lib.getEmittedBin();
42 check.checkInHeaders();
43 check.checkExact("name target_features");
44 check.checkExact("features 14");
45 check.checkExact("+ atomics");
46 check.checkExact("+ bulk-memory");
47 check.checkExact("+ exception-handling");
48 check.checkExact("+ extended-const");
49 check.checkExact("+ half-precision");
50 check.checkExact("+ multimemory");
51 check.checkExact("+ multivalue");
52 check.checkExact("+ mutable-globals");
53 check.checkExact("+ nontrapping-fptoint");
54 check.checkExact("+ reference-types");
55 check.checkExact("+ relaxed-simd");
56 check.checkExact("+ sign-ext");
57 check.checkExact("+ simd128");
58 check.checkExact("+ tail-call");
5940
60 const test_step = b.step("test", "Run linker test");41 const test_step = b.step("test", "Run linker test");
61 test_step.dependOn(&check.step);42 test_step.dependOn(&lib.step);
62 b.default_step = test_step;43 b.default_step = test_step;
63}44}
test/link/wasm/producers/build.zig-2
...@@ -1,8 +1,6 @@...@@ -1,8 +1,6 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub const requires_stage2 = true;
5
6pub fn build(b: *std.Build) void {4pub fn build(b: *std.Build) void {
7 const test_step = b.step("test", "Test it");5 const test_step = b.step("test", "Test it");
8 b.default_step = test_step;6 b.default_step = test_step;
test/link/wasm/segments/build.zig deleted-48
...@@ -1,48 +0,0 @@
1const std = @import("std");
2
3pub const requires_stage2 = true;
4
5pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const lib = b.addExecutable(.{
17 .name = "lib",
18 .root_module = b.createModule(.{
19 .root_source_file = b.path("lib.zig"),
20 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
21 .optimize = optimize,
22 .strip = false,
23 }),
24 });
25 lib.entry = .disabled;
26 lib.use_llvm = false;
27 lib.use_lld = false;
28 lib.link_gc_sections = false; // so data is not garbage collected and we can verify data section
29 b.installArtifact(lib);
30
31 const check_lib = lib.checkObject();
32 check_lib.checkInHeaders();
33 check_lib.checkExact("Section data");
34 check_lib.checkExact("entries 2"); // rodata & data, no bss because we're exporting memory
35
36 check_lib.checkInHeaders();
37 check_lib.checkExact("Section custom");
38 check_lib.checkInHeaders();
39 check_lib.checkExact("name name"); // names custom section
40 check_lib.checkInHeaders();
41 check_lib.checkExact("type data_segment");
42 check_lib.checkExact("names 2");
43 check_lib.checkExact("index 0");
44 check_lib.checkExact("name .rodata");
45 check_lib.checkExact("index 1");
46 check_lib.checkExact("name .data");
47 test_step.dependOn(&check_lib.step);
48}
test/link/wasm/segments/lib.zig deleted-9
...@@ -1,9 +0,0 @@
1pub const rodata: u32 = 5;
2pub var data: u32 = 10;
3pub var bss: u32 = undefined;
4
5export fn foo() void {
6 _ = rodata;
7 _ = data;
8 _ = bss;
9}
test/link/wasm/shared-memory/build.zig+11-8
...@@ -6,8 +6,6 @@ pub fn build(b: *std.Build) void {...@@ -6,8 +6,6 @@ pub fn build(b: *std.Build) void {
66
7 add(b, test_step, .Debug);7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}9}
1210
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void {11fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void {
...@@ -45,6 +43,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt...@@ -45,6 +43,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
45 check_exe.checkInHeaders();43 check_exe.checkInHeaders();
46 check_exe.checkExact("Section export");44 check_exe.checkExact("Section export");
47 check_exe.checkExact("entries 2");45 check_exe.checkExact("entries 2");
46 check_exe.checkExact("name foo");
48 check_exe.checkExact("name memory"); // ensure we also export memory again47 check_exe.checkExact("name memory"); // ensure we also export memory again
4948
50 // This section *must* be emit as the start function is set to the index49 // This section *must* be emit as the start function is set to the index
...@@ -71,23 +70,27 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt...@@ -71,23 +70,27 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
71 check_exe.checkExact("type function");70 check_exe.checkExact("type function");
72 if (optimize_mode == .Debug) {71 if (optimize_mode == .Debug) {
73 check_exe.checkExact("name __wasm_init_memory");72 check_exe.checkExact("name __wasm_init_memory");
73 check_exe.checkExact("name __wasm_init_tls");
74 }74 }
75 check_exe.checkExact("name __wasm_init_tls");
76 check_exe.checkExact("type global");75 check_exe.checkExact("type global");
7776
78 // In debug mode the symbol __tls_base is resolved to an undefined symbol77 // In debug mode the symbol __tls_base is resolved to an undefined symbol
79 // from the object file, hence its placement differs than in release modes78 // from the object file, hence its placement differs than in release modes
80 // where the entire tls segment is optimized away, and tls_base will have79 // where the entire tls segment is optimized away, and tls_base will have
81 // its original position.80 // its original position.
82 check_exe.checkExact("name __tls_base");
83 check_exe.checkExact("name __tls_size");
84 check_exe.checkExact("name __tls_align");
85
86 check_exe.checkExact("type data_segment");
87 if (optimize_mode == .Debug) {81 if (optimize_mode == .Debug) {
82 check_exe.checkExact("name __tls_base");
83 check_exe.checkExact("name __tls_size");
84 check_exe.checkExact("name __tls_align");
85
86 check_exe.checkExact("type data_segment");
88 check_exe.checkExact("names 1");87 check_exe.checkExact("names 1");
89 check_exe.checkExact("index 0");88 check_exe.checkExact("index 0");
90 check_exe.checkExact("name .tdata");89 check_exe.checkExact("name .tdata");
90 } else {
91 check_exe.checkNotPresent("name __tls_base");
92 check_exe.checkNotPresent("name __tls_size");
93 check_exe.checkNotPresent("name __tls_align");
91 }94 }
9295
93 test_step.dependOn(&check_exe.step);96 test_step.dependOn(&check_exe.step);
test/link/wasm/stack_pointer/build.zig-2
...@@ -1,7 +1,5 @@...@@ -1,7 +1,5 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
5pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test it");4 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;5 b.default_step = test_step;
test/link/wasm/type/build.zig-5
...@@ -1,15 +1,10 @@...@@ -1,15 +1,10 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
5pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test it");4 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;5 b.default_step = test_step;
86
9 add(b, test_step, .Debug);7 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}8}
149
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {10fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
test/standalone/test_runner_path/build.zig-2
...@@ -1,7 +1,5 @@...@@ -1,7 +1,5 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
5pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test the program");4 const test_step = b.step("test", "Test the program");
7 b.default_step = test_step;5 b.default_step = test_step;
test/tests.zig+5
...@@ -1375,6 +1375,7 @@ const ModuleTestOptions = struct {...@@ -1375,6 +1375,7 @@ const ModuleTestOptions = struct {
1375 skip_single_threaded: bool,1375 skip_single_threaded: bool,
1376 skip_non_native: bool,1376 skip_non_native: bool,
1377 skip_libc: bool,1377 skip_libc: bool,
1378 use_llvm: ?bool = null,
1378 max_rss: usize = 0,1379 max_rss: usize = 0,
1379 no_builtin: bool = false,1380 no_builtin: bool = false,
1380 build_options: ?*std.Build.Step.Options = null,1381 build_options: ?*std.Build.Step.Options = null,
...@@ -1411,6 +1412,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {...@@ -1411,6 +1412,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
1411 if (options.skip_single_threaded and test_target.single_threaded == true)1412 if (options.skip_single_threaded and test_target.single_threaded == true)
1412 continue;1413 continue;
14131414
1415 if (options.use_llvm) |use_llvm| {
1416 if (test_target.use_llvm != use_llvm) continue;
1417 }
1418
1414 // TODO get compiler-rt tests passing for self-hosted backends.1419 // TODO get compiler-rt tests passing for self-hosted backends.
1415 if ((target.cpu.arch != .x86_64 or target.ofmt != .elf) and1420 if ((target.cpu.arch != .x86_64 or target.ofmt != .elf) and
1416 test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt"))1421 test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt"))