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
643643 src/link/StringTable.zig
644644 src/link/Wasm.zig
645645 src/link/Wasm/Archive.zig
646 src/link/Wasm/Flush.zig
646647 src/link/Wasm/Object.zig
647 src/link/Wasm/Symbol.zig
648 src/link/Wasm/ZigObject.zig
649648 src/link/aarch64.zig
650649 src/link/riscv.zig
651650 src/link/table_section.zig
build.zig+5
......@@ -447,6 +447,7 @@ pub fn build(b: *std.Build) !void {
447447 .skip_single_threaded = skip_single_threaded,
448448 .skip_non_native = skip_non_native,
449449 .skip_libc = skip_libc,
450 .use_llvm = use_llvm,
450451 .max_rss = 1 * 1024 * 1024 * 1024,
451452 }));
452453
......@@ -462,6 +463,7 @@ pub fn build(b: *std.Build) !void {
462463 .skip_single_threaded = true,
463464 .skip_non_native = skip_non_native,
464465 .skip_libc = skip_libc,
466 .use_llvm = use_llvm,
465467 }));
466468
467469 test_modules_step.dependOn(tests.addModuleTests(b, .{
......@@ -476,6 +478,7 @@ pub fn build(b: *std.Build) !void {
476478 .skip_single_threaded = true,
477479 .skip_non_native = skip_non_native,
478480 .skip_libc = true,
481 .use_llvm = use_llvm,
479482 .no_builtin = true,
480483 }));
481484
......@@ -491,6 +494,7 @@ pub fn build(b: *std.Build) !void {
491494 .skip_single_threaded = true,
492495 .skip_non_native = skip_non_native,
493496 .skip_libc = true,
497 .use_llvm = use_llvm,
494498 .no_builtin = true,
495499 }));
496500
......@@ -506,6 +510,7 @@ pub fn build(b: *std.Build) !void {
506510 .skip_single_threaded = skip_single_threaded,
507511 .skip_non_native = skip_non_native,
508512 .skip_libc = skip_libc,
513 .use_llvm = use_llvm,
509514 // I observed a value of 4572626944 on the M2 CI.
510515 .max_rss = 5029889638,
511516 }));
lib/std/Build/Step/CheckObject.zig+17-4
......@@ -2424,7 +2424,22 @@ const WasmDumper = struct {
24242424 }
24252425
24262426 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();
24282443 const writer = output.writer();
24292444
24302445 switch (check.kind) {
......@@ -2442,8 +2457,6 @@ const WasmDumper = struct {
24422457
24432458 else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}),
24442459 }
2445
2446 return output.toOwnedSlice();
24472460 }
24482461
24492462 fn parseAndDumpSection(
......@@ -2682,7 +2695,7 @@ const WasmDumper = struct {
26822695 else => unreachable,
26832696 }
26842697 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)) {
26862699 return step.fail("expected 'end' opcode in init expression", .{});
26872700 }
26882701 }
lib/std/Target.zig+6
......@@ -1219,6 +1219,12 @@ pub const Cpu = struct {
12191219 } else true;
12201220 }
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
12221228 pub fn isEnabled(set: Set, arch_feature_index: Index) bool {
12231229 const usize_index = arch_feature_index / @bitSizeOf(usize);
12241230 const bit_index: ShiftInt = @intCast(arch_feature_index % @bitSizeOf(usize));
lib/std/Thread.zig+8-5
......@@ -1018,12 +1018,15 @@ const WasiThreadImpl = struct {
10181018 return .{ .thread = &instance.thread };
10191019 }
10201020
1021 /// Bootstrap procedure, called by the host environment after thread creation.
1022 export fn wasi_thread_start(tid: i32, arg: *Instance) void {
1023 if (builtin.single_threaded) {
1024 // ensure function is not analyzed in single-threaded mode
1025 return;
1021 comptime {
1022 if (!builtin.single_threaded) {
1023 @export(wasi_thread_start, .{ .name = "wasi_thread_start" });
10261024 }
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);
10271030 __set_stack_pointer(arg.thread.memory.ptr + arg.stack_offset);
10281031 __wasm_init_tls(arg.thread.memory.ptr + arg.tls_offset);
10291032 @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(
641641 return self;
642642 }
643643
644 /// An empty `value_list` may be passed, in which case the values array becomes `undefined`.
644645 pub fn reinit(self: *Self, gpa: Allocator, key_list: []const K, value_list: []const V) Oom!void {
645646 try self.entries.resize(gpa, key_list.len);
646647 @memcpy(self.keys(), key_list);
647 if (@sizeOf(V) != 0) {
648 if (value_list.len == 0) {
649 @memset(self.values(), undefined);
650 } else {
648651 assert(key_list.len == value_list.len);
649652 @memcpy(self.values(), value_list);
650653 }
lib/std/array_list.zig+2-4
......@@ -267,8 +267,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
267267 /// Never invalidates element pointers.
268268 /// Asserts that the list can hold one additional item.
269269 pub fn appendAssumeCapacity(self: *Self, item: T) void {
270 const new_item_ptr = self.addOneAssumeCapacity();
271 new_item_ptr.* = item;
270 self.addOneAssumeCapacity().* = item;
272271 }
273272
274273 /// Remove the element at index `i`, shift elements after index
......@@ -879,8 +878,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
879878 /// Never invalidates element pointers.
880879 /// Asserts that the list can hold one additional item.
881880 pub fn appendAssumeCapacity(self: *Self, item: T) void {
882 const new_item_ptr = self.addOneAssumeCapacity();
883 new_item_ptr.* = item;
881 self.addOneAssumeCapacity().* = item;
884882 }
885883
886884 /// 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;
1616
1717fn getStdOutHandle() posix.fd_t {
1818 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 }
2319 return windows.peb().ProcessParameters.hStdOutput;
2420 }
2521
......@@ -36,10 +32,6 @@ pub fn getStdOut() File {
3632
3733fn getStdErrHandle() posix.fd_t {
3834 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 }
4335 return windows.peb().ProcessParameters.hStdError;
4436 }
4537
......@@ -56,10 +48,6 @@ pub fn getStdErr() File {
5648
5749fn getStdInHandle() posix.fd_t {
5850 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 }
6351 return windows.peb().ProcessParameters.hStdInput;
6452 }
6553
lib/std/wasm.zig+17-180
......@@ -4,8 +4,6 @@
44const std = @import("std.zig");
55const testing = std.testing;
66
7// TODO: Add support for multi-byte ops (e.g. table operations)
8
97/// Wasm instruction opcodes
108///
119/// All instructions are defined as per spec:
......@@ -195,27 +193,6 @@ pub const Opcode = enum(u8) {
195193 _,
196194};
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
219196/// Opcodes that require a prefix `0xFC`.
220197/// Each opcode represents a varuint32, meaning
221198/// they are encoded as leb128 in binary.
......@@ -241,12 +218,6 @@ pub const MiscOpcode = enum(u32) {
241218 _,
242219};
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
250221/// Simd opcodes that require a prefix `0xFD`.
251222/// Each opcode represents a varuint32, meaning
252223/// they are encoded as leb128 in binary.
......@@ -512,12 +483,6 @@ pub const SimdOpcode = enum(u32) {
512483 f32x4_relaxed_dot_bf16x8_add_f32x4 = 0x114,
513484};
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
521486/// Atomic opcodes that require a prefix `0xFE`.
522487/// Each opcode represents a varuint32, meaning
523488/// they are encoded as leb128 in binary.
......@@ -592,12 +557,6 @@ pub const AtomicsOpcode = enum(u32) {
592557 i64_atomic_rmw32_cmpxchg_u = 0x4E,
593558};
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
601560/// Enum representing all Wasm value types as per spec:
602561/// https://webassembly.github.io/spec/core/binary/types.html
603562pub const Valtype = enum(u8) {
......@@ -608,11 +567,6 @@ pub const Valtype = enum(u8) {
608567 v128 = 0x7B,
609568};
610569
611/// Returns the integer value of a `Valtype`
612pub fn valtype(value: Valtype) u8 {
613 return @intFromEnum(value);
614}
615
616570/// Reference types, where the funcref references to a function regardless of its type
617571/// and ref references an object from the embedder.
618572pub const RefType = enum(u8) {
......@@ -620,41 +574,17 @@ pub const RefType = enum(u8) {
620574 externref = 0x6F,
621575};
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
640577/// Limits classify the size range of resizeable storage associated with memory types and table types.
641578pub const Limits = struct {
642 flags: u8,
579 flags: Flags,
643580 min: u32,
644581 max: u32,
645582
646 pub const Flags = enum(u8) {
647 WASM_LIMITS_FLAG_HAS_MAX = 0x1,
648 WASM_LIMITS_FLAG_IS_SHARED = 0x2,
583 pub const Flags = packed struct(u8) {
584 has_max: bool,
585 is_shared: bool,
586 reserved: u6 = 0,
649587 };
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 }
658588};
659589
660590/// Initialization expressions are used to set the initial value on an object
......@@ -667,18 +597,6 @@ pub const InitExpression = union(enum) {
667597 global_get: u32,
668598};
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
682600/// Describes the layout of the memory where `min` represents
683601/// the minimal amount of pages, and the optional `max` represents
684602/// the max pages. When `null` will allow the host to determine the
......@@ -687,88 +605,6 @@ pub const Memory = struct {
687605 limits: Limits,
688606};
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
772608/// Wasm module sections as per spec:
773609/// https://webassembly.github.io/spec/core/binary/modules.html
774610pub const Section = enum(u8) {
......@@ -788,11 +624,6 @@ pub const Section = enum(u8) {
788624 _,
789625};
790626
791/// Returns the integer value of a given `Section`
792pub fn section(val: Section) u8 {
793 return @intFromEnum(val);
794}
795
796627/// The kind of the type when importing or exporting to/from the host environment.
797628/// https://webassembly.github.io/spec/core/syntax/modules.html
798629pub const ExternalKind = enum(u8) {
......@@ -802,11 +633,6 @@ pub const ExternalKind = enum(u8) {
802633 global,
803634};
804635
805/// Returns the integer value of a given `ExternalKind`
806pub fn externalKind(val: ExternalKind) u8 {
807 return @intFromEnum(val);
808}
809
810636/// Defines the enum values for each subsection id for the "Names" custom section
811637/// as described by:
812638/// https://webassembly.github.io/spec/core/appendix/custom.html?highlight=name#name-section
......@@ -829,7 +655,18 @@ pub const function_type: u8 = 0x60;
829655pub const result_type: u8 = 0x40;
830656
831657/// 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
834671// binary constants
835672pub const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm
lib/std/zig/ErrorBundle.zig+15-13
......@@ -11,6 +11,11 @@ string_bytes: []const u8,
1111/// The first thing in this array is an `ErrorMessageList`.
1212extra: []const u32,
1313
14/// Index into `string_bytes`.
15pub const String = u32;
16/// Index into `string_bytes`, or null.
17pub const OptionalString = u32;
18
1419/// Special encoding when there are no errors.
1520pub const empty: ErrorBundle = .{
1621 .string_bytes = &.{},
......@@ -33,14 +38,13 @@ pub const ErrorMessageList = struct {
3338 len: u32,
3439 start: u32,
3540 /// null-terminated string index. 0 means no compile log text.
36 compile_log_text: u32,
41 compile_log_text: OptionalString,
3742};
3843
3944/// Trailing:
4045/// * ReferenceTrace for each reference_trace_len
4146pub const SourceLocation = struct {
42 /// null terminated string index
43 src_path: u32,
47 src_path: String,
4448 line: u32,
4549 column: u32,
4650 /// byte offset of starting token
......@@ -49,17 +53,15 @@ pub const SourceLocation = struct {
4953 span_main: u32,
5054 /// byte offset of end of last token
5155 span_end: u32,
52 /// null terminated string index, possibly null.
5356 /// Does not include the trailing newline.
54 source_line: u32 = 0,
57 source_line: OptionalString = 0,
5558 reference_trace_len: u32 = 0,
5659};
5760
5861/// Trailing:
5962/// * MessageIndex for each notes_len.
6063pub const ErrorMessage = struct {
61 /// null terminated string index
62 msg: u32,
64 msg: String,
6365 /// Usually one, but incremented for redundant messages.
6466 count: u32 = 1,
6567 src_loc: SourceLocationIndex = .none,
......@@ -71,7 +73,7 @@ pub const ReferenceTrace = struct {
7173 /// Except for the sentinel ReferenceTrace element, in which case:
7274 /// * 0 means remaining references hidden
7375 /// * >0 means N references hidden
74 decl_name: u32,
76 decl_name: String,
7577 /// Index into extra of a SourceLocation
7678 /// If this is 0, this is the sentinel ReferenceTrace element.
7779 src_loc: SourceLocationIndex,
......@@ -138,7 +140,7 @@ fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T,
138140}
139141
140142/// 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 {
142144 const string_bytes = eb.string_bytes;
143145 var end: usize = index;
144146 while (string_bytes[end] != 0) {
......@@ -384,18 +386,18 @@ pub const Wip = struct {
384386 };
385387 }
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 {
388390 const gpa = wip.gpa;
389 const index: u32 = @intCast(wip.string_bytes.items.len);
391 const index: String = @intCast(wip.string_bytes.items.len);
390392 try wip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1);
391393 wip.string_bytes.appendSliceAssumeCapacity(s);
392394 wip.string_bytes.appendAssumeCapacity(0);
393395 return index;
394396 }
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 {
397399 const gpa = wip.gpa;
398 const index: u32 = @intCast(wip.string_bytes.items.len);
400 const index: String = @intCast(wip.string_bytes.items.len);
399401 try wip.string_bytes.writer(gpa).print(fmt, args);
400402 try wip.string_bytes.append(gpa, 0);
401403 return index;
src/Compilation.zig+164-8
......@@ -113,6 +113,14 @@ link_diags: link.Diags,
113113link_task_queue: ThreadSafeQueue(link.Task) = .empty,
114114/// Ensure only 1 simultaneous call to `flushTaskQueue`.
115115link_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
117125work_queues: [
118126 len: {
......@@ -1515,6 +1523,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
15151523 .file_system_inputs = options.file_system_inputs,
15161524 .parent_whole_cache = options.parent_whole_cache,
15171525 .link_diags = .init(gpa),
1526 .remaining_prelink_tasks = 0,
15181527 };
15191528
15201529 // 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
15871596 .pdb_source_path = options.pdb_source_path,
15881597 .pdb_out_path = options.pdb_out_path,
15891598 .entry_addr = null, // CLI does not expose this option (yet?)
1599 .object_host_name = "env",
15901600 };
15911601
15921602 switch (options.cache_mode) {
......@@ -1715,6 +1725,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
17151725 };
17161726 comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});
17171727 }
1728 comp.remaining_prelink_tasks += @intCast(comp.c_object_table.count());
17181729
17191730 // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`.
17201731 const win32_resource_count =
......@@ -1722,6 +1733,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
17221733 if (win32_resource_count > 0) {
17231734 dev.check(.win32_resource);
17241735 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);
17251740 for (options.rc_source_files) |rc_source_file| {
17261741 const win32_resource = try gpa.create(Win32Resource);
17271742 errdefer gpa.destroy(win32_resource);
......@@ -1732,6 +1747,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
17321747 };
17331748 comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {});
17341749 }
1750
17351751 if (options.manifest_file) |manifest_path| {
17361752 const win32_resource = try gpa.create(Win32Resource);
17371753 errdefer gpa.destroy(win32_resource);
......@@ -1779,10 +1795,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
17791795 inline for (fields) |field| {
17801796 if (@field(paths, field.name)) |path| {
17811797 comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path });
1798 comp.remaining_prelink_tasks += 1;
17821799 }
17831800 }
17841801 // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`.
17851802 comp.link_task_queue.shared.appendAssumeCapacity(.load_host_libc);
1803 comp.remaining_prelink_tasks += 1;
17861804 } else if (target.isMusl() and !target.isWasm()) {
17871805 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
17881806
......@@ -1791,14 +1809,17 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
17911809 .{ .musl_crt_file = .crti_o },
17921810 .{ .musl_crt_file = .crtn_o },
17931811 });
1812 comp.remaining_prelink_tasks += 2;
17941813 }
17951814 if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| {
17961815 try comp.queueJobs(&.{.{ .musl_crt_file = f }});
1816 comp.remaining_prelink_tasks += 1;
17971817 }
17981818 try comp.queueJobs(&.{.{ .musl_crt_file = switch (comp.config.link_mode) {
17991819 .static => .libc_a,
18001820 .dynamic => .libc_so,
18011821 } }});
1822 comp.remaining_prelink_tasks += 1;
18021823 } else if (target.isGnuLibC()) {
18031824 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
18041825
......@@ -1807,14 +1828,18 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
18071828 .{ .glibc_crt_file = .crti_o },
18081829 .{ .glibc_crt_file = .crtn_o },
18091830 });
1831 comp.remaining_prelink_tasks += 2;
18101832 }
18111833 if (glibc.needsCrt0(comp.config.output_mode)) |f| {
18121834 try comp.queueJobs(&.{.{ .glibc_crt_file = f }});
1835 comp.remaining_prelink_tasks += 1;
18131836 }
18141837 try comp.queueJobs(&[_]Job{
18151838 .{ .glibc_shared_objects = {} },
18161839 .{ .glibc_crt_file = .libc_nonshared_a },
18171840 });
1841 comp.remaining_prelink_tasks += 1;
1842 comp.remaining_prelink_tasks += glibc.sharedObjectsCount(&target);
18181843 } else if (target.isWasm() and target.os.tag == .wasi) {
18191844 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
18201845
......@@ -1822,11 +1847,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
18221847 try comp.queueJob(.{
18231848 .wasi_libc_crt_file = crt_file,
18241849 });
1850 comp.remaining_prelink_tasks += 1;
18251851 }
18261852 try comp.queueJobs(&[_]Job{
18271853 .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) },
18281854 .{ .wasi_libc_crt_file = .libc_a },
18291855 });
1856 comp.remaining_prelink_tasks += 2;
18301857 } else if (target.isMinGW()) {
18311858 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
18321859
......@@ -1835,6 +1862,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
18351862 .{ .mingw_crt_file = .mingw32_lib },
18361863 crt_job,
18371864 });
1865 comp.remaining_prelink_tasks += 2;
18381866
18391867 // When linking mingw-w64 there are some import libs we always need.
18401868 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
18461874 }
18471875 } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) {
18481876 try comp.queueJob(.{ .zig_libc = {} });
1877 comp.remaining_prelink_tasks += 1;
18491878 } else {
18501879 return error.LibCUnavailable;
18511880 }
......@@ -1860,13 +1889,16 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
18601889 }
18611890 if (comp.wantBuildLibUnwindFromSource()) {
18621891 try comp.queueJob(.{ .libunwind = {} });
1892 comp.remaining_prelink_tasks += 1;
18631893 }
18641894 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
18651895 try comp.queueJob(.libcxx);
18661896 try comp.queueJob(.libcxxabi);
1897 comp.remaining_prelink_tasks += 2;
18671898 }
18681899 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) {
18691900 try comp.queueJob(.libtsan);
1901 comp.remaining_prelink_tasks += 1;
18701902 }
18711903
18721904 if (target.isMinGW() and comp.config.any_non_single_threaded) {
......@@ -1885,22 +1917,27 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
18851917 if (is_exe_or_dyn_lib) {
18861918 log.debug("queuing a job to build compiler_rt_lib", .{});
18871919 comp.job_queued_compiler_rt_lib = true;
1920 comp.remaining_prelink_tasks += 1;
18881921 } else if (output_mode != .Obj) {
18891922 log.debug("queuing a job to build compiler_rt_obj", .{});
18901923 // In this case we are making a static library, so we ask
18911924 // for a compiler-rt object to put in it.
18921925 comp.job_queued_compiler_rt_obj = true;
1926 comp.remaining_prelink_tasks += 1;
18931927 }
18941928 }
18951929
18961930 if (is_exe_or_dyn_lib and comp.config.any_fuzz and capable_of_building_compiler_rt) {
18971931 log.debug("queuing a job to build libfuzzer", .{});
18981932 comp.job_queued_fuzzer_lib = true;
1933 comp.remaining_prelink_tasks += 1;
18991934 }
19001935 }
19011936
19021937 try comp.link_task_queue.shared.append(gpa, .load_explicitly_provided);
1938 comp.remaining_prelink_tasks += 1;
19031939 }
1940 log.debug("total prelink tasks: {d}", .{comp.remaining_prelink_tasks});
19041941
19051942 return comp;
19061943}
......@@ -1976,6 +2013,7 @@ pub fn destroy(comp: *Compilation) void {
19762013
19772014 comp.link_diags.deinit();
19782015 comp.link_task_queue.deinit(gpa);
2016 comp.link_task_queue_postponed.deinit(gpa);
19792017
19802018 comp.clearMiscFailures();
19812019
......@@ -2438,9 +2476,8 @@ fn flush(
24382476 if (comp.bin_file) |lf| {
24392477 // This is needed before reading the error flags.
24402478 lf.flush(arena, tid, prog_node) catch |err| switch (err) {
2441 error.FlushFailure, error.LinkFailure => {}, // error reported through link_diags.flags
2442 error.LLDReportedFailure => {}, // error reported via lockAndParseLldStderr
2443 else => |e| return e,
2479 error.LinkFailure => {}, // Already reported.
2480 error.OutOfMemory => return error.OutOfMemory,
24442481 };
24452482 }
24462483
......@@ -3025,8 +3062,120 @@ pub fn saveState(comp: *Compilation) !void {
30253062 //// TODO: compilation errors
30263063 //// TODO: namespaces
30273064 //// TODO: decls
3028 //// TODO: linker state
30293065 }
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
30303179 var basename_buf: [255]u8 = undefined;
30313180 const basename = std.fmt.bufPrint(&basename_buf, "{s}.zcs", .{
30323181 comp.root_name,
......@@ -3209,6 +3358,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
32093358 if (!zcu.navFileScope(nav).okToReportErrors()) continue;
32103359 try addModuleErrorMsg(zcu, &bundle, error_msg.*);
32113360 }
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 }
32123365 for (zcu.failed_exports.values()) |value| {
32133366 try addModuleErrorMsg(zcu, &bundle, value.*);
32143367 }
......@@ -3252,7 +3405,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
32523405 }));
32533406 }
32543407
3255 try comp.link_diags.addMessagesToBundle(&bundle);
3408 try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file);
32563409
32573410 if (comp.zcu) |zcu| {
32583411 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
......@@ -3524,9 +3677,9 @@ pub fn performAllTheWork(
35243677
35253678 defer if (comp.zcu) |zcu| {
35263679 zcu.sema_prog_node.end();
3527 zcu.sema_prog_node = std.Progress.Node.none;
3680 zcu.sema_prog_node = .none;
35283681 zcu.codegen_prog_node.end();
3529 zcu.codegen_prog_node = std.Progress.Node.none;
3682 zcu.codegen_prog_node = .none;
35303683
35313684 zcu.generation += 1;
35323685 };
......@@ -3659,7 +3812,7 @@ fn performAllTheWorkInner(
36593812 try zcu.flushRetryableFailures();
36603813
36613814 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;
36633816 }
36643817
36653818 if (!comp.separateCodegenThreadOk()) {
......@@ -3689,6 +3842,8 @@ fn performAllTheWorkInner(
36893842 });
36903843 continue;
36913844 }
3845 zcu.sema_prog_node.end();
3846 zcu.sema_prog_node = .none;
36923847 }
36933848 break;
36943849 }
......@@ -3962,6 +4117,7 @@ fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) voi
39624117 if (comp.separateCodegenThreadOk()) {
39634118 comp.queueLinkTasks(&.{link_task});
39644119 } else {
4120 assert(comp.remaining_prelink_tasks == 0);
39654121 link.doTask(comp, tid, link_task);
39664122 }
39674123}
src/InternPool.zig+41-1
......@@ -552,6 +552,15 @@ pub const Nav = struct {
552552 };
553553 }
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
555564 /// Always returns `null` for `status == .type_resolved`. This function is inteded
556565 /// to be used by code generation, since semantic analysis will ensure that any `Nav`
557566 /// which is potentially `extern` is fully resolved.
......@@ -585,6 +594,15 @@ pub const Nav = struct {
585594 };
586595 }
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
588606 /// Asserts that `status != .unresolved`.
589607 pub fn isThreadlocal(nav: Nav, ip: *const InternPool) bool {
590608 return switch (nav.status) {
......@@ -598,6 +616,20 @@ pub const Nav = struct {
598616 };
599617 }
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
601633 /// If this returns `true`, then a pointer to this `Nav` might actually be encoded as a pointer
602634 /// to some other `Nav` due to an extern definition or extern alias (see #21027).
603635 /// This query is valid on `Nav`s for whom only the type is resolved.
......@@ -3360,6 +3392,10 @@ pub const LoadedUnionType = struct {
33603392 return flags.status == .field_types_wip;
33613393 }
33623394
3395 pub fn requiresComptime(u: LoadedUnionType, ip: *const InternPool) RequiresComptime {
3396 return u.flagsUnordered(ip).requires_comptime;
3397 }
3398
33633399 pub fn setRequiresComptimeWip(u: LoadedUnionType, ip: *InternPool) RequiresComptime {
33643400 const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex;
33653401 extra_mutex.lock();
......@@ -4014,7 +4050,7 @@ pub const LoadedStructType = struct {
40144050 }
40154051 }
40164052
4017 pub fn haveLayout(s: LoadedStructType, ip: *InternPool) bool {
4053 pub fn haveLayout(s: LoadedStructType, ip: *const InternPool) bool {
40184054 return switch (s.layout) {
40194055 .@"packed" => s.backingIntTypeUnordered(ip) != .none,
40204056 .auto, .@"extern" => s.flagsUnordered(ip).layout_resolved,
......@@ -11797,6 +11833,10 @@ pub fn toEnum(ip: *const InternPool, comptime E: type, i: Index) E {
1179711833 return @enumFromInt(ip.indexToKey(int).int.storage.u64);
1179811834}
1179911835
11836pub fn toFunc(ip: *const InternPool, i: Index) Key.Func {
11837 return ip.indexToKey(i).func;
11838}
11839
1180011840pub fn aggregateTypeLen(ip: *const InternPool, ty: Index) u64 {
1180111841 return switch (ip.indexToKey(ty)) {
1180211842 .struct_type => ip.loadStructType(ty).field_types.len,
src/Sema.zig+5-5
......@@ -38298,7 +38298,7 @@ pub fn flushExports(sema: *Sema) !void {
3829838298 // So, pick up and delete any existing exports. This strategy performs
3829938299 // redundant work, but that's okay, because this case is exceedingly rare.
3830038300 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).*);
3830238302 } else if (zcu.multi_exports.get(sema.owner)) |info| {
3830338303 try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]);
3830438304 }
......@@ -38307,12 +38307,12 @@ pub fn flushExports(sema: *Sema) !void {
3830738307 // `sema.exports` is completed; store the data into the `Zcu`.
3830838308 if (sema.exports.items.len == 1) {
3830938309 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: {
3831138311 _ = 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);
3831338313 };
38314 zcu.all_exports.items[export_idx] = sema.exports.items[0];
38315 zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, @intCast(export_idx));
38314 export_idx.ptr(zcu).* = sema.exports.items[0];
38315 zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, export_idx);
3831638316 } else {
3831738317 try zcu.multi_exports.ensureUnusedCapacity(gpa, 1);
3831838318 const exports_base = zcu.all_exports.items.len;
src/Type.zig+108-85
......@@ -441,7 +441,7 @@ pub fn toValue(self: Type) Value {
441441
442442const RuntimeBitsError = SemaError || error{NeedLazy};
443443
444pub fn hasRuntimeBits(ty: Type, zcu: *Zcu) bool {
444pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool {
445445 return hasRuntimeBitsInner(ty, false, .eager, zcu, {}) catch unreachable;
446446}
447447
......@@ -452,7 +452,7 @@ pub fn hasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool {
452452 };
453453}
454454
455pub fn hasRuntimeBitsIgnoreComptime(ty: Type, zcu: *Zcu) bool {
455pub fn hasRuntimeBitsIgnoreComptime(ty: Type, zcu: *const Zcu) bool {
456456 return hasRuntimeBitsInner(ty, true, .eager, zcu, {}) catch unreachable;
457457}
458458
......@@ -471,7 +471,7 @@ pub fn hasRuntimeBitsInner(
471471 ty: Type,
472472 ignore_comptime_only: bool,
473473 comptime strat: ResolveStratLazy,
474 zcu: *Zcu,
474 zcu: strat.ZcuPtr(),
475475 tid: strat.Tid(),
476476) RuntimeBitsError!bool {
477477 const ip = &zcu.intern_pool;
......@@ -560,7 +560,7 @@ pub fn hasRuntimeBitsInner(
560560 },
561561 .struct_type => {
562562 const struct_type = ip.loadStructType(ty.toIntern());
563 if (struct_type.assumeRuntimeBitsIfFieldTypesWip(ip)) {
563 if (strat != .eager and struct_type.assumeRuntimeBitsIfFieldTypesWip(ip)) {
564564 // In this case, we guess that hasRuntimeBits() for this type is true,
565565 // and then later if our guess was incorrect, we emit a compile error.
566566 return true;
......@@ -596,7 +596,7 @@ pub fn hasRuntimeBitsInner(
596596 const union_type = ip.loadUnionType(ty.toIntern());
597597 const union_flags = union_type.flagsUnordered(ip);
598598 switch (union_flags.runtime_tag) {
599 .none => {
599 .none => if (strat != .eager) {
600600 // In this case, we guess that hasRuntimeBits() for this type is true,
601601 // and then later if our guess was incorrect, we emit a compile error.
602602 if (union_type.assumeRuntimeBitsIfFieldTypesWip(ip)) return true;
......@@ -774,7 +774,7 @@ pub fn fnHasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool {
774774pub fn fnHasRuntimeBitsInner(
775775 ty: Type,
776776 comptime strat: ResolveStrat,
777 zcu: *Zcu,
777 zcu: strat.ZcuPtr(),
778778 tid: strat.Tid(),
779779) SemaError!bool {
780780 const fn_info = zcu.typeToFunc(ty).?;
......@@ -815,7 +815,7 @@ pub fn ptrAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment {
815815pub fn ptrAlignmentInner(
816816 ty: Type,
817817 comptime strat: ResolveStrat,
818 zcu: *Zcu,
818 zcu: strat.ZcuPtr(),
819819 tid: strat.Tid(),
820820) !Alignment {
821821 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
......@@ -868,14 +868,25 @@ pub const ResolveStratLazy = enum {
868868 /// This should typically be used from semantic analysis.
869869 sema,
870870
871 pub fn Tid(comptime strat: ResolveStratLazy) type {
871 pub fn Tid(strat: ResolveStratLazy) type {
872872 return switch (strat) {
873873 .lazy, .sema => Zcu.PerThread.Id,
874874 .eager => void,
875875 };
876876 }
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) {
879890 .lazy, .sema => Zcu.PerThread,
880891 .eager => void,
881892 } {
......@@ -896,14 +907,21 @@ pub const ResolveStrat = enum {
896907 /// This should typically be used from semantic analysis.
897908 sema,
898909
899 pub fn Tid(comptime strat: ResolveStrat) type {
910 pub fn Tid(strat: ResolveStrat) type {
900911 return switch (strat) {
901912 .sema => Zcu.PerThread.Id,
902913 .normal => void,
903914 };
904915 }
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) {
907925 .sema => Zcu.PerThread,
908926 .normal => void,
909927 } {
......@@ -922,7 +940,7 @@ pub const ResolveStrat = enum {
922940};
923941
924942/// 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 {
926944 return (ty.abiAlignmentInner(.eager, zcu, {}) catch unreachable).scalar;
927945}
928946
......@@ -939,7 +957,7 @@ pub fn abiAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment {
939957pub fn abiAlignmentInner(
940958 ty: Type,
941959 comptime strat: ResolveStratLazy,
942 zcu: *Zcu,
960 zcu: strat.ZcuPtr(),
943961 tid: strat.Tid(),
944962) SemaError!AbiAlignmentInner {
945963 const pt = strat.pt(zcu, tid);
......@@ -1156,7 +1174,7 @@ pub fn abiAlignmentInner(
11561174fn abiAlignmentInnerErrorUnion(
11571175 ty: Type,
11581176 comptime strat: ResolveStratLazy,
1159 zcu: *Zcu,
1177 zcu: strat.ZcuPtr(),
11601178 tid: strat.Tid(),
11611179 payload_ty: Type,
11621180) SemaError!AbiAlignmentInner {
......@@ -1198,7 +1216,7 @@ fn abiAlignmentInnerErrorUnion(
11981216fn abiAlignmentInnerOptional(
11991217 ty: Type,
12001218 comptime strat: ResolveStratLazy,
1201 zcu: *Zcu,
1219 zcu: strat.ZcuPtr(),
12021220 tid: strat.Tid(),
12031221) SemaError!AbiAlignmentInner {
12041222 const pt = strat.pt(zcu, tid);
......@@ -1244,7 +1262,7 @@ const AbiSizeInner = union(enum) {
12441262
12451263/// Asserts the type has the ABI size already resolved.
12461264/// 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 {
12481266 return (abiSizeInner(ty, .eager, zcu, {}) catch unreachable).scalar;
12491267}
12501268
......@@ -1269,7 +1287,7 @@ pub fn abiSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 {
12691287pub fn abiSizeInner(
12701288 ty: Type,
12711289 comptime strat: ResolveStratLazy,
1272 zcu: *Zcu,
1290 zcu: strat.ZcuPtr(),
12731291 tid: strat.Tid(),
12741292) SemaError!AbiSizeInner {
12751293 const target = zcu.getTarget();
......@@ -1542,7 +1560,7 @@ pub fn abiSizeInner(
15421560fn abiSizeInnerOptional(
15431561 ty: Type,
15441562 comptime strat: ResolveStratLazy,
1545 zcu: *Zcu,
1563 zcu: strat.ZcuPtr(),
15461564 tid: strat.Tid(),
15471565) SemaError!AbiSizeInner {
15481566 const child_ty = ty.optionalChild(zcu);
......@@ -1701,7 +1719,7 @@ pub fn maxIntAlignment(target: std.Target, use_llvm: bool) u16 {
17011719 };
17021720}
17031721
1704pub fn bitSize(ty: Type, zcu: *Zcu) u64 {
1722pub fn bitSize(ty: Type, zcu: *const Zcu) u64 {
17051723 return bitSizeInner(ty, .normal, zcu, {}) catch unreachable;
17061724}
17071725
......@@ -1712,7 +1730,7 @@ pub fn bitSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 {
17121730pub fn bitSizeInner(
17131731 ty: Type,
17141732 comptime strat: ResolveStrat,
1715 zcu: *Zcu,
1733 zcu: strat.ZcuPtr(),
17161734 tid: strat.Tid(),
17171735) SemaError!u64 {
17181736 const target = zcu.getTarget();
......@@ -2148,7 +2166,7 @@ pub fn unionBackingType(ty: Type, pt: Zcu.PerThread) !Type {
21482166 };
21492167}
21502168
2151pub fn unionGetLayout(ty: Type, zcu: *Zcu) Zcu.UnionLayout {
2169pub fn unionGetLayout(ty: Type, zcu: *const Zcu) Zcu.UnionLayout {
21522170 const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern());
21532171 return Type.getUnionLayout(union_obj, zcu);
21542172}
......@@ -2746,7 +2764,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
27462764
27472765/// During semantic analysis, instead call `ty.comptimeOnlySema` which
27482766/// 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 {
27502768 return ty.comptimeOnlyInner(.normal, zcu, {}) catch unreachable;
27512769}
27522770
......@@ -2759,7 +2777,7 @@ pub fn comptimeOnlySema(ty: Type, pt: Zcu.PerThread) SemaError!bool {
27592777pub fn comptimeOnlyInner(
27602778 ty: Type,
27612779 comptime strat: ResolveStrat,
2762 zcu: *Zcu,
2780 zcu: strat.ZcuPtr(),
27632781 tid: strat.Tid(),
27642782) SemaError!bool {
27652783 const ip = &zcu.intern_pool;
......@@ -2834,40 +2852,44 @@ pub fn comptimeOnlyInner(
28342852 if (struct_type.layout == .@"packed")
28352853 return false;
28362854
2837 // A struct with no fields is not comptime-only.
2838 return switch (struct_type.setRequiresComptimeWip(ip)) {
2839 .no, .wip => false,
2840 .yes => true,
2841 .unknown => {
2842 // Inlined `assert` so that the resolution calls below are not statically reachable.
2843 if (strat != .sema) unreachable;
2844
2845 if (struct_type.flagsUnordered(ip).field_types_wip) {
2846 struct_type.setRequiresComptime(ip, .unknown);
2847 return false;
2848 }
2855 return switch (strat) {
2856 .normal => switch (struct_type.requiresComptime(ip)) {
2857 .wip => unreachable,
2858 .no => false,
2859 .yes => true,
2860 .unknown => unreachable,
2861 },
2862 .sema => switch (struct_type.setRequiresComptimeWip(ip)) {
2863 .no, .wip => false,
2864 .yes => true,
2865 .unknown => {
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);
2853 try ty.resolveFields(pt);
2854
2855 for (0..struct_type.field_types.len) |i_usize| {
2856 const i: u32 = @intCast(i_usize);
2857 if (struct_type.fieldIsComptime(ip, i)) continue;
2858 const field_ty = struct_type.field_types.get(ip)[i];
2859 if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) {
2860 // Note that this does not cause the layout to
2861 // be considered resolved. Comptime-only types
2862 // still maintain a layout of their
2863 // runtime-known fields.
2864 struct_type.setRequiresComptime(ip, .yes);
2865 return true;
2873 const pt = strat.pt(zcu, tid);
2874 try ty.resolveFields(pt);
2875
2876 for (0..struct_type.field_types.len) |i_usize| {
2877 const i: u32 = @intCast(i_usize);
2878 if (struct_type.fieldIsComptime(ip, i)) continue;
2879 const field_ty = struct_type.field_types.get(ip)[i];
2880 if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) {
2881 // Note that this does not cause the layout to
2882 // be considered resolved. Comptime-only types
2883 // still maintain a layout of their
2884 // runtime-known fields.
2885 struct_type.setRequiresComptime(ip, .yes);
2886 return true;
2887 }
28662888 }
2867 }
28682889
2869 struct_type.setRequiresComptime(ip, .no);
2870 return false;
2890 struct_type.setRequiresComptime(ip, .no);
2891 return false;
2892 },
28712893 },
28722894 };
28732895 },
......@@ -2882,35 +2904,40 @@ pub fn comptimeOnlyInner(
28822904
28832905 .union_type => {
28842906 const union_type = ip.loadUnionType(ty.toIntern());
2885 switch (union_type.setRequiresComptimeWip(ip)) {
2886 .no, .wip => return false,
2887 .yes => return true,
2888 .unknown => {
2889 // Inlined `assert` so that the resolution calls below are not statically reachable.
2890 if (strat != .sema) unreachable;
2891
2892 if (union_type.flagsUnordered(ip).status == .field_types_wip) {
2893 union_type.setRequiresComptime(ip, .unknown);
2894 return false;
2895 }
2907 return switch (strat) {
2908 .normal => switch (union_type.requiresComptime(ip)) {
2909 .wip => unreachable,
2910 .no => false,
2911 .yes => true,
2912 .unknown => unreachable,
2913 },
2914 .sema => switch (union_type.setRequiresComptimeWip(ip)) {
2915 .no, .wip => return false,
2916 .yes => return true,
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);
2900 try ty.resolveFields(pt);
2925 const pt = strat.pt(zcu, tid);
2926 try ty.resolveFields(pt);
29012927
2902 for (0..union_type.field_types.len) |field_idx| {
2903 const field_ty = union_type.field_types.get(ip)[field_idx];
2904 if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) {
2905 union_type.setRequiresComptime(ip, .yes);
2906 return true;
2928 for (0..union_type.field_types.len) |field_idx| {
2929 const field_ty = union_type.field_types.get(ip)[field_idx];
2930 if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) {
2931 union_type.setRequiresComptime(ip, .yes);
2932 return true;
2933 }
29072934 }
2908 }
29092935
2910 union_type.setRequiresComptime(ip, .no);
2911 return false;
2936 union_type.setRequiresComptime(ip, .no);
2937 return false;
2938 },
29122939 },
2913 }
2940 };
29142941 },
29152942
29162943 .opaque_type => false,
......@@ -3207,7 +3234,7 @@ pub fn fieldAlignmentInner(
32073234 ty: Type,
32083235 index: usize,
32093236 comptime strat: ResolveStrat,
3210 zcu: *Zcu,
3237 zcu: strat.ZcuPtr(),
32113238 tid: strat.Tid(),
32123239) SemaError!Alignment {
32133240 const ip = &zcu.intern_pool;
......@@ -3281,7 +3308,7 @@ pub fn structFieldAlignmentInner(
32813308 explicit_alignment: Alignment,
32823309 layout: std.builtin.Type.ContainerLayout,
32833310 comptime strat: Type.ResolveStrat,
3284 zcu: *Zcu,
3311 zcu: strat.ZcuPtr(),
32853312 tid: strat.Tid(),
32863313) SemaError!Alignment {
32873314 assert(layout != .@"packed");
......@@ -3323,7 +3350,7 @@ pub fn unionFieldAlignmentInner(
33233350 explicit_alignment: Alignment,
33243351 layout: std.builtin.Type.ContainerLayout,
33253352 comptime strat: Type.ResolveStrat,
3326 zcu: *Zcu,
3353 zcu: strat.ZcuPtr(),
33273354 tid: strat.Tid(),
33283355) SemaError!Alignment {
33293356 assert(layout != .@"packed");
......@@ -3392,11 +3419,7 @@ pub const FieldOffset = struct {
33923419};
33933420
33943421/// Supports structs and unions.
3395pub fn structFieldOffset(
3396 ty: Type,
3397 index: usize,
3398 zcu: *Zcu,
3399) u64 {
3422pub fn structFieldOffset(ty: Type, index: usize, zcu: *const Zcu) u64 {
34003423 const ip = &zcu.intern_pool;
34013424 switch (ip.indexToKey(ty.toIntern())) {
34023425 .struct_type => {
......@@ -3944,7 +3967,7 @@ fn resolveUnionInner(
39443967 };
39453968}
39463969
3947pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *Zcu) Zcu.UnionLayout {
3970pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout {
39483971 const ip = &zcu.intern_pool;
39493972 assert(loaded_union.haveLayout(ip));
39503973 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 {
241241
242242/// If the value fits in a u64, return it, otherwise null.
243243/// Asserts not undefined.
244pub fn getUnsignedInt(val: Value, zcu: *Zcu) ?u64 {
244pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 {
245245 return getUnsignedIntInner(val, .normal, zcu, {}) catch unreachable;
246246}
247247
248248/// 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 {
250250 return getUnsignedInt(val, zcu).?;
251251}
252252
......@@ -259,7 +259,7 @@ pub fn getUnsignedIntSema(val: Value, pt: Zcu.PerThread) !?u64 {
259259pub fn getUnsignedIntInner(
260260 val: Value,
261261 comptime strat: ResolveStrat,
262 zcu: *Zcu,
262 zcu: strat.ZcuPtr(),
263263 tid: strat.Tid(),
264264) !?u64 {
265265 return switch (val.toIntern()) {
......@@ -304,7 +304,7 @@ pub fn toUnsignedIntSema(val: Value, pt: Zcu.PerThread) !u64 {
304304}
305305
306306/// 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 {
308308 return switch (val.toIntern()) {
309309 .bool_false => 0,
310310 .bool_true => 1,
src/Zcu.zig+93-24
......@@ -19,8 +19,8 @@ const Ast = std.zig.Ast;
1919const Zcu = @This();
2020const Compilation = @import("Compilation.zig");
2121const Cache = std.Build.Cache;
22const Value = @import("Value.zig");
23const Type = @import("Type.zig");
22pub const Value = @import("Value.zig");
23pub const Type = @import("Type.zig");
2424const Package = @import("Package.zig");
2525const link = @import("link.zig");
2626const Air = @import("Air.zig");
......@@ -79,11 +79,11 @@ local_zir_cache: Compilation.Directory,
7979all_exports: std.ArrayListUnmanaged(Export) = .empty,
8080/// This is a list of free indices in `all_exports`. These indices may be reused by exports from
8181/// future semantic analysis.
82free_exports: std.ArrayListUnmanaged(u32) = .empty,
82free_exports: std.ArrayListUnmanaged(Export.Index) = .empty,
8383/// Maps from an `AnalUnit` which performs a single export, to the index into `all_exports` of
8484/// the export it performs. Note that the key is not the `Decl` being exported, but the `AnalUnit`
8585/// whose analysis triggered the export.
86single_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .empty,
86single_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, Export.Index) = .empty,
8787/// Like `single_exports`, but for `AnalUnit`s which perform multiple exports.
8888/// The exports are `all_exports.items[index..][0..len]`.
8989multi_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct {
......@@ -127,6 +127,7 @@ transitive_failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .emp
127127/// This may be a simple "value" `Nav`, or it may be a function.
128128/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator.
129129failed_codegen: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, *ErrorMsg) = .empty,
130failed_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, *ErrorMsg) = .empty,
130131/// Keep track of one `@compileLog` callsite per `AnalUnit`.
131132/// The value is the source location of the `@compileLog` call, convertible to a `LazySrcLoc`.
132133compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct {
......@@ -144,8 +145,7 @@ compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct {
144145failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .empty,
145146/// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator.
146147failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .empty,
147/// Key is index into `all_exports`.
148failed_exports: std.AutoArrayHashMapUnmanaged(u32, *ErrorMsg) = .empty,
148failed_exports: std.AutoArrayHashMapUnmanaged(Export.Index, *ErrorMsg) = .empty,
149149/// If analysis failed due to a cimport error, the corresponding Clang errors
150150/// are stored here.
151151cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = .empty,
......@@ -524,6 +524,15 @@ pub const Export = struct {
524524 section: InternPool.OptionalNullTerminatedString = .none,
525525 visibility: std.builtin.SymbolVisibility = .default,
526526 };
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 };
527536};
528537
529538pub const Reference = struct {
......@@ -2439,16 +2448,14 @@ pub fn deinit(zcu: *Zcu) void {
24392448 zcu.local_zir_cache.handle.close();
24402449 zcu.global_zir_cache.handle.close();
24412450
2442 for (zcu.failed_analysis.values()) |value| {
2443 value.destroy(gpa);
2444 }
2445 for (zcu.failed_codegen.values()) |value| {
2446 value.destroy(gpa);
2447 }
2451 for (zcu.failed_analysis.values()) |value| value.destroy(gpa);
2452 for (zcu.failed_codegen.values()) |value| value.destroy(gpa);
2453 for (zcu.failed_types.values()) |value| value.destroy(gpa);
24482454 zcu.analysis_in_progress.deinit(gpa);
24492455 zcu.failed_analysis.deinit(gpa);
24502456 zcu.transitive_failed_analysis.deinit(gpa);
24512457 zcu.failed_codegen.deinit(gpa);
2458 zcu.failed_types.deinit(gpa);
24522459
24532460 for (zcu.failed_files.values()) |value| {
24542461 if (value) |msg| msg.destroy(gpa);
......@@ -3093,7 +3100,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
30933100 const gpa = zcu.gpa;
30943101
30953102 const exports_base, const exports_len = if (zcu.single_exports.fetchSwapRemove(anal_unit)) |kv|
3096 .{ kv.value, 1 }
3103 .{ @intFromEnum(kv.value), 1 }
30973104 else if (zcu.multi_exports.fetchSwapRemove(anal_unit)) |info|
30983105 .{ info.value.index, info.value.len }
30993106 else
......@@ -3107,11 +3114,12 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
31073114 // This case is needed because in some rare edge cases, `Sema` wants to add and delete exports
31083115 // within a single update.
31093116 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);
31113119 if (zcu.comp.bin_file) |lf| {
31123120 lf.deleteExport(exp.exported, exp.opts.name);
31133121 }
3114 if (zcu.failed_exports.fetchSwapRemove(@intCast(export_idx))) |failed_kv| {
3122 if (zcu.failed_exports.fetchSwapRemove(export_idx)) |failed_kv| {
31153123 failed_kv.value.destroy(gpa);
31163124 }
31173125 }
......@@ -3123,7 +3131,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
31233131 return;
31243132 };
31253133 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));
31273135 }
31283136}
31293137
......@@ -3269,7 +3277,7 @@ fn lockAndClearFileCompileError(zcu: *Zcu, file: *File) void {
32693277
32703278pub fn handleUpdateExports(
32713279 zcu: *Zcu,
3272 export_indices: []const u32,
3280 export_indices: []const Export.Index,
32733281 result: link.File.UpdateExportsError!void,
32743282) Allocator.Error!void {
32753283 const gpa = zcu.gpa;
......@@ -3277,12 +3285,10 @@ pub fn handleUpdateExports(
32773285 error.OutOfMemory => return error.OutOfMemory,
32783286 error.AnalysisFail => {
32793287 const export_idx = export_indices[0];
3280 const new_export = &zcu.all_exports.items[export_idx];
3288 const new_export = export_idx.ptr(zcu);
32813289 new_export.status = .failed_retryable;
32823290 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
3283 const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{
3284 @errorName(err),
3285 });
3291 const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{@errorName(err)});
32863292 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg);
32873293 },
32883294 };
......@@ -3443,7 +3449,7 @@ pub fn atomicPtrAlignment(
34433449/// * `@TypeOf(.{})`
34443450/// * A struct which has no fields (`struct {}`).
34453451/// * Not a struct.
3446pub fn typeToStruct(zcu: *Zcu, ty: Type) ?InternPool.LoadedStructType {
3452pub fn typeToStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType {
34473453 if (ty.ip_index == .none) return null;
34483454 const ip = &zcu.intern_pool;
34493455 return switch (ip.indexToKey(ty.ip_index)) {
......@@ -3452,7 +3458,7 @@ pub fn typeToStruct(zcu: *Zcu, ty: Type) ?InternPool.LoadedStructType {
34523458 };
34533459}
34543460
3455pub fn typeToPackedStruct(zcu: *Zcu, ty: Type) ?InternPool.LoadedStructType {
3461pub fn typeToPackedStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType {
34563462 const s = zcu.typeToStruct(ty) orelse return null;
34573463 if (s.layout != .@"packed") return null;
34583464 return s;
......@@ -3477,7 +3483,7 @@ pub fn iesFuncIndex(zcu: *const Zcu, ies_index: InternPool.Index) InternPool.Ind
34773483}
34783484
34793485pub 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);
34813487}
34823488
34833489pub 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 {
37913797 };
37923798}
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
37943812pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 {
37953813 const ip = &zcu.intern_pool;
37963814 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 {
40514069 else => true,
40524070 };
40534071}
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
17221722 // Correcting this failure will involve changing a type this function
17231723 // depends on, hence triggering re-analysis of this function, so this
17241724 // 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.
17271725 } else if (comp.bin_file) |lf| {
17281726 lf.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) {
17291727 error.OutOfMemory => return error.OutOfMemory,
1730 error.AnalysisFail => {
1731 assert(zcu.failed_codegen.contains(nav_index));
1732 },
1733 else => {
1728 error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)),
1729 error.Overflow => {
17341730 try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
17351731 gpa,
17361732 zcu.navSrcLoc(nav_index),
17371733 "unable to codegen: {s}",
17381734 .{@errorName(err)},
17391735 ));
1740 try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index }));
1736 // Not a retryable failure.
17411737 },
17421738 };
17431739 } else if (zcu.llvm_object) |llvm_object| {
......@@ -2819,8 +2815,8 @@ pub fn processExports(pt: Zcu.PerThread) !void {
28192815 const gpa = zcu.gpa;
28202816
28212817 // 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;
2823 var uav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, std.ArrayListUnmanaged(u32)) = .empty;
2818 var nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, std.ArrayListUnmanaged(Zcu.Export.Index)) = .empty;
2819 var uav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, std.ArrayListUnmanaged(Zcu.Export.Index)) = .empty;
28242820 defer {
28252821 for (nav_exports.values()) |*exports| {
28262822 exports.deinit(gpa);
......@@ -2839,7 +2835,7 @@ pub fn processExports(pt: Zcu.PerThread) !void {
28392835 try nav_exports.ensureTotalCapacity(gpa, zcu.single_exports.count() + zcu.multi_exports.count());
28402836
28412837 for (zcu.single_exports.values()) |export_idx| {
2842 const exp = zcu.all_exports.items[export_idx];
2838 const exp = export_idx.ptr(zcu);
28432839 const value_ptr, const found_existing = switch (exp.exported) {
28442840 .nav => |nav| gop: {
28452841 const gop = try nav_exports.getOrPut(gpa, nav);
......@@ -2867,7 +2863,7 @@ pub fn processExports(pt: Zcu.PerThread) !void {
28672863 },
28682864 };
28692865 if (!found_existing) value_ptr.* = .{};
2870 try value_ptr.append(gpa, @intCast(export_idx));
2866 try value_ptr.append(gpa, @enumFromInt(export_idx));
28712867 }
28722868 }
28732869
......@@ -2886,20 +2882,20 @@ pub fn processExports(pt: Zcu.PerThread) !void {
28862882 }
28872883}
28882884
2889const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, u32);
2885const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, Zcu.Export.Index);
28902886
28912887fn processExportsInner(
28922888 pt: Zcu.PerThread,
28932889 symbol_exports: *SymbolExports,
28942890 exported: Zcu.Exported,
2895 export_indices: []const u32,
2891 export_indices: []const Zcu.Export.Index,
28962892) error{OutOfMemory}!void {
28972893 const zcu = pt.zcu;
28982894 const gpa = zcu.gpa;
28992895 const ip = &zcu.intern_pool;
29002896
29012897 for (export_indices) |export_idx| {
2902 const new_export = &zcu.all_exports.items[export_idx];
2898 const new_export = export_idx.ptr(zcu);
29032899 const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name);
29042900 if (gop.found_existing) {
29052901 new_export.status = .failed_retryable;
......@@ -2908,7 +2904,7 @@ fn processExportsInner(
29082904 new_export.opts.name.fmt(ip),
29092905 });
29102906 errdefer msg.destroy(gpa);
2911 const other_export = zcu.all_exports.items[gop.value_ptr.*];
2907 const other_export = gop.value_ptr.ptr(zcu);
29122908 try zcu.errNote(other_export.src, msg, "other symbol here", .{});
29132909 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg);
29142910 new_export.status = .failed;
......@@ -3100,6 +3096,7 @@ pub fn populateTestFunctions(
31003096pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{OutOfMemory}!void {
31013097 const zcu = pt.zcu;
31023098 const comp = zcu.comp;
3099 const gpa = zcu.gpa;
31033100 const ip = &zcu.intern_pool;
31043101
31053102 const nav = zcu.intern_pool.getNav(nav_index);
......@@ -3113,26 +3110,15 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error
31133110 } else if (comp.bin_file) |lf| {
31143111 lf.updateNav(pt, nav_index) catch |err| switch (err) {
31153112 error.OutOfMemory => return error.OutOfMemory,
3116 error.AnalysisFail => {
3117 assert(zcu.failed_codegen.contains(nav_index));
3118 },
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(
3113 error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)),
3114 error.Overflow => {
3115 try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
31233116 gpa,
31243117 zcu.navSrcLoc(nav_index),
31253118 "unable to codegen: {s}",
31263119 .{@errorName(err)},
31273120 ));
3128 if (nav.analysis != null) {
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 }
3121 // Not a retryable failure.
31363122 },
31373123 };
31383124 } else if (zcu.llvm_object) |llvm_object| {
......@@ -3142,24 +3128,26 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error
31423128 }
31433129}
31443130
3145pub fn linkerUpdateContainerType(pt: Zcu.PerThread, ty: InternPool.Index) !void {
3131pub fn linkerUpdateContainerType(pt: Zcu.PerThread, ty: InternPool.Index) error{OutOfMemory}!void {
31463132 const zcu = pt.zcu;
3133 const gpa = zcu.gpa;
31473134 const comp = zcu.comp;
31483135 const ip = &zcu.intern_pool;
31493136
31503137 const codegen_prog_node = zcu.codegen_prog_node.start(Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), 0);
31513138 defer codegen_prog_node.end();
31523139
3140 if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(gpa);
3141
31533142 if (!Air.typeFullyResolved(Type.fromInterned(ty), zcu)) {
31543143 // 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 compilation
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 };
3144 return;
31623145 }
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 };
31633151}
31643152
31653153pub 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");
2424const Alignment = InternPool.Alignment;
2525
2626const CodeGenError = codegen.CodeGenError;
27const Result = codegen.Result;
2827
2928const bits = @import("bits.zig");
3029const abi = @import("abi.zig");
......@@ -51,7 +50,6 @@ debug_output: link.File.DebugInfoOutput,
5150target: *const std.Target,
5251func_index: InternPool.Index,
5352owner_nav: InternPool.Nav.Index,
54err_msg: ?*ErrorMsg,
5553args: []MCValue,
5654ret_mcv: MCValue,
5755fn_type: Type,
......@@ -325,9 +323,9 @@ pub fn generate(
325323 func_index: InternPool.Index,
326324 air: Air,
327325 liveness: Liveness,
328 code: *std.ArrayList(u8),
326 code: *std.ArrayListUnmanaged(u8),
329327 debug_output: link.File.DebugInfoOutput,
330) CodeGenError!Result {
328) CodeGenError!void {
331329 const zcu = pt.zcu;
332330 const gpa = zcu.gpa;
333331 const func = zcu.funcInfo(func_index);
......@@ -353,7 +351,6 @@ pub fn generate(
353351 .bin_file = lf,
354352 .func_index = func_index,
355353 .owner_nav = func.owner_nav,
356 .err_msg = null,
357354 .args = undefined, // populated after `resolveCallingConventionValues`
358355 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
359356 .fn_type = fn_type,
......@@ -370,10 +367,7 @@ pub fn generate(
370367 defer function.dbg_info_relocs.deinit(gpa);
371368
372369 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
373 error.CodegenFail => return Result{ .fail = function.err_msg.? },
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 },
370 error.CodegenFail => return error.CodegenFail,
377371 else => |e| return e,
378372 };
379373 defer call_info.deinit(&function);
......@@ -384,24 +378,23 @@ pub fn generate(
384378 function.max_end_stack = call_info.stack_byte_count;
385379
386380 function.gen() catch |err| switch (err) {
387 error.CodegenFail => return Result{ .fail = function.err_msg.? },
388 error.OutOfRegisters => return Result{
389 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
390 },
381 error.CodegenFail => return error.CodegenFail,
382 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
391383 else => |e| return e,
392384 };
393385
394386 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)});
396389 }
397390
398 var mir = Mir{
391 var mir: Mir = .{
399392 .instructions = function.mir_instructions.toOwnedSlice(),
400393 .extra = try function.mir_extra.toOwnedSlice(gpa),
401394 };
402395 defer mir.deinit(gpa);
403396
404 var emit = Emit{
397 var emit: Emit = .{
405398 .mir = mir,
406399 .bin_file = lf,
407400 .debug_output = debug_output,
......@@ -417,15 +410,9 @@ pub fn generate(
417410 defer emit.deinit();
418411
419412 emit.emitMir() catch |err| switch (err) {
420 error.EmitFail => return Result{ .fail = emit.err_msg.? },
413 error.EmitFail => return function.failMsg(emit.err_msg.?),
421414 else => |e| return e,
422415 };
423
424 if (function.err_msg) |em| {
425 return Result{ .fail = em };
426 } else {
427 return Result.ok;
428 }
429416}
430417
431418fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
......@@ -567,7 +554,7 @@ fn gen(self: *Self) !void {
567554 .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } },
568555 });
569556 } else {
570 return self.failSymbol("TODO AArch64: allow larger stacks", .{});
557 @panic("TODO AArch64: allow larger stacks");
571558 }
572559
573560 _ = try self.addInst(.{
......@@ -723,7 +710,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
723710 .cmp_gt => try self.airCmp(inst, .gt),
724711 .cmp_neq => try self.airCmp(inst, .neq),
725712
726 .cmp_vector => try self.airCmpVector(inst),
713 .cmp_vector => try self.airCmpVector(inst),
727714 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
728715
729716 .alloc => try self.airAlloc(inst),
......@@ -744,7 +731,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
744731 .fpext => try self.airFpext(inst),
745732 .intcast => try self.airIntCast(inst),
746733 .trunc => try self.airTrunc(inst),
747 .int_from_bool => try self.airIntFromBool(inst),
734 .int_from_bool => try self.airIntFromBool(inst),
748735 .is_non_null => try self.airIsNonNull(inst),
749736 .is_non_null_ptr => try self.airIsNonNullPtr(inst),
750737 .is_null => try self.airIsNull(inst),
......@@ -756,7 +743,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
756743 .load => try self.airLoad(inst),
757744 .loop => try self.airLoop(inst),
758745 .not => try self.airNot(inst),
759 .int_from_ptr => try self.airIntFromPtr(inst),
746 .int_from_ptr => try self.airIntFromPtr(inst),
760747 .ret => try self.airRet(inst),
761748 .ret_safe => try self.airRet(inst), // TODO
762749 .ret_load => try self.airRetLoad(inst),
......@@ -765,8 +752,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
765752 .struct_field_ptr=> try self.airStructFieldPtr(inst),
766753 .struct_field_val=> try self.airStructFieldVal(inst),
767754 .array_to_slice => try self.airArrayToSlice(inst),
768 .float_from_int => try self.airFloatFromInt(inst),
769 .int_from_float => try self.airIntFromFloat(inst),
755 .float_from_int => try self.airFloatFromInt(inst),
756 .int_from_float => try self.airIntFromFloat(inst),
770757 .cmpxchg_strong => try self.airCmpxchg(inst),
771758 .cmpxchg_weak => try self.airCmpxchg(inst),
772759 .atomic_rmw => try self.airAtomicRmw(inst),
......@@ -1107,7 +1094,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
11071094/// Copies a value to a register without tracking the register. The register is not considered
11081095/// allocated. A second call to `copyToTmpRegister` may return the same register.
11091096/// 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 {
11111098 const raw_reg = try self.register_manager.allocReg(null, gp);
11121099 const reg = self.registerAlias(raw_reg, ty);
11131100 try self.genSetReg(ty, reg, mcv);
......@@ -1125,12 +1112,12 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa
11251112 return MCValue{ .register = reg };
11261113}
11271114
1128fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
1115fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!void {
11291116 const stack_offset = try self.allocMemPtr(inst);
11301117 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
11311118}
11321119
1133fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
1120fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
11341121 const pt = self.pt;
11351122 const zcu = pt.zcu;
11361123 const result: MCValue = switch (self.ret_mcv) {
......@@ -1152,19 +1139,19 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
11521139 return self.finishAir(inst, result, .{ .none, .none, .none });
11531140}
11541141
1155fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
1142fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!void {
11561143 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
11571144 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
11581145 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11591146}
11601147
1161fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
1148fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!void {
11621149 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
11631150 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
11641151 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11651152}
11661153
1167fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
1154fn airIntCast(self: *Self, inst: Air.Inst.Index) InnerError!void {
11681155 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
11691156 if (self.liveness.isUnused(inst))
11701157 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
......@@ -1293,7 +1280,7 @@ fn trunc(
12931280 }
12941281}
12951282
1296fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
1283fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!void {
12971284 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
12981285 const operand = try self.resolveInst(ty_op.operand);
12991286 const operand_ty = self.typeOf(ty_op.operand);
......@@ -1306,14 +1293,14 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
13061293 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
13071294}
13081295
1309fn airIntFromBool(self: *Self, inst: Air.Inst.Index) !void {
1296fn airIntFromBool(self: *Self, inst: Air.Inst.Index) InnerError!void {
13101297 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
13111298 const operand = try self.resolveInst(un_op);
13121299 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
13131300 return self.finishAir(inst, result, .{ un_op, .none, .none });
13141301}
13151302
1316fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1303fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!void {
13171304 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
13181305 const pt = self.pt;
13191306 const zcu = pt.zcu;
......@@ -1484,7 +1471,7 @@ fn minMax(
14841471 }
14851472}
14861473
1487fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {
1474fn airMinMax(self: *Self, inst: Air.Inst.Index) InnerError!void {
14881475 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
14891476 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
14901477 const lhs_ty = self.typeOf(bin_op.lhs);
......@@ -1502,7 +1489,7 @@ fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {
15021489 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
15031490}
15041491
1505fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
1492fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!void {
15061493 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
15071494 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
15081495 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
......@@ -2440,7 +2427,7 @@ fn ptrArithmetic(
24402427 }
24412428}
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 {
24442431 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
24452432 const lhs_ty = self.typeOf(bin_op.lhs);
24462433 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 {
24902477 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
24912478}
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 {
24942481 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
24952482 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
24962483 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
25052492 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
25062493}
25072494
2508fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
2495fn airAddSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
25092496 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
25102497 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
25112498 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
25122499}
25132500
2514fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
2501fn airSubSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
25152502 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
25162503 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
25172504 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
25182505}
25192506
2520fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
2507fn airMulSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
25212508 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
25222509 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
25232510 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
25242511}
25252512
2526fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2513fn airOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
25272514 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
25282515 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
25292516 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
......@@ -2536,9 +2523,9 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
25362523 const rhs_ty = self.typeOf(extra.rhs);
25372524
25382525 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));
25402527 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
25432530 switch (lhs_ty.zigTypeTag(zcu)) {
25442531 .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 {
26522639 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
26532640}
26542641
2655fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2642fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
26562643 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
26572644 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
26582645 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 {
28762863 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
28772864}
28782865
2879fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2866fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
28802867 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
28812868 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
28822869 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 {
30122999 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
30133000}
30143001
3015fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
3002fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
30163003 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
30173004 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
30183005 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
30193006}
30203007
3021fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
3008fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
30223009 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
30233010 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
30243011 const optional_ty = self.typeOf(ty_op.operand);
......@@ -3055,13 +3042,13 @@ fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty:
30553042 }
30563043}
30573044
3058fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
3045fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
30593046 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
30603047 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
30613048 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
30623049}
30633050
3064fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
3051fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void {
30653052 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
30663053 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
30673054 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
......@@ -3137,7 +3124,7 @@ fn errUnionErr(
31373124 }
31383125}
31393126
3140fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
3127fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
31413128 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
31423129 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
31433130 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
......@@ -3218,7 +3205,7 @@ fn errUnionPayload(
32183205 }
32193206}
32203207
3221fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
3208fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
32223209 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
32233210 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
32243211 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
......@@ -3230,26 +3217,26 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
32303217}
32313218
32323219// *(E!T) -> E
3233fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {
3220fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
32343221 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
32353222 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});
32363223 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
32373224}
32383225
32393226// *(E!T) -> *T
3240fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
3227fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
32413228 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
32423229 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});
32433230 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
32443231}
32453232
3246fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
3233fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void {
32473234 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
32483235 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
32493236 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
32503237}
32513238
3252fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
3239fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) InnerError!void {
32533240 const result: MCValue = if (self.liveness.isUnused(inst))
32543241 .dead
32553242 else
......@@ -3257,17 +3244,17 @@ fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
32573244 return self.finishAir(inst, result, .{ .none, .none, .none });
32583245}
32593246
3260fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
3247fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) InnerError!void {
32613248 _ = inst;
32623249 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
32633250}
32643251
3265fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
3252fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) InnerError!void {
32663253 _ = inst;
32673254 return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
32683255}
32693256
3270fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
3257fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!void {
32713258 const pt = self.pt;
32723259 const zcu = pt.zcu;
32733260 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 {
33133300}
33143301
33153302/// T to E!T
3316fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
3303fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
33173304 const pt = self.pt;
33183305 const zcu = pt.zcu;
33193306 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 {
33383325}
33393326
33403327/// E to E!T
3341fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
3328fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
33423329 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
33433330 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
33443331 const pt = self.pt;
......@@ -3379,7 +3366,7 @@ fn slicePtr(mcv: MCValue) MCValue {
33793366 }
33803367}
33813368
3382fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
3369fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
33833370 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
33843371 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
33853372 const mcv = try self.resolveInst(ty_op.operand);
......@@ -3388,7 +3375,7 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
33883375 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
33893376}
33903377
3391fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
3378fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!void {
33923379 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
33933380 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
33943381 const ptr_bits = 64;
......@@ -3412,7 +3399,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
34123399 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
34133400}
34143401
3415fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
3402fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
34163403 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
34173404 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
34183405 const ptr_bits = 64;
......@@ -3429,7 +3416,7 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
34293416 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
34303417}
34313418
3432fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
3419fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
34333420 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
34343421 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
34353422 const mcv = try self.resolveInst(ty_op.operand);
......@@ -3444,7 +3431,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
34443431 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
34453432}
34463433
3447fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
3434fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
34483435 const pt = self.pt;
34493436 const zcu = pt.zcu;
34503437 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
......@@ -3487,7 +3474,7 @@ fn ptrElemVal(
34873474 }
34883475}
34893476
3490fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
3477fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
34913478 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
34923479 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
34933480 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
......@@ -3506,13 +3493,13 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
35063493 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
35073494}
35083495
3509fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
3496fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
35103497 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
35113498 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});
35123499 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
35133500}
35143501
3515fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
3502fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
35163503 const pt = self.pt;
35173504 const zcu = pt.zcu;
35183505 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 {
35263513 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
35273514}
35283515
3529fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
3516fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
35303517 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
35313518 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
35323519 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
......@@ -3542,55 +3529,55 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
35423529 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
35433530}
35443531
3545fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
3532fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
35463533 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
35473534 _ = bin_op;
35483535 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});
35493536}
35503537
3551fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
3538fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
35523539 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
35533540 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
35543541 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
35553542}
35563543
3557fn airClz(self: *Self, inst: Air.Inst.Index) !void {
3544fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!void {
35583545 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
35593546 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
35603547 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
35613548}
35623549
3563fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
3550fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!void {
35643551 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
35653552 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});
35663553 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
35673554}
35683555
3569fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
3556fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!void {
35703557 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
35713558 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
35723559 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
35733560}
35743561
3575fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
3562fn airAbs(self: *Self, inst: Air.Inst.Index) InnerError!void {
35763563 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
35773564 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch});
35783565 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
35793566}
35803567
3581fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
3568fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!void {
35823569 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
35833570 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});
35843571 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
35853572}
35863573
3587fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
3574fn airBitReverse(self: *Self, inst: Air.Inst.Index) InnerError!void {
35883575 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
35893576 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});
35903577 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
35913578}
35923579
3593fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
3580fn airUnaryMath(self: *Self, inst: Air.Inst.Index) InnerError!void {
35943581 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
35953582 const result: MCValue = if (self.liveness.isUnused(inst))
35963583 .dead
......@@ -3885,7 +3872,7 @@ fn genInlineMemsetCode(
38853872 // end:
38863873}
38873874
3888fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
3875fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
38893876 const pt = self.pt;
38903877 const zcu = pt.zcu;
38913878 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
40864073 }
40874074}
40884075
4089fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
4076fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) InnerError!void {
40904077 if (safety) {
40914078 // TODO if the value is undef, write 0xaa bytes to dest
40924079 } else {
......@@ -4103,14 +4090,14 @@ fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
41034090 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
41044091}
41054092
4106fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
4093fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
41074094 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
41084095 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
41094096 const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index);
41104097 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
41114098}
41124099
4113fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
4100fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) InnerError!void {
41144101 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
41154102 const result = try self.structFieldPtr(inst, ty_op.operand, index);
41164103 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
41384125 };
41394126}
41404127
4141fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
4128fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
41424129 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
41434130 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
41444131 const operand = extra.struct_operand;
......@@ -4194,7 +4181,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
41944181 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
41954182}
41964183
4197fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
4184fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
41984185 const pt = self.pt;
41994186 const zcu = pt.zcu;
42004187 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 {
42184205 return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none });
42194206}
42204207
4221fn airArg(self: *Self, inst: Air.Inst.Index) !void {
4208fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
42224209 // skip zero-bit arguments as they don't have a corresponding arg instruction
42234210 var arg_index = self.arg_index;
42244211 while (self.args[arg_index] == .none) arg_index += 1;
......@@ -4238,7 +4225,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
42384225 return self.finishAir(inst, result, .{ .none, .none, .none });
42394226}
42404227
4241fn airTrap(self: *Self) !void {
4228fn airTrap(self: *Self) InnerError!void {
42424229 _ = try self.addInst(.{
42434230 .tag = .brk,
42444231 .data = .{ .imm16 = 0x0001 },
......@@ -4246,7 +4233,7 @@ fn airTrap(self: *Self) !void {
42464233 return self.finishAirBookkeeping();
42474234}
42484235
4249fn airBreakpoint(self: *Self) !void {
4236fn airBreakpoint(self: *Self) InnerError!void {
42504237 _ = try self.addInst(.{
42514238 .tag = .brk,
42524239 .data = .{ .imm16 = 0xf000 },
......@@ -4254,17 +4241,17 @@ fn airBreakpoint(self: *Self) !void {
42544241 return self.finishAirBookkeeping();
42554242}
42564243
4257fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
4244fn airRetAddr(self: *Self, inst: Air.Inst.Index) InnerError!void {
42584245 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for aarch64", .{});
42594246 return self.finishAir(inst, result, .{ .none, .none, .none });
42604247}
42614248
4262fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
4249fn airFrameAddress(self: *Self, inst: Air.Inst.Index) InnerError!void {
42634250 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for aarch64", .{});
42644251 return self.finishAir(inst, result, .{ .none, .none, .none });
42654252}
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 {
42684255 if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{});
42694256 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
42704257 const callee = pl_op.operand;
......@@ -4422,7 +4409,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
44224409 return bt.finishAir(result);
44234410}
44244411
4425fn airRet(self: *Self, inst: Air.Inst.Index) !void {
4412fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!void {
44264413 const pt = self.pt;
44274414 const zcu = pt.zcu;
44284415 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 {
44554442 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
44564443}
44574444
4458fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
4445fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
44594446 const pt = self.pt;
44604447 const zcu = pt.zcu;
44614448 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 {
44994486 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
45004487}
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 {
45034490 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
45044491 const lhs_ty = self.typeOf(bin_op.lhs);
45054492
......@@ -4597,12 +4584,12 @@ fn cmp(
45974584 }
45984585}
45994586
4600fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
4587fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!void {
46014588 _ = inst;
46024589 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
46034590}
46044591
4605fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
4592fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!void {
46064593 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
46074594 const operand = try self.resolveInst(un_op);
46084595 _ = operand;
......@@ -4610,7 +4597,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
46104597 return self.finishAir(inst, result, .{ un_op, .none, .none });
46114598}
46124599
4613fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
4600fn airDbgStmt(self: *Self, inst: Air.Inst.Index) InnerError!void {
46144601 const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
46154602
46164603 _ = try self.addInst(.{
......@@ -4624,7 +4611,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
46244611 return self.finishAirBookkeeping();
46254612}
46264613
4627fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
4614fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) InnerError!void {
46284615 const pt = self.pt;
46294616 const zcu = pt.zcu;
46304617 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 {
46354622 try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
46364623}
46374624
4638fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
4625fn airDbgVar(self: *Self, inst: Air.Inst.Index) InnerError!void {
46394626 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
46404627 const operand = pl_op.operand;
46414628 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
......@@ -4686,7 +4673,7 @@ fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
46864673 }
46874674}
46884675
4689fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
4676fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
46904677 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
46914678 const cond = try self.resolveInst(pl_op.operand);
46924679 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
......@@ -4919,7 +4906,7 @@ fn isNonErr(
49194906 }
49204907}
49214908
4922fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
4909fn airIsNull(self: *Self, inst: Air.Inst.Index) InnerError!void {
49234910 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
49244911 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
49254912 const operand = try self.resolveInst(un_op);
......@@ -4930,7 +4917,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
49304917 return self.finishAir(inst, result, .{ un_op, .none, .none });
49314918}
49324919
4933fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4920fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
49344921 const pt = self.pt;
49354922 const zcu = pt.zcu;
49364923 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 {
49474934 return self.finishAir(inst, result, .{ un_op, .none, .none });
49484935}
49494936
4950fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
4937fn airIsNonNull(self: *Self, inst: Air.Inst.Index) InnerError!void {
49514938 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
49524939 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
49534940 const operand = try self.resolveInst(un_op);
......@@ -4958,7 +4945,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
49584945 return self.finishAir(inst, result, .{ un_op, .none, .none });
49594946}
49604947
4961fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4948fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
49624949 const pt = self.pt;
49634950 const zcu = pt.zcu;
49644951 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 {
49754962 return self.finishAir(inst, result, .{ un_op, .none, .none });
49764963}
49774964
4978fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
4965fn airIsErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
49794966 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
49804967 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
49814968 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
......@@ -4986,7 +4973,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
49864973 return self.finishAir(inst, result, .{ un_op, .none, .none });
49874974}
49884975
4989fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
4976fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
49904977 const pt = self.pt;
49914978 const zcu = pt.zcu;
49924979 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 {
50034990 return self.finishAir(inst, result, .{ un_op, .none, .none });
50044991}
50054992
5006fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
4993fn airIsNonErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
50074994 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
50084995 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
50094996 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
......@@ -5014,7 +5001,7 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
50145001 return self.finishAir(inst, result, .{ un_op, .none, .none });
50155002}
50165003
5017fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
5004fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
50185005 const pt = self.pt;
50195006 const zcu = pt.zcu;
50205007 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 {
50315018 return self.finishAir(inst, result, .{ un_op, .none, .none });
50325019}
50335020
5034fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
5021fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!void {
50355022 // A loop is a setup to be able to jump back to the beginning.
50365023 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
50375024 const loop = self.air.extraData(Air.Block, ty_pl.payload);
......@@ -5052,7 +5039,7 @@ fn jump(self: *Self, inst: Mir.Inst.Index) !void {
50525039 });
50535040}
50545041
5055fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
5042fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!void {
50565043 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
50575044 const extra = self.air.extraData(Air.Block, ty_pl.payload);
50585045 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) !
50905077 return self.finishAir(inst, result, .{ .none, .none, .none });
50915078}
50925079
5093fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
5080fn airSwitch(self: *Self, inst: Air.Inst.Index) InnerError!void {
50945081 const switch_br = self.air.unwrapSwitch(inst);
50955082 const condition_ty = self.typeOf(switch_br.operand);
50965083 const liveness = try self.liveness.getSwitchBr(
......@@ -5224,7 +5211,7 @@ fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
52245211 }
52255212}
52265213
5227fn airBr(self: *Self, inst: Air.Inst.Index) !void {
5214fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
52285215 const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br;
52295216 try self.br(branch.block_inst, branch.operand);
52305217 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
......@@ -5268,7 +5255,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
52685255 }));
52695256}
52705257
5271fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
5258fn airAsm(self: *Self, inst: Air.Inst.Index) InnerError!void {
52725259 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
52735260 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
52745261 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
56015588 .tag = .ldr_ptr_stack,
56025589 .data = .{ .load_store_stack = .{
56035590 .rt = reg,
5604 .offset = @as(u32, @intCast(off)),
5591 .offset = @intCast(off),
56055592 } },
56065593 });
56075594 },
......@@ -5617,13 +5604,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
56175604 .immediate => |x| {
56185605 _ = try self.addInst(.{
56195606 .tag = .movz,
5620 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x)) } },
5607 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x) } },
56215608 });
56225609
56235610 if (x & 0x0000_0000_ffff_0000 != 0) {
56245611 _ = try self.addInst(.{
56255612 .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 } },
56275614 });
56285615 }
56295616
......@@ -5631,13 +5618,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
56315618 if (x & 0x0000_ffff_0000_0000 != 0) {
56325619 _ = try self.addInst(.{
56335620 .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 } },
56355622 });
56365623 }
56375624 if (x & 0xffff_0000_0000_0000 != 0) {
56385625 _ = try self.addInst(.{
56395626 .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 } },
56415628 });
56425629 }
56435630 }
......@@ -5709,7 +5696,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
57095696 .tag = tag,
57105697 .data = .{ .load_store_stack = .{
57115698 .rt = reg,
5712 .offset = @as(u32, @intCast(off)),
5699 .offset = @intCast(off),
57135700 } },
57145701 });
57155702 },
......@@ -5733,7 +5720,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
57335720 .tag = tag,
57345721 .data = .{ .load_store_stack = .{
57355722 .rt = reg,
5736 .offset = @as(u32, @intCast(off)),
5723 .offset = @intCast(off),
57375724 } },
57385725 });
57395726 },
......@@ -5918,13 +5905,13 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
59185905 }
59195906}
59205907
5921fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void {
5908fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
59225909 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
59235910 const result = try self.resolveInst(un_op);
59245911 return self.finishAir(inst, result, .{ un_op, .none, .none });
59255912}
59265913
5927fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
5914fn airBitCast(self: *Self, inst: Air.Inst.Index) InnerError!void {
59285915 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
59295916 const result = if (self.liveness.isUnused(inst)) .dead else result: {
59305917 const operand = try self.resolveInst(ty_op.operand);
......@@ -5945,7 +5932,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
59455932 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
59465933}
59475934
5948fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
5935fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!void {
59495936 const pt = self.pt;
59505937 const zcu = pt.zcu;
59515938 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 {
59635950 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
59645951}
59655952
5966fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {
5953fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) InnerError!void {
59675954 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
59685955 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{
59695956 self.target.cpu.arch,
......@@ -5971,7 +5958,7 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {
59715958 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
59725959}
59735960
5974fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {
5961fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) InnerError!void {
59755962 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
59765963 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{
59775964 self.target.cpu.arch,
......@@ -5979,7 +5966,7 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {
59795966 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
59805967}
59815968
5982fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
5969fn airCmpxchg(self: *Self, inst: Air.Inst.Index) InnerError!void {
59835970 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
59845971 const extra = self.air.extraData(Air.Block, ty_pl.payload);
59855972 _ = extra;
......@@ -5989,23 +5976,23 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
59895976 });
59905977}
59915978
5992fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
5979fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) InnerError!void {
59935980 _ = inst;
59945981 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
59955982}
59965983
5997fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {
5984fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
59985985 _ = inst;
59995986 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});
60005987}
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 {
60035990 _ = inst;
60045991 _ = order;
60055992 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
60065993}
60075994
6008fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
5995fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) InnerError!void {
60095996 _ = inst;
60105997 if (safety) {
60115998 // 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 {
60156002 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
60166003}
60176004
6018fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
6005fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void {
60196006 _ = inst;
60206007 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
60216008}
60226009
6023fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
6010fn airTagName(self: *Self, inst: Air.Inst.Index) InnerError!void {
60246011 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
60256012 const operand = try self.resolveInst(un_op);
60266013 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
......@@ -6030,7 +6017,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
60306017 return self.finishAir(inst, result, .{ un_op, .none, .none });
60316018}
60326019
6033fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
6020fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!void {
60346021 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
60356022 const operand = try self.resolveInst(un_op);
60366023 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
......@@ -6040,33 +6027,33 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
60406027 return self.finishAir(inst, result, .{ un_op, .none, .none });
60416028}
60426029
6043fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
6030fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!void {
60446031 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
60456032 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for {}", .{self.target.cpu.arch});
60466033 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
60476034}
60486035
6049fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
6036fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!void {
60506037 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
60516038 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
60526039 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch});
60536040 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });
60546041}
60556042
6056fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
6043fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!void {
60576044 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
60586045 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
60596046 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for {}", .{self.target.cpu.arch});
60606047 return self.finishAir(inst, result, .{ extra.a, extra.b, .none });
60616048}
60626049
6063fn airReduce(self: *Self, inst: Air.Inst.Index) !void {
6050fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!void {
60646051 const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
60656052 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for aarch64", .{});
60666053 return self.finishAir(inst, result, .{ reduce.operand, .none, .none });
60676054}
60686055
6069fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
6056fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!void {
60706057 const pt = self.pt;
60716058 const zcu = pt.zcu;
60726059 const vector_ty = self.typeOfIndex(inst);
......@@ -6090,19 +6077,19 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
60906077 return bt.finishAir(result);
60916078}
60926079
6093fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
6080fn airUnionInit(self: *Self, inst: Air.Inst.Index) InnerError!void {
60946081 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
60956082 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
60966083 _ = extra;
60976084 return self.fail("TODO implement airUnionInit for aarch64", .{});
60986085}
60996086
6100fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
6087fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!void {
61016088 const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
61026089 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
61036090}
61046091
6105fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
6092fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!void {
61066093 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
61076094 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
61086095 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
......@@ -6111,7 +6098,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
61116098 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
61126099}
61136100
6114fn airTry(self: *Self, inst: Air.Inst.Index) !void {
6101fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!void {
61156102 const pt = self.pt;
61166103 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
61176104 const extra = self.air.extraData(Air.Try, pl_op.payload);
......@@ -6139,7 +6126,7 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {
61396126 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
61406127}
61416128
6142fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
6129fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
61436130 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
61446131 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
61456132 const body = self.air.extra[extra.end..][0..extra.data.body_len];
......@@ -6191,10 +6178,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
61916178 .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },
61926179 .load_symbol, .load_tlv, .lea_symbol, .lea_direct => unreachable, // TODO
61936180 },
6194 .fail => |msg| {
6195 self.err_msg = msg;
6196 return error.CodegenFail;
6197 },
6181 .fail => |msg| return self.failMsg(msg),
61986182 };
61996183 return mcv;
62006184}
......@@ -6355,18 +6339,14 @@ fn wantSafety(self: *Self) bool {
63556339 };
63566340}
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 } {
63596343 @branchHint(.cold);
6360 assert(self.err_msg == null);
6361 self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args);
6362 return error.CodegenFail;
6344 return self.pt.zcu.codegenFail(self.owner_nav, format, args);
63636345}
63646346
6365fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError {
6347fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } {
63666348 @branchHint(.cold);
6367 assert(self.err_msg == null);
6368 self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args);
6369 return error.CodegenFail;
6349 return self.pt.zcu.codegenFailMsg(self.owner_nav, msg);
63706350}
63716351
63726352fn parseRegName(name: []const u8) ?Register {
src/arch/aarch64/Emit.zig+4-2
......@@ -20,7 +20,7 @@ debug_output: link.File.DebugInfoOutput,
2020target: *const std.Target,
2121err_msg: ?*ErrorMsg = null,
2222src_loc: Zcu.LazySrcLoc,
23code: *std.ArrayList(u8),
23code: *std.ArrayListUnmanaged(u8),
2424
2525prev_di_line: u32,
2626prev_di_column: u32,
......@@ -424,8 +424,10 @@ fn lowerBranches(emit: *Emit) !void {
424424}
425425
426426fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
427 const comp = emit.bin_file.comp;
428 const gpa = comp.gpa;
427429 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);
429431}
430432
431433fn 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);
2323const build_options = @import("build_options");
2424const Alignment = InternPool.Alignment;
2525
26const Result = codegen.Result;
2726const CodeGenError = codegen.CodeGenError;
2827
2928const bits = @import("bits.zig");
......@@ -333,9 +332,9 @@ pub fn generate(
333332 func_index: InternPool.Index,
334333 air: Air,
335334 liveness: Liveness,
336 code: *std.ArrayList(u8),
335 code: *std.ArrayListUnmanaged(u8),
337336 debug_output: link.File.DebugInfoOutput,
338) CodeGenError!Result {
337) CodeGenError!void {
339338 const zcu = pt.zcu;
340339 const gpa = zcu.gpa;
341340 const func = zcu.funcInfo(func_index);
......@@ -377,10 +376,7 @@ pub fn generate(
377376 defer function.dbg_info_relocs.deinit(gpa);
378377
379378 var call_info = function.resolveCallingConventionValues(func_ty) catch |err| switch (err) {
380 error.CodegenFail => return Result{ .fail = function.err_msg.? },
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 },
379 error.CodegenFail => return error.CodegenFail,
384380 else => |e| return e,
385381 };
386382 defer call_info.deinit(&function);
......@@ -391,15 +387,14 @@ pub fn generate(
391387 function.max_end_stack = call_info.stack_byte_count;
392388
393389 function.gen() catch |err| switch (err) {
394 error.CodegenFail => return Result{ .fail = function.err_msg.? },
395 error.OutOfRegisters => return Result{
396 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
397 },
390 error.CodegenFail => return error.CodegenFail,
391 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
398392 else => |e| return e,
399393 };
400394
401395 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)});
403398 }
404399
405400 var mir = Mir{
......@@ -424,15 +419,9 @@ pub fn generate(
424419 defer emit.deinit();
425420
426421 emit.emitMir() catch |err| switch (err) {
427 error.EmitFail => return Result{ .fail = emit.err_msg.? },
422 error.EmitFail => return function.failMsg(emit.err_msg.?),
428423 else => |e| return e,
429424 };
430
431 if (function.err_msg) |em| {
432 return Result{ .fail = em };
433 } else {
434 return Result.ok;
435 }
436425}
437426
438427fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
......@@ -6310,20 +6299,19 @@ fn wantSafety(self: *Self) bool {
63106299 };
63116300}
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 } {
63146303 @branchHint(.cold);
6315 assert(self.err_msg == null);
6316 const gpa = self.gpa;
6317 self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args);
6318 return error.CodegenFail;
6304 const zcu = self.pt.zcu;
6305 const func = zcu.funcInfo(self.func_index);
6306 const msg = try ErrorMsg.create(zcu.gpa, self.src_loc, format, args);
6307 return zcu.codegenFailMsg(func.owner_nav, msg);
63196308}
63206309
6321fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError {
6310fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } {
63226311 @branchHint(.cold);
6323 assert(self.err_msg == null);
6324 const gpa = self.gpa;
6325 self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args);
6326 return error.CodegenFail;
6312 const zcu = self.pt.zcu;
6313 const func = zcu.funcInfo(self.func_index);
6314 return zcu.codegenFailMsg(func.owner_nav, msg);
63276315}
63286316
63296317fn parseRegName(name: []const u8) ?Register {
src/arch/arm/Emit.zig+4-2
......@@ -24,7 +24,7 @@ debug_output: link.File.DebugInfoOutput,
2424target: *const std.Target,
2525err_msg: ?*ErrorMsg = null,
2626src_loc: Zcu.LazySrcLoc,
27code: *std.ArrayList(u8),
27code: *std.ArrayListUnmanaged(u8),
2828
2929prev_di_line: u32,
3030prev_di_column: u32,
......@@ -342,8 +342,10 @@ fn lowerBranches(emit: *Emit) !void {
342342}
343343
344344fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
345 const comp = emit.bin_file.comp;
346 const gpa = comp.gpa;
345347 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);
347349}
348350
349351fn 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);
3232const Alignment = InternPool.Alignment;
3333
3434const CodeGenError = codegen.CodeGenError;
35const Result = codegen.Result;
3635
3736const bits = @import("bits.zig");
3837const abi = @import("abi.zig");
......@@ -62,7 +61,6 @@ gpa: Allocator,
6261mod: *Package.Module,
6362target: *const std.Target,
6463debug_output: link.File.DebugInfoOutput,
65err_msg: ?*ErrorMsg,
6664args: []MCValue,
6765ret_mcv: InstTracking,
6866fn_type: Type,
......@@ -759,9 +757,9 @@ pub fn generate(
759757 func_index: InternPool.Index,
760758 air: Air,
761759 liveness: Liveness,
762 code: *std.ArrayList(u8),
760 code: *std.ArrayListUnmanaged(u8),
763761 debug_output: link.File.DebugInfoOutput,
764) CodeGenError!Result {
762) CodeGenError!void {
765763 const zcu = pt.zcu;
766764 const comp = zcu.comp;
767765 const gpa = zcu.gpa;
......@@ -788,7 +786,6 @@ pub fn generate(
788786 .target = &mod.resolved_target.result,
789787 .debug_output = debug_output,
790788 .owner = .{ .nav_index = func.owner_nav },
791 .err_msg = null,
792789 .args = undefined, // populated after `resolveCallingConventionValues`
793790 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
794791 .fn_type = fn_type,
......@@ -829,10 +826,7 @@ pub fn generate(
829826
830827 const fn_info = zcu.typeToFunc(fn_type).?;
831828 var call_info = function.resolveCallingConventionValues(fn_info, &.{}) catch |err| switch (err) {
832 error.CodegenFail => return Result{ .fail = function.err_msg.? },
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 },
829 error.CodegenFail => return error.CodegenFail,
836830 else => |e| return e,
837831 };
838832
......@@ -861,10 +855,8 @@ pub fn generate(
861855 }));
862856
863857 function.gen() catch |err| switch (err) {
864 error.CodegenFail => return Result{ .fail = function.err_msg.? },
865 error.OutOfRegisters => return Result{
866 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
867 },
858 error.CodegenFail => return error.CodegenFail,
859 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
868860 else => |e| return e,
869861 };
870862
......@@ -895,28 +887,10 @@ pub fn generate(
895887 defer emit.deinit();
896888
897889 emit.emitMir() catch |err| switch (err) {
898 error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? },
899 error.InvalidInstruction => |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 },
890 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?),
891 error.InvalidInstruction => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}),
912892 else => |e| return e,
913893 };
914
915 if (function.err_msg) |em| {
916 return Result{ .fail = em };
917 } else {
918 return Result.ok;
919 }
920894}
921895
922896pub fn generateLazy(
......@@ -924,9 +898,9 @@ pub fn generateLazy(
924898 pt: Zcu.PerThread,
925899 src_loc: Zcu.LazySrcLoc,
926900 lazy_sym: link.File.LazySymbol,
927 code: *std.ArrayList(u8),
901 code: *std.ArrayListUnmanaged(u8),
928902 debug_output: link.File.DebugInfoOutput,
929) CodeGenError!Result {
903) CodeGenError!void {
930904 const comp = bin_file.comp;
931905 const gpa = comp.gpa;
932906 const mod = comp.root_mod;
......@@ -941,7 +915,6 @@ pub fn generateLazy(
941915 .target = &mod.resolved_target.result,
942916 .debug_output = debug_output,
943917 .owner = .{ .lazy_sym = lazy_sym },
944 .err_msg = null,
945918 .args = undefined, // populated after `resolveCallingConventionValues`
946919 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
947920 .fn_type = undefined,
......@@ -957,10 +930,8 @@ pub fn generateLazy(
957930 defer function.mir_instructions.deinit(gpa);
958931
959932 function.genLazy(lazy_sym) catch |err| switch (err) {
960 error.CodegenFail => return Result{ .fail = function.err_msg.? },
961 error.OutOfRegisters => return Result{
962 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
963 },
933 error.CodegenFail => return error.CodegenFail,
934 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
964935 else => |e| return e,
965936 };
966937
......@@ -991,28 +962,10 @@ pub fn generateLazy(
991962 defer emit.deinit();
992963
993964 emit.emitMir() catch |err| switch (err) {
994 error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? },
995 error.InvalidInstruction => |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 },
965 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?),
966 error.InvalidInstruction => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}),
1008967 else => |e| return e,
1009968 };
1010
1011 if (function.err_msg) |em| {
1012 return Result{ .fail = em };
1013 } else {
1014 return Result.ok;
1015 }
1016969}
1017970
1018971const FormatWipMirData = struct {
......@@ -4758,19 +4711,19 @@ fn airFieldParentPtr(func: *Func, inst: Air.Inst.Index) !void {
47584711 return func.fail("TODO implement codegen airFieldParentPtr", .{});
47594712}
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 {
47624715 const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg;
47634716 const ty = arg.ty.toType();
47644717 if (arg.name == .none) return;
47654718
47664719 switch (func.debug_output) {
47674720 .dwarf => |dw| switch (mcv) {
4768 .register => |reg| try dw.genLocalDebugInfo(
4721 .register => |reg| dw.genLocalDebugInfo(
47694722 .local_arg,
47704723 arg.name.toSlice(func.air),
47714724 ty,
47724725 .{ .reg = reg.dwarfNum() },
4773 ),
4726 ) catch |err| return func.fail("failed to generate debug info: {s}", .{@errorName(err)}),
47744727 .load_frame => {},
47754728 else => {},
47764729 },
......@@ -4779,7 +4732,7 @@ fn genArgDbgInfo(func: Func, inst: Air.Inst.Index, mcv: MCValue) !void {
47794732 }
47804733}
47814734
4782fn airArg(func: *Func, inst: Air.Inst.Index) !void {
4735fn airArg(func: *Func, inst: Air.Inst.Index) InnerError!void {
47834736 var arg_index = func.arg_index;
47844737
47854738 // we skip over args that have no bits
......@@ -5255,7 +5208,7 @@ fn airDbgInlineBlock(func: *Func, inst: Air.Inst.Index) !void {
52555208 try func.lowerBlock(inst, @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]));
52565209}
52575210
5258fn airDbgVar(func: *Func, inst: Air.Inst.Index) !void {
5211fn airDbgVar(func: *Func, inst: Air.Inst.Index) InnerError!void {
52595212 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
52605213 const operand = pl_op.operand;
52615214 const ty = func.typeOf(operand);
......@@ -5263,7 +5216,8 @@ fn airDbgVar(func: *Func, inst: Air.Inst.Index) !void {
52635216 const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
52645217
52655218 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
52685222 return func.finishAir(inst, .unreach, .{ operand, .none, .none });
52695223}
......@@ -8236,10 +8190,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
82368190 return func.fail("TODO: genTypedValue {s}", .{@tagName(mcv)});
82378191 },
82388192 },
8239 .fail => |msg| {
8240 func.err_msg = msg;
8241 return error.CodegenFail;
8242 },
8193 .fail => |msg| return func.failMsg(msg),
82438194 };
82448195 return mcv;
82458196}
......@@ -8427,17 +8378,23 @@ fn wantSafety(func: *Func) bool {
84278378 };
84288379}
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 } {
84318382 @branchHint(.cold);
8432 assert(func.err_msg == null);
8433 func.err_msg = try ErrorMsg.create(func.gpa, func.src_loc, format, args);
8383 const zcu = func.pt.zcu;
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 }
84348388 return error.CodegenFail;
84358389}
84368390
8437fn failSymbol(func: *Func, comptime format: []const u8, args: anytype) InnerError {
8391fn failMsg(func: *const Func, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } {
84388392 @branchHint(.cold);
8439 assert(func.err_msg == null);
8440 func.err_msg = try ErrorMsg.create(func.gpa, func.src_loc, format, args);
8393 const zcu = func.pt.zcu;
8394 switch (func.owner) {
8395 .nav_index => |i| return zcu.codegenFailMsg(i, msg),
8396 .lazy_sym => |s| return zcu.codegenFailTypeMsg(s.ty, msg),
8397 }
84418398 return error.CodegenFail;
84428399}
84438400
src/arch/riscv64/Emit.zig+9-8
......@@ -3,7 +3,7 @@
33bin_file: *link.File,
44lower: Lower,
55debug_output: link.File.DebugInfoOutput,
6code: *std.ArrayList(u8),
6code: *std.ArrayListUnmanaged(u8),
77
88prev_di_line: u32,
99prev_di_column: u32,
......@@ -18,6 +18,7 @@ pub const Error = Lower.Error || error{
1818};
1919
2020pub fn emitMir(emit: *Emit) Error!void {
21 const gpa = emit.bin_file.comp.gpa;
2122 log.debug("mir instruction len: {}", .{emit.lower.mir.instructions.len});
2223 for (0..emit.lower.mir.instructions.len) |mir_i| {
2324 const mir_index: Mir.Inst.Index = @intCast(mir_i);
......@@ -30,7 +31,7 @@ pub fn emitMir(emit: *Emit) Error!void {
3031 var lowered_relocs = lowered.relocs;
3132 for (lowered.insts, 0..) |lowered_inst, lowered_index| {
3233 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
3536 while (lowered_relocs.len > 0 and
3637 lowered_relocs[0].lowered_inst_index == lowered_index) : ({
......@@ -56,13 +57,13 @@ pub fn emitMir(emit: *Emit) Error!void {
5657 const hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20);
5758 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, .{
6061 .r_offset = start_offset,
6162 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type,
6263 .r_addend = 0,
6364 }, zo);
6465
65 try atom_ptr.addReloc(elf_file.base.comp.gpa, .{
66 try atom_ptr.addReloc(gpa, .{
6667 .r_offset = start_offset + 4,
6768 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type,
6869 .r_addend = 0,
......@@ -76,19 +77,19 @@ pub fn emitMir(emit: *Emit) Error!void {
7677
7778 const R_RISCV = std.elf.R_RISCV;
7879
79 try atom_ptr.addReloc(elf_file.base.comp.gpa, .{
80 try atom_ptr.addReloc(gpa, .{
8081 .r_offset = start_offset,
8182 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20),
8283 .r_addend = 0,
8384 }, zo);
8485
85 try atom_ptr.addReloc(elf_file.base.comp.gpa, .{
86 try atom_ptr.addReloc(gpa, .{
8687 .r_offset = start_offset + 4,
8788 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD),
8889 .r_addend = 0,
8990 }, zo);
9091
91 try atom_ptr.addReloc(elf_file.base.comp.gpa, .{
92 try atom_ptr.addReloc(gpa, .{
9293 .r_offset = start_offset + 8,
9394 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I),
9495 .r_addend = 0,
......@@ -101,7 +102,7 @@ pub fn emitMir(emit: *Emit) Error!void {
101102
102103 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, .{
105106 .r_offset = start_offset,
106107 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
107108 .r_addend = 0,
src/arch/sparc64/CodeGen.zig+24-28
......@@ -21,7 +21,6 @@ const Emit = @import("Emit.zig");
2121const Liveness = @import("../../Liveness.zig");
2222const Type = @import("../../Type.zig");
2323const CodeGenError = codegen.CodeGenError;
24const Result = @import("../../codegen.zig").Result;
2524const Endian = std.builtin.Endian;
2625const Alignment = InternPool.Alignment;
2726
......@@ -55,7 +54,7 @@ liveness: Liveness,
5554bin_file: *link.File,
5655target: *const std.Target,
5756func_index: InternPool.Index,
58code: *std.ArrayList(u8),
57code: *std.ArrayListUnmanaged(u8),
5958debug_output: link.File.DebugInfoOutput,
6059err_msg: ?*ErrorMsg,
6160args: []MCValue,
......@@ -266,9 +265,9 @@ pub fn generate(
266265 func_index: InternPool.Index,
267266 air: Air,
268267 liveness: Liveness,
269 code: *std.ArrayList(u8),
268 code: *std.ArrayListUnmanaged(u8),
270269 debug_output: link.File.DebugInfoOutput,
271) CodeGenError!Result {
270) CodeGenError!void {
272271 const zcu = pt.zcu;
273272 const gpa = zcu.gpa;
274273 const func = zcu.funcInfo(func_index);
......@@ -284,7 +283,7 @@ pub fn generate(
284283 }
285284 try branch_stack.append(.{});
286285
287 var function = Self{
286 var function: Self = .{
288287 .gpa = gpa,
289288 .pt = pt,
290289 .air = air,
......@@ -310,10 +309,7 @@ pub fn generate(
310309 defer function.exitlude_jump_relocs.deinit(gpa);
311310
312311 var call_info = function.resolveCallingConventionValues(func_ty, .callee) catch |err| switch (err) {
313 error.CodegenFail => return Result{ .fail = function.err_msg.? },
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 },
312 error.CodegenFail => return error.CodegenFail,
317313 else => |e| return e,
318314 };
319315 defer call_info.deinit(&function);
......@@ -324,10 +320,8 @@ pub fn generate(
324320 function.max_end_stack = call_info.stack_byte_count;
325321
326322 function.gen() catch |err| switch (err) {
327 error.CodegenFail => return Result{ .fail = function.err_msg.? },
328 error.OutOfRegisters => return Result{
329 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
330 },
323 error.CodegenFail => return error.CodegenFail,
324 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
331325 else => |e| return e,
332326 };
333327
......@@ -337,7 +331,7 @@ pub fn generate(
337331 };
338332 defer mir.deinit(gpa);
339333
340 var emit = Emit{
334 var emit: Emit = .{
341335 .mir = mir,
342336 .bin_file = lf,
343337 .debug_output = debug_output,
......@@ -351,15 +345,9 @@ pub fn generate(
351345 defer emit.deinit();
352346
353347 emit.emitMir() catch |err| switch (err) {
354 error.EmitFail => return Result{ .fail = emit.err_msg.? },
348 error.EmitFail => return function.failMsg(emit.err_msg.?),
355349 else => |e| return e,
356350 };
357
358 if (function.err_msg) |em| {
359 return Result{ .fail = em };
360 } else {
361 return Result.ok;
362 }
363351}
364352
365353fn gen(self: *Self) !void {
......@@ -1014,7 +1002,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
10141002 return bt.finishAir(result);
10151003}
10161004
1017fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1005fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
10181006 const pt = self.pt;
10191007 const zcu = pt.zcu;
10201008 const arg_index = self.arg_index;
......@@ -1036,7 +1024,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
10361024 }
10371025 };
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
10411030 if (self.liveness.isUnused(inst))
10421031 return self.finishAirBookkeeping();
......@@ -3511,12 +3500,19 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)
35113500 }
35123501}
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 } {
35153504 @branchHint(.cold);
3516 assert(self.err_msg == null);
3517 const gpa = self.gpa;
3518 self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args);
3519 return error.CodegenFail;
3505 const zcu = self.pt.zcu;
3506 const func = zcu.funcInfo(self.func_index);
3507 const msg = try ErrorMsg.create(zcu.gpa, self.src_loc, format, args);
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);
35203516}
35213517
35223518/// 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,
2222target: *const std.Target,
2323err_msg: ?*ErrorMsg = null,
2424src_loc: Zcu.LazySrcLoc,
25code: *std.ArrayList(u8),
25code: *std.ArrayListUnmanaged(u8),
2626
2727prev_di_line: u32,
2828prev_di_column: u32,
......@@ -678,10 +678,13 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType {
678678}
679679
680680fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
681 const comp = emit.bin_file.comp;
682 const gpa = comp.gpa;
683
681684 // SPARCv9 instructions are always arranged in BE regardless of the
682685 // endianness mode the CPU is running in (Section 3.1 of the ISA specification).
683686 // 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);
687690}
src/arch/wasm/CodeGen.zig+3170-3455
......@@ -1,14 +1,13 @@
11const std = @import("std");
22const builtin = @import("builtin");
33const Allocator = std.mem.Allocator;
4const ArrayList = std.ArrayList;
54const assert = std.debug.assert;
65const testing = std.testing;
76const leb = std.leb;
87const mem = std.mem;
9const wasm = std.wasm;
108const log = std.log.scoped(.codegen);
119
10const CodeGen = @This();
1211const codegen = @import("../../codegen.zig");
1312const Zcu = @import("../../Zcu.zig");
1413const InternPool = @import("../../InternPool.zig");
......@@ -19,13 +18,113 @@ const Compilation = @import("../../Compilation.zig");
1918const link = @import("../../link.zig");
2019const Air = @import("../../Air.zig");
2120const Liveness = @import("../../Liveness.zig");
22const target_util = @import("../../target.zig");
2321const Mir = @import("Mir.zig");
2422const Emit = @import("Emit.zig");
2523const abi = @import("abi.zig");
2624const Alignment = InternPool.Alignment;
2725const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
2826const 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
30129/// Wasm Value, created when generating an instruction
31130const WValue = union(enum) {
......@@ -55,22 +154,15 @@ const WValue = union(enum) {
55154 float32: f32,
56155 /// A constant 64bit float value
57156 float64: f64,
58 /// A value that represents a pointer to the data section
59 /// Note: The value contains the symbol index, rather than the actual address
60 /// as we use this to perform the relocation.
61 memory: u32,
62 /// A value that represents a parent pointer and an offset
63 /// from that pointer. i.e. when slicing with constant values.
64 memory_offset: struct {
65 /// The symbol of the parent pointer
66 pointer: u32,
67 /// Offset will be set as addend when relocating
68 offset: u32,
157 nav_ref: struct {
158 nav_index: InternPool.Nav.Index,
159 offset: i32 = 0,
160 },
161 uav_ref: struct {
162 ip_index: InternPool.Index,
163 offset: i32 = 0,
164 orig_ptr_ty: InternPool.Index = .none,
69165 },
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,
74166 /// Offset from the bottom of the virtual stack, with the offset
75167 /// pointing to where the value lives.
76168 stack_offset: struct {
......@@ -101,7 +193,7 @@ const WValue = union(enum) {
101193 switch (value) {
102194 .stack => {
103195 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);
105197 return new_local;
106198 },
107199 .local, .stack_offset => return value,
......@@ -119,7 +211,7 @@ const WValue = union(enum) {
119211 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.
120212
121213 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];
123215 switch (valtype) {
124216 .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
125217 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,
......@@ -132,8 +224,6 @@ const WValue = union(enum) {
132224 }
133225};
134226
135/// Wasm ops, but without input/output/signedness information
136/// Used for `buildOpcode`
137227const Op = enum {
138228 @"unreachable",
139229 nop,
......@@ -147,12 +237,8 @@ const Op = enum {
147237 br_table,
148238 @"return",
149239 call,
150 call_indirect,
151240 drop,
152241 select,
153 local_get,
154 local_set,
155 local_tee,
156242 global_get,
157243 global_set,
158244 load,
......@@ -200,70 +286,38 @@ const Op = enum {
200286 extend,
201287};
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 }
231289const OpcodeBuildArguments = struct {
232290 /// First valtype in the opcode (usually represents the type of the output)
233 valtype1: ?wasm.Valtype = null,
291 valtype1: ?std.wasm.Valtype = null,
234292 /// The operation (e.g. call, unreachable, div, min, sqrt, etc.)
235293 op: Op,
236294 /// Width of the operation (e.g. 8 for i32_load8_s, 16 for i64_extend16_i32_s)
237295 width: ?u8 = null,
238296 /// Second valtype in the opcode name (usually represents the type of the input)
239 valtype2: ?wasm.Valtype = null,
297 valtype2: ?std.wasm.Valtype = null,
240298 /// Signedness of the op
241299 signedness: ?std.builtin.Signedness = null,
242300};
243301
244/// Helper function that builds an Opcode given the arguments needed
245fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
302/// TODO: deprecated, should be split up per tag.
303fn buildOpcode(args: OpcodeBuildArguments) std.wasm.Opcode {
246304 switch (args.op) {
247 .@"unreachable" => return .@"unreachable",
248 .nop => return .nop,
249 .block => return .block,
250 .loop => return .loop,
251 .@"if" => return .@"if",
252 .@"else" => return .@"else",
253 .end => return .end,
254 .br => return .br,
255 .br_if => return .br_if,
256 .br_table => return .br_table,
257 .@"return" => return .@"return",
258 .call => return .call,
259 .call_indirect => return .call_indirect,
260 .drop => return .drop,
261 .select => return .select,
262 .local_get => return .local_get,
263 .local_set => return .local_set,
264 .local_tee => return .local_tee,
265 .global_get => return .global_get,
266 .global_set => return .global_set,
305 .@"unreachable" => unreachable,
306 .nop => unreachable,
307 .block => unreachable,
308 .loop => unreachable,
309 .@"if" => unreachable,
310 .@"else" => unreachable,
311 .end => unreachable,
312 .br => unreachable,
313 .br_if => unreachable,
314 .br_table => unreachable,
315 .@"return" => unreachable,
316 .call => unreachable,
317 .drop => unreachable,
318 .select => unreachable,
319 .global_get => unreachable,
320 .global_set => unreachable,
267321
268322 .load => if (args.width) |width| switch (width) {
269323 8 => switch (args.valtype1.?) {
......@@ -621,121 +675,17 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
621675test "Wasm - buildOpcode" {
622676 // Make sure buildOpcode is referenced, and test some examples
623677 const i32_const = buildOpcode(.{ .op = .@"const", .valtype1 = .i32 });
624 const end = buildOpcode(.{ .op = .end });
625 const local_get = buildOpcode(.{ .op = .local_get });
626678 const i64_extend32_s = buildOpcode(.{ .op = .extend, .valtype1 = .i64, .width = 32, .signedness = .signed });
627679 const f64_reinterpret_i64 = buildOpcode(.{ .op = .reinterpret, .valtype1 = .f64, .valtype2 = .i64 });
628680
629 try testing.expectEqual(@as(wasm.Opcode, .i32_const), i32_const);
630 try testing.expectEqual(@as(wasm.Opcode, .end), end);
631 try testing.expectEqual(@as(wasm.Opcode, .local_get), local_get);
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);
681 try testing.expectEqual(@as(std.wasm.Opcode, .i32_const), i32_const);
682 try testing.expectEqual(@as(std.wasm.Opcode, .i64_extend32_s), i64_extend32_s);
683 try testing.expectEqual(@as(std.wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64);
634684}
635685
636686/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
637687pub 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
739689const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
740690
741691const InnerError = error{
......@@ -746,38 +696,33 @@ const InnerError = error{
746696 Overflow,
747697} || link.File.UpdateDebugInfoError;
748698
749pub fn deinit(func: *CodeGen) void {
750 // in case of an error and we still have branches
751 for (func.branches.items) |*branch| {
752 branch.deinit(func.gpa);
753 }
754 func.branches.deinit(func.gpa);
755 func.blocks.deinit(func.gpa);
756 func.loops.deinit(func.gpa);
757 func.locals.deinit(func.gpa);
758 func.simd_immediates.deinit(func.gpa);
759 func.mir_instructions.deinit(func.gpa);
760 func.mir_extra.deinit(func.gpa);
761 func.free_locals_i32.deinit(func.gpa);
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;
699pub fn deinit(cg: *CodeGen) void {
700 const gpa = cg.gpa;
701 for (cg.branches.items) |*branch| branch.deinit(gpa);
702 cg.branches.deinit(gpa);
703 cg.blocks.deinit(gpa);
704 cg.loops.deinit(gpa);
705 cg.simd_immediates.deinit(gpa);
706 cg.free_locals_i32.deinit(gpa);
707 cg.free_locals_i64.deinit(gpa);
708 cg.free_locals_f32.deinit(gpa);
709 cg.free_locals_f64.deinit(gpa);
710 cg.free_locals_v128.deinit(gpa);
711 cg.* = undefined;
767712}
768713
769/// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig
770fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError {
771 func.err_msg = try Zcu.ErrorMsg.create(func.gpa, func.src_loc, fmt, args);
772 return error.CodegenFail;
714fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
715 const zcu = cg.pt.zcu;
716 const func = zcu.funcInfo(cg.func_index);
717 return zcu.codegenFail(func.owner_nav, fmt, args);
773718}
774719
775720/// Resolves the `WValue` for the given instruction `inst`
776721/// When the given instruction has a `Value`, it returns a constant instead
777fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
778 var branch_index = func.branches.items.len;
722fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
723 var branch_index = cg.branches.items.len;
779724 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];
781726 if (branch.values.get(ref)) |value| {
782727 return value;
783728 }
......@@ -787,16 +732,16 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
787732 // means we must generate it from a constant.
788733 // We always store constants in the most outer branch as they must never
789734 // 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);
791736 assert(!gop.found_existing);
792737
793 const pt = func.pt;
738 const pt = cg.pt;
794739 const zcu = pt.zcu;
795 const val = (try func.air.value(ref, pt)).?;
796 const ty = func.typeOf(ref);
740 const val = (try cg.air.value(ref, pt)).?;
741 const ty = cg.typeOf(ref);
797742 if (!ty.hasRuntimeBitsIgnoreComptime(zcu) and !ty.isInt(zcu) and !ty.isError(zcu)) {
798743 gop.value_ptr.* = .none;
799 return gop.value_ptr.*;
744 return .none;
800745 }
801746
802747 // 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 {
805750 //
806751 // In the other cases, we will simply lower the constant to a value that fits
807752 // into a single local (such as a pointer, integer, bool, etc).
808 const result: WValue = if (isByRef(ty, pt, func.target.*))
809 switch (try func.bin_file.lowerUav(pt, val.toIntern(), .none, func.src_loc)) {
810 .mcv => |mcv| .{ .memory = mcv.load_symbol },
811 .fail => |err_msg| {
812 func.err_msg = err_msg;
813 return error.CodegenFail;
814 },
815 }
753 const result: WValue = if (isByRef(ty, zcu, cg.target))
754 .{ .uav_ref = .{ .ip_index = val.toIntern() } }
816755 else
817 try func.lowerConstant(val, ty);
756 try cg.lowerConstant(val, ty);
818757
819758 gop.value_ptr.* = result;
820759 return result;
821760}
822761
823762/// 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 {
825764 assert(operands.len <= Liveness.bpi - 1);
826 var tomb_bits = func.liveness.getTombBits(inst);
765 var tomb_bits = cg.liveness.getTombBits(inst);
827766 for (operands) |operand| {
828767 const dies = @as(u1, @truncate(tomb_bits)) != 0;
829768 tomb_bits >>= 1;
830769 if (!dies) continue;
831 processDeath(func, operand);
770 processDeath(cg, operand);
832771 }
833772
834773 // results of `none` can never be referenced.
......@@ -836,13 +775,13 @@ fn finishAir(func: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []c
836775 const trackable_result = if (result != .stack)
837776 result
838777 else
839 try result.toLocal(func, func.typeOfIndex(inst));
840 const branch = func.currentBranch();
778 try result.toLocal(cg, cg.typeOfIndex(inst));
779 const branch = cg.currentBranch();
841780 branch.values.putAssumeCapacityNoClobber(inst.toRef(), trackable_result);
842781 }
843782
844783 if (std.debug.runtime_safety) {
845 func.air_bookkeeping += 1;
784 cg.air_bookkeeping += 1;
846785 }
847786}
848787
......@@ -855,8 +794,8 @@ const Branch = struct {
855794 }
856795};
857796
858inline fn currentBranch(func: *CodeGen) *Branch {
859 return &func.branches.items[func.branches.items.len - 1];
797inline fn currentBranch(cg: *CodeGen) *Branch {
798 return &cg.branches.items[cg.branches.items.len - 1];
860799}
861800
862801const BigTomb = struct {
......@@ -883,131 +822,143 @@ const BigTomb = struct {
883822 }
884823};
885824
886fn iterateBigTomb(func: *CodeGen, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
887 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, operand_count + 1);
825fn iterateBigTomb(cg: *CodeGen, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
826 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, operand_count + 1);
888827 return BigTomb{
889 .gen = func,
828 .gen = cg,
890829 .inst = inst,
891 .lbt = func.liveness.iterateBigTomb(inst),
830 .lbt = cg.liveness.iterateBigTomb(inst),
892831 };
893832}
894833
895fn processDeath(func: *CodeGen, ref: Air.Inst.Ref) void {
834fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void {
896835 if (ref.toIndex() == null) return;
897836 // Branches are currently only allowed to free locals allocated
898837 // within their own branch.
899838 // 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;
901840 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);
903842 if (value.local.value < reserved_indexes) {
904843 return; // function arguments can never be re-used
905844 }
906845 log.debug("Decreasing reference for ref: %{d}, using local '{d}'", .{ @intFromEnum(ref.toIndex().?), value.local.value });
907846 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer
908847 if (value.local.references == 0) {
909 value.free(func);
848 value.free(cg);
910849 }
911850}
912851
913/// Appends a MIR instruction and returns its index within the list of instructions
914fn addInst(func: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {
915 try func.mir_instructions.append(func.gpa, inst);
852fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {
853 try cg.mir_instructions.append(cg.gpa, inst);
854}
855
856fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
857 try cg.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
916858}
917859
918fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
919 try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
860fn addExtended(cg: *CodeGen, opcode: std.wasm.MiscOpcode) error{OutOfMemory}!void {
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 } });
920864}
921865
922fn addExtended(func: *CodeGen, opcode: wasm.MiscOpcode) error{OutOfMemory}!void {
923 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));
924 try func.mir_extra.append(func.gpa, @intFromEnum(opcode));
925 try func.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });
866fn addLabel(cg: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {
867 try cg.addInst(.{ .tag = tag, .data = .{ .label = label } });
926868}
927869
928fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {
929 try func.addInst(.{ .tag = tag, .data = .{ .label = label } });
870fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void {
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 } });
930876}
931877
932878/// Accepts an unsigned 32bit integer rather than a signed integer to
933879/// prevent us from having to bitcast multiple times as most values
934880/// within codegen are represented as unsigned rather than signed.
935fn addImm32(func: *CodeGen, imm: u32) error{OutOfMemory}!void {
936 try func.addInst(.{ .tag = .i32_const, .data = .{ .imm32 = @bitCast(imm) } });
881fn addImm32(cg: *CodeGen, imm: u32) error{OutOfMemory}!void {
882 try cg.addInst(.{ .tag = .i32_const, .data = .{ .imm32 = @bitCast(imm) } });
937883}
938884
939885/// Accepts an unsigned 64bit integer rather than a signed integer to
940886/// prevent us from having to bitcast multiple times as most values
941887/// within codegen are represented as unsigned rather than signed.
942fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void {
943 const extra_index = try func.addExtra(Mir.Imm64.fromU64(imm));
944 try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });
888fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void {
889 const extra_index = try cg.addExtra(Mir.Imm64.init(imm));
890 try cg.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });
945891}
946892
947893/// Accepts the index into the list of 128bit-immediates
948fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void {
949 const simd_values = func.simd_immediates.items[index];
950 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));
894fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void {
895 const simd_values = cg.simd_immediates.items[index];
896 const extra_index = cg.extraLen();
951897 // tag + 128bit value
952 try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);
953 func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));
954 func.mir_extra.appendSliceAssumeCapacity(@alignCast(mem.bytesAsSlice(u32, &simd_values)));
955 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
898 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 5);
899 cg.mir_extra.appendAssumeCapacity(@intFromEnum(std.wasm.SimdOpcode.v128_const));
900 cg.mir_extra.appendSliceAssumeCapacity(@alignCast(mem.bytesAsSlice(u32, &simd_values)));
901 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
956902}
957903
958fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {
959 const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float));
960 try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
904fn addFloat64(cg: *CodeGen, float: f64) error{OutOfMemory}!void {
905 const extra_index = try cg.addExtra(Mir.Float64.init(float));
906 try cg.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
961907}
962908
963909/// 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 {
965 const extra_index = try func.addExtra(mem_arg);
966 try func.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } });
910fn addMemArg(cg: *CodeGen, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
911 const extra_index = try cg.addExtra(mem_arg);
912 try cg.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } });
967913}
968914
969915/// Inserts an instruction from the 'atomics' feature which accesses wasm's linear memory dependent on the
970916/// given `tag`.
971fn addAtomicMemArg(func: *CodeGen, tag: wasm.AtomicsOpcode, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
972 const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = wasm.atomicsOpcode(tag) }));
973 _ = try func.addExtra(mem_arg);
974 try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });
917fn addAtomicMemArg(cg: *CodeGen, tag: std.wasm.AtomicsOpcode, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
918 const extra_index = try cg.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) }));
919 _ = try cg.addExtra(mem_arg);
920 try cg.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });
975921}
976922
977923/// Helper function to emit atomic mir opcodes.
978fn addAtomicTag(func: *CodeGen, tag: wasm.AtomicsOpcode) error{OutOfMemory}!void {
979 const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = wasm.atomicsOpcode(tag) }));
980 try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });
924fn addAtomicTag(cg: *CodeGen, tag: std.wasm.AtomicsOpcode) error{OutOfMemory}!void {
925 const extra_index = try cg.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) }));
926 try cg.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });
981927}
982928
983929/// Appends entries to `mir_extra` based on the type of `extra`.
984930/// Returns the index into `mir_extra`
985fn addExtra(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
931fn addExtra(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
986932 const fields = std.meta.fields(@TypeOf(extra));
987 try func.mir_extra.ensureUnusedCapacity(func.gpa, fields.len);
988 return func.addExtraAssumeCapacity(extra);
933 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, fields.len);
934 return cg.addExtraAssumeCapacity(extra);
989935}
990936
991937/// Appends entries to `mir_extra` based on the type of `extra`.
992938/// Returns the index into `mir_extra`
993fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
939fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
994940 const fields = std.meta.fields(@TypeOf(extra));
995 const result = @as(u32, @intCast(func.mir_extra.items.len));
941 const result = cg.extraLen();
996942 inline for (fields) |field| {
997 func.mir_extra.appendAssumeCapacity(switch (field.type) {
943 cg.mir_extra.appendAssumeCapacity(switch (field.type) {
998944 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)),
999951 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
1000952 });
1001953 }
1002954 return result;
1003955}
1004956
1005/// Using a given `Type`, returns the corresponding valtype for .auto callconv
1006fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype {
1007 const zcu = pt.zcu;
957/// For `std.builtin.CallingConvention.auto`.
958pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.wasm.Valtype {
1008959 const ip = &zcu.intern_pool;
1009960 return switch (ty.zigTypeTag(zcu)) {
1010 .float => switch (ty.floatBits(target)) {
961 .float => switch (ty.floatBits(target.*)) {
1011962 16 => .i32, // stored/loaded as u16
1012963 32 => .f32,
1013964 64 => .f64,
......@@ -1022,19 +973,20 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype {
1022973 .@"struct" => blk: {
1023974 if (zcu.typeToPackedStruct(ty)) |packed_struct| {
1024975 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);
1026977 } else {
1027978 break :blk .i32;
1028979 }
1029980 },
1030 .vector => switch (determineSimdStoreStrategy(ty, zcu, target)) {
981 .vector => switch (CodeGen.determineSimdStoreStrategy(ty, zcu, target)) {
1031982 .direct => .v128,
1032983 .unrolled => .i32,
1033984 },
1034985 .@"union" => switch (ty.containerLayout(zcu)) {
1035 .@"packed" => blk: {
1036 const int_ty = pt.intType(.unsigned, @as(u16, @intCast(ty.bitSize(zcu)))) catch @panic("out of memory");
1037 break :blk typeToValtype(int_ty, pt, target);
986 .@"packed" => switch (ty.bitSize(zcu)) {
987 0...32 => .i32,
988 33...64 => .i64,
989 else => .i32,
1038990 },
1039991 else => .i32,
1040992 },
......@@ -1042,42 +994,94 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype {
1042994 };
1043995}
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
1050997/// Using a given `Type`, returns the corresponding wasm value type
1051/// Differently from `genValtype` this also allows `void` to create a block
998/// Differently from `typeToValtype` this also allows `void` to create a block
1052999/// 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 {
10541001 return switch (ty.ip_index) {
1055 .void_type, .noreturn_type => wasm.block_empty,
1056 else => genValtype(ty, pt, target),
1002 .void_type, .noreturn_type => .empty,
1003 else => .fromValtype(typeToValtype(ty, zcu, target)),
10571004 };
10581005}
10591006
10601007/// 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 {
10621009 switch (value) {
10631010 .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?)
10641011 .none, .stack => {}, // no-op
1065 .local => |idx| try func.addLabel(.local_get, idx.value),
1066 .imm32 => |val| try func.addImm32(val),
1067 .imm64 => |val| try func.addImm64(val),
1068 .imm128 => |val| try func.addImm128(val),
1069 .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
1070 .float64 => |val| try func.addFloat64(val),
1071 .memory => |ptr| {
1072 const extra_index = try func.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 });
1073 try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
1012 .local => |idx| try cg.addLocal(.local_get, idx.value),
1013 .imm32 => |val| try cg.addImm32(val),
1014 .imm64 => |val| try cg.addImm64(val),
1015 .imm128 => |val| try cg.addImm128(val),
1016 .float32 => |val| try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
1017 .float64 => |val| try cg.addFloat64(val),
1018 .nav_ref => |nav_ref| {
1019 const wasm = cg.wasm;
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 }
10741044 },
1075 .memory_offset => |mem_off| {
1076 const extra_index = try func.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset });
1077 try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
1045 .uav_ref => |uav| {
1046 const wasm = cg.wasm;
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 }
10781083 },
1079 .function_index => |index| try func.addLabel(.function_index, index), // write function index and generate relocation
1080 .stack_offset => try func.addLabel(.local_get, func.bottom_stack_value.local.value), // caller must ensure to address the offset
1084 .stack_offset => try cg.addLocal(.local_get, cg.bottom_stack_value.local.value), // caller must ensure to address the offset
10811085 }
10821086}
10831087
......@@ -1085,7 +1089,7 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
10851089/// The old `WValue` found at instruction `ref` is then replaced by the
10861090/// modified `WValue` and returned. When given a non-local or non-stack-offset,
10871091/// 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 {
10891093 if (operand != .local and operand != .stack_offset) return operand;
10901094 var new_value = operand;
10911095 switch (new_value) {
......@@ -1093,17 +1097,17 @@ fn reuseOperand(func: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue {
10931097 .stack_offset => |*stack_offset| stack_offset.references += 1,
10941098 else => unreachable,
10951099 }
1096 const old_value = func.getResolvedInst(ref);
1100 const old_value = cg.getResolvedInst(ref);
10971101 old_value.* = new_value;
10981102 return new_value;
10991103}
11001104
11011105/// From a reference, returns its resolved `WValue`.
11021106/// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet.
1103fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue {
1104 var index = func.branches.items.len;
1107fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue {
1108 var index = cg.branches.items.len;
11051109 while (index > 0) : (index -= 1) {
1106 const branch = func.branches.items[index - 1];
1110 const branch = cg.branches.items[index - 1];
11071111 if (branch.values.getPtr(ref)) |value| {
11081112 return value;
11091113 }
......@@ -1113,243 +1117,238 @@ fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue {
11131117
11141118/// Creates one locals for a given `Type`.
11151119/// Returns a corresponding `Wvalue` with `local` as active tag
1116fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
1117 const pt = func.pt;
1118 const valtype = typeToValtype(ty, pt, func.target.*);
1120fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
1121 const zcu = cg.pt.zcu;
1122 const valtype = typeToValtype(ty, zcu, cg.target);
11191123 const index_or_null = switch (valtype) {
1120 .i32 => func.free_locals_i32.popOrNull(),
1121 .i64 => func.free_locals_i64.popOrNull(),
1122 .f32 => func.free_locals_f32.popOrNull(),
1123 .f64 => func.free_locals_f64.popOrNull(),
1124 .v128 => func.free_locals_v128.popOrNull(),
1124 .i32 => cg.free_locals_i32.popOrNull(),
1125 .i64 => cg.free_locals_i64.popOrNull(),
1126 .f32 => cg.free_locals_f32.popOrNull(),
1127 .f64 => cg.free_locals_f64.popOrNull(),
1128 .v128 => cg.free_locals_v128.popOrNull(),
11251129 };
11261130 if (index_or_null) |index| {
11271131 log.debug("reusing local ({d}) of type {}", .{ index, valtype });
11281132 return .{ .local = .{ .value = index, .references = 1 } };
11291133 }
11301134 log.debug("new local of type {}", .{valtype});
1131 return func.ensureAllocLocal(ty);
1135 return cg.ensureAllocLocal(ty);
11321136}
11331137
11341138/// Ensures a new local will be created. This is useful when it's useful
11351139/// to use a zero-initialized local.
1136fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
1137 const pt = func.pt;
1138 try func.locals.append(func.gpa, genValtype(ty, pt, func.target.*));
1139 const initial_index = func.local_index;
1140 func.local_index += 1;
1140fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
1141 const zcu = cg.pt.zcu;
1142 try cg.locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target));
1143 const initial_index = cg.local_index;
1144 cg.local_index += 1;
11411145 return .{ .local = .{ .value = initial_index, .references = 1 } };
11421146}
11431147
1144/// Generates a `wasm.Type` from a given function type.
1145/// Memory is owned by the caller.
1146fn genFunctype(
1147 gpa: Allocator,
1148 cc: std.builtin.CallingConvention,
1149 params: []const InternPool.Index,
1150 return_type: Type,
1151 pt: Zcu.PerThread,
1152 target: std.Target,
1153) !wasm.Type {
1154 const zcu = pt.zcu;
1155 var temp_params = std.ArrayList(wasm.Valtype).init(gpa);
1156 defer temp_params.deinit();
1157 var returns = std.ArrayList(wasm.Valtype).init(gpa);
1158 defer returns.deinit();
1159
1160 if (firstParamSRet(cc, return_type, pt, target)) {
1161 try temp_params.append(.i32); // memory address is always a 32-bit handle
1162 } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) {
1163 if (cc == .wasm_watc) {
1164 const res_classes = abi.classifyType(return_type, zcu);
1165 assert(res_classes[0] == .direct and res_classes[1] == .none);
1166 const scalar_type = abi.scalarType(return_type, zcu);
1167 try returns.append(typeToValtype(scalar_type, pt, target));
1168 } else {
1169 try returns.append(typeToValtype(return_type, pt, target));
1148pub const Function = extern struct {
1149 /// Index into `Wasm.mir_instructions`.
1150 mir_off: u32,
1151 /// This is unused except for as a safety slice bound and could be removed.
1152 mir_len: u32,
1153 /// Index into `Wasm.mir_extra`.
1154 mir_extra_off: u32,
1155 /// This is unused except for as a safety slice bound and could be removed.
1156 mir_extra_len: u32,
1157 locals_off: u32,
1158 locals_len: u32,
1159 prologue: Prologue,
1160
1161 pub const Prologue = extern struct {
1162 flags: Flags,
1163 sp_local: u32,
1164 stack_size: u32,
1165 bottom_stack_local: u32,
1166
1167 pub const Flags = packed struct(u32) {
1168 stack_alignment: Alignment,
1169 padding: u26 = 0,
1170 };
1171
1172 pub const none: Prologue = .{
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;
11701181 }
1171 } else if (return_type.isError(zcu)) {
1172 try returns.append(.i32);
1173 }
1174
1175 // param types
1176 for (params) |param_type_ip| {
1177 const param_type = Type.fromInterned(param_type_ip);
1178 if (!param_type.hasRuntimeBitsIgnoreComptime(zcu)) continue;
1179
1180 switch (cc) {
1181 .wasm_watc => {
1182 const param_classes = abi.classifyType(param_type, zcu);
1183 if (param_classes[1] == .none) {
1184 if (param_classes[0] == .direct) {
1185 const scalar_type = abi.scalarType(param_type, zcu);
1186 try temp_params.append(typeToValtype(scalar_type, pt, target));
1187 } else {
1188 try temp_params.append(typeToValtype(param_type, pt, target));
1189 }
1190 } else {
1191 // i128/f128
1192 try temp_params.append(.i64);
1193 try temp_params.append(.i64);
1194 }
1195 },
1196 else => try temp_params.append(typeToValtype(param_type, pt, target)),
1182 };
1183
1184 pub fn lower(f: *Function, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) Allocator.Error!void {
1185 const gpa = wasm.base.comp.gpa;
1186
1187 // Write the locals in the prologue of the function body.
1188 const locals = wasm.all_zcu_locals.items[f.locals_off..][0..f.locals_len];
1189 try code.ensureUnusedCapacity(gpa, 5 + locals.len * 6 + 38);
1190
1191 std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(locals.len))) catch unreachable;
1192 for (locals) |local| {
1193 std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable;
1194 code.appendAssumeCapacity(@intFromEnum(local));
1195 }
1196
1197 // Stack management section of function prologue.
1198 const stack_alignment = f.prologue.flags.stack_alignment;
1199 if (stack_alignment.toByteUnits()) |align_bytes| {
1200 const sp_global: Wasm.GlobalIndex = .stack_pointer;
1201 // load stack pointer
1202 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get));
1203 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
1204 // store stack pointer so we can restore it when we return from the function
1205 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));
1206 leb.writeUleb128(code.fixedWriter(), f.prologue.sp_local) catch unreachable;
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;
11971227 }
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();
11981239 }
1240};
11991241
1200 return wasm.Type{
1201 .params = try temp_params.toOwnedSlice(),
1202 .returns = try returns.toOwnedSlice(),
1203 };
1204}
1242pub const Error = error{
1243 OutOfMemory,
1244 /// Compiler was asked to operate on a number larger than supported.
1245 Overflow,
1246 /// Indicates the error is already stored in Zcu `failed_codegen`.
1247 CodegenFail,
1248};
12051249
1206pub fn generate(
1207 bin_file: *link.File,
1250pub fn function(
1251 wasm: *Wasm,
12081252 pt: Zcu.PerThread,
1209 src_loc: Zcu.LazySrcLoc,
12101253 func_index: InternPool.Index,
12111254 air: Air,
12121255 liveness: Liveness,
1213 code: *std.ArrayList(u8),
1214 debug_output: link.File.DebugInfoOutput,
1215) codegen.CodeGenError!codegen.Result {
1256) Error!Function {
12161257 const zcu = pt.zcu;
12171258 const gpa = zcu.gpa;
1218 const func = zcu.funcInfo(func_index);
1219 const file_scope = zcu.navFileScope(func.owner_nav);
1259 const cg = zcu.funcInfo(func_index);
1260 const file_scope = zcu.navFileScope(cg.owner_nav);
12201261 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
12211272 var code_gen: CodeGen = .{
12221273 .gpa = gpa,
12231274 .pt = pt,
12241275 .air = air,
12251276 .liveness = liveness,
1226 .code = code,
1227 .owner_nav = func.owner_nav,
1228 .src_loc = src_loc,
1229 .err_msg = undefined,
1230 .locals = .{},
1277 .owner_nav = cg.owner_nav,
12311278 .target = target,
1232 .bin_file = bin_file.cast(.wasm).?,
1233 .debug_output = debug_output,
1279 .ptr_size = switch (target.cpu.arch) {
1280 .wasm32 => .wasm32,
1281 .wasm64 => .wasm64,
1282 else => unreachable,
1283 },
1284 .wasm = wasm,
12341285 .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),
12351294 };
12361295 defer code_gen.deinit();
12371296
1238 genFunc(&code_gen) catch |err| switch (err) {
1239 error.CodegenFail => return codegen.Result{ .fail = code_gen.err_msg },
1240 else => |e| return e,
1297 return functionInner(&code_gen, any_returns) catch |err| switch (err) {
1298 error.CodegenFail => return error.CodegenFail,
1299 else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}),
12411300 };
1242
1243 return codegen.Result.ok;
12441301}
12451302
1246fn genFunc(func: *CodeGen) InnerError!void {
1247 const pt = func.pt;
1248 const zcu = 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;
1303fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function {
1304 const wasm = cg.wasm;
1305 const zcu = cg.pt.zcu;
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, .{});
12651310 // clean up outer branch
12661311 defer {
1267 var outer_branch = func.branches.pop();
1268 outer_branch.deinit(func.gpa);
1269 assert(func.branches.items.len == 0); // missing branch merge
1312 var outer_branch = cg.branches.pop();
1313 outer_branch.deinit(cg.gpa);
1314 assert(cg.branches.items.len == 0); // missing branch merge
12701315 }
12711316 // Generate MIR for function body
1272 try func.genBody(func.air.getMainBody());
1317 try cg.genBody(cg.air.getMainBody());
12731318
12741319 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)
12751320 // 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) {
1277 const inst: Air.Inst.Index = @enumFromInt(func.air.instructions.len - 1);
1278 const last_inst_ty = func.typeOfIndex(inst);
1321 if (any_returns and cg.air.instructions.len > 0) {
1322 const inst: Air.Inst.Index = @enumFromInt(cg.air.instructions.len - 1);
1323 const last_inst_ty = cg.typeOfIndex(inst);
12791324 if (!last_inst_ty.hasRuntimeBitsIgnoreComptime(zcu) or last_inst_ty.isNoReturn(zcu)) {
1280 try func.addTag(.@"unreachable");
1325 try cg.addTag(.@"unreachable");
12811326 }
12821327 }
12831328 // End of function body
1284 try func.addTag(.end);
1285
1286 try func.addTag(.dbg_epilogue_begin);
1287
1288 // check if we have to initialize and allocate anything into the stack frame.
1289 // If so, create enough stack space and insert the instructions at the front of the list.
1290 if (func.initial_stack_value != .none) {
1291 var prologue = std.ArrayList(Mir.Inst).init(func.gpa);
1292 defer prologue.deinit();
1293
1294 const sp = @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym);
1295 // load stack pointer
1296 try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } });
1297 // store stack pointer so we can restore it when we return from the function
1298 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_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;
1329 try cg.addTag(.end);
1330 try cg.addTag(.dbg_epilogue_begin);
1331
1332 return .{
1333 .mir_off = start_mir_off,
1334 .mir_len = @intCast(wasm.mir_instructions.len - start_mir_off),
1335 .mir_extra_off = cg.start_mir_extra_off,
1336 .mir_extra_len = cg.extraLen(),
1337 .locals_off = cg.start_locals_off,
1338 .locals_len = @intCast(wasm.all_zcu_locals.items.len - cg.start_locals_off),
1339 .prologue = if (cg.initial_stack_value == .none) .none else .{
1340 .sp_local = cg.initial_stack_value.local.value,
1341 .flags = .{ .stack_alignment = cg.stack_alignment },
1342 .stack_size = cg.stack_size,
1343 .bottom_stack_local = cg.bottom_stack_value.local.value,
13451344 },
1346 else => |e| return e,
13471345 };
13481346}
13491347
13501348const CallWValues = struct {
13511349 args: []WValue,
13521350 return_value: WValue,
1351 local_index: u32,
13531352
13541353 fn deinit(values: *CallWValues, gpa: Allocator) void {
13551354 gpa.free(values.args);
......@@ -1357,28 +1356,33 @@ const CallWValues = struct {
13571356 }
13581357};
13591358
1360fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWValues {
1361 const pt = func.pt;
1362 const zcu = pt.zcu;
1359fn resolveCallingConventionValues(
1360 zcu: *const Zcu,
1361 fn_ty: Type,
1362 target: *const std.Target,
1363) Allocator.Error!CallWValues {
1364 const gpa = zcu.gpa;
13631365 const ip = &zcu.intern_pool;
13641366 const fn_info = zcu.typeToFunc(fn_ty).?;
13651367 const cc = fn_info.cc;
1368
13661369 var result: CallWValues = .{
13671370 .args = &.{},
13681371 .return_value = .none,
1372 .local_index = 0,
13691373 };
13701374 if (cc == .naked) return result;
13711375
1372 var args = std.ArrayList(WValue).init(func.gpa);
1376 var args = std.ArrayList(WValue).init(gpa);
13731377 defer args.deinit();
13741378
13751379 // Check if we store the result as a pointer to the stack rather than
13761380 // 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)) {
13781382 // the sret arg will be passed as first argument, therefore we
13791383 // set the `return_value` before allocating locals for regular args.
1380 result.return_value = .{ .local = .{ .value = func.local_index, .references = 1 } };
1381 func.local_index += 1;
1384 result.return_value = .{ .local = .{ .value = result.local_index, .references = 1 } };
1385 result.local_index += 1;
13821386 }
13831387
13841388 switch (cc) {
......@@ -1388,8 +1392,8 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV
13881392 continue;
13891393 }
13901394
1391 try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } });
1392 func.local_index += 1;
1395 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1396 result.local_index += 1;
13931397 }
13941398 },
13951399 .wasm_watc => {
......@@ -1397,23 +1401,28 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV
13971401 const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu);
13981402 for (ty_classes) |class| {
13991403 if (class == .none) continue;
1400 try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } });
1401 func.local_index += 1;
1404 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1405 result.local_index += 1;
14021406 }
14031407 }
14041408 },
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.
14061410 }
14071411 result.args = try args.toOwnedSlice();
14081412 return result;
14091413}
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 {
14121421 switch (cc) {
14131422 .@"inline" => unreachable,
1414 .auto => return isByRef(return_type, pt, target),
1423 .auto => return isByRef(return_type, zcu, target),
14151424 .wasm_watc => {
1416 const ty_classes = abi.classifyType(return_type, pt.zcu);
1425 const ty_classes = abi.classifyType(return_type, zcu);
14171426 if (ty_classes[0] == .indirect) return true;
14181427 if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true;
14191428 return false;
......@@ -1424,94 +1433,88 @@ fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu.
14241433
14251434/// Lowers a Zig type and its value based on a given calling convention to ensure
14261435/// 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 {
14281437 if (cc != .wasm_watc) {
1429 return func.lowerToStack(value);
1438 return cg.lowerToStack(value);
14301439 }
14311440
1432 const pt = func.pt;
1433 const zcu = pt.zcu;
1441 const zcu = cg.pt.zcu;
14341442 const ty_classes = abi.classifyType(ty, zcu);
14351443 assert(ty_classes[0] != .none);
14361444 switch (ty.zigTypeTag(zcu)) {
14371445 .@"struct", .@"union" => {
14381446 if (ty_classes[0] == .indirect) {
1439 return func.lowerToStack(value);
1447 return cg.lowerToStack(value);
14401448 }
14411449 assert(ty_classes[0] == .direct);
14421450 const scalar_type = abi.scalarType(ty, zcu);
14431451 switch (value) {
1444 .memory,
1445 .memory_offset,
1446 .stack_offset,
1447 => _ = try func.load(value, scalar_type, 0),
1452 .nav_ref, .stack_offset => _ = try cg.load(value, scalar_type, 0),
14481453 .dead => unreachable,
1449 else => try func.emitWValue(value),
1454 else => try cg.emitWValue(value),
14501455 }
14511456 },
14521457 .int, .float => {
14531458 if (ty_classes[1] == .none) {
1454 return func.lowerToStack(value);
1459 return cg.lowerToStack(value);
14551460 }
14561461 assert(ty_classes[0] == .direct and ty_classes[1] == .direct);
14571462 assert(ty.abiSize(zcu) == 16);
14581463 // in this case we have an integer or float that must be lowered as 2 i64's.
1459 try func.emitWValue(value);
1460 try func.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 });
1461 try func.emitWValue(value);
1462 try func.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 });
1464 try cg.emitWValue(value);
1465 try cg.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 });
1466 try cg.emitWValue(value);
1467 try cg.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 });
14631468 },
1464 else => return func.lowerToStack(value),
1469 else => return cg.lowerToStack(value),
14651470 }
14661471}
14671472
14681473/// Lowers a `WValue` to the stack. This means when the `value` results in
14691474/// `.stack_offset` we calculate the pointer of this offset and use that.
14701475/// 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 {
14721477 switch (value) {
14731478 .stack_offset => |offset| {
1474 try func.emitWValue(value);
1479 try cg.emitWValue(value);
14751480 if (offset.value > 0) {
1476 switch (func.arch()) {
1481 switch (cg.ptr_size) {
14771482 .wasm32 => {
1478 try func.addImm32(offset.value);
1479 try func.addTag(.i32_add);
1483 try cg.addImm32(offset.value);
1484 try cg.addTag(.i32_add);
14801485 },
14811486 .wasm64 => {
1482 try func.addImm64(offset.value);
1483 try func.addTag(.i64_add);
1487 try cg.addImm64(offset.value);
1488 try cg.addTag(.i64_add);
14841489 },
1485 else => unreachable,
14861490 }
14871491 }
14881492 },
1489 else => try func.emitWValue(value),
1493 else => try cg.emitWValue(value),
14901494 }
14911495}
14921496
14931497/// Creates a local for the initial stack value
14941498/// Asserts `initial_stack_value` is `.none`
1495fn initializeStack(func: *CodeGen) !void {
1496 assert(func.initial_stack_value == .none);
1499fn initializeStack(cg: *CodeGen) !void {
1500 assert(cg.initial_stack_value == .none);
14971501 // Reserve a local to store the current stack pointer
14981502 // We can later use this local to set the stack pointer back to the value
14991503 // we have stored here.
1500 func.initial_stack_value = try func.ensureAllocLocal(Type.usize);
1504 cg.initial_stack_value = try cg.ensureAllocLocal(Type.usize);
15011505 // 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);
15031507}
15041508
15051509/// Reads the stack pointer from `Context.initial_stack_value` and writes it
15061510/// to the global stack pointer variable
1507fn restoreStackPointer(func: *CodeGen) !void {
1511fn restoreStackPointer(cg: *CodeGen) !void {
15081512 // only restore the pointer if it was initialized
1509 if (func.initial_stack_value == .none) return;
1513 if (cg.initial_stack_value == .none) return;
15101514 // 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 pointer
1514 try func.addLabel(.global_set, @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym));
1517 try cg.addTag(.global_set_sp);
15151518}
15161519
15171520/// 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 {
15201523/// moveStack unless a local was already created to store the pointer.
15211524///
15221525/// Asserts Type has codegenbits
1523fn allocStack(func: *CodeGen, ty: Type) !WValue {
1524 const zcu = func.pt.zcu;
1526fn allocStack(cg: *CodeGen, ty: Type) !WValue {
1527 const pt = cg.pt;
1528 const zcu = pt.zcu;
15251529 assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
1526 if (func.initial_stack_value == .none) {
1527 try func.initializeStack();
1530 if (cg.initial_stack_value == .none) {
1531 try cg.initializeStack();
15281532 }
15291533
15301534 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", .{
1532 ty.fmt(func.pt), ty.abiSize(zcu),
1535 return cg.fail("Type {} with ABI size of {d} exceeds stack frame size", .{
1536 ty.fmt(pt), ty.abiSize(zcu),
15331537 });
15341538 };
15351539 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));
1540 defer func.stack_size = offset + abi_size;
1543 const offset: u32 = @intCast(abi_align.forward(cg.stack_size));
1544 defer cg.stack_size = offset + abi_size;
15411545
15421546 return .{ .stack_offset = .{ .value = offset, .references = 1 } };
15431547}
......@@ -1546,30 +1550,30 @@ fn allocStack(func: *CodeGen, ty: Type) !WValue {
15461550/// the value of its type will live.
15471551/// This is different from allocStack where this will use the pointer's alignment
15481552/// if it is set, to ensure the stack alignment will be set correctly.
1549fn allocStackPtr(func: *CodeGen, inst: Air.Inst.Index) !WValue {
1550 const pt = func.pt;
1553fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue {
1554 const pt = cg.pt;
15511555 const zcu = pt.zcu;
1552 const ptr_ty = func.typeOfIndex(inst);
1556 const ptr_ty = cg.typeOfIndex(inst);
15531557 const pointee_ty = ptr_ty.childType(zcu);
15541558
1555 if (func.initial_stack_value == .none) {
1556 try func.initializeStack();
1559 if (cg.initial_stack_value == .none) {
1560 try cg.initializeStack();
15571561 }
15581562
15591563 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.
15611565 }
15621566
15631567 const abi_alignment = ptr_ty.ptrAlignment(zcu);
15641568 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", .{
15661570 pointee_ty.fmt(pt), pointee_ty.abiSize(zcu),
15671571 });
15681572 };
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));
1572 defer func.stack_size = offset + abi_size;
1575 const offset: u32 = @intCast(abi_alignment.forward(cg.stack_size));
1576 defer cg.stack_size = offset + abi_size;
15731577
15741578 return .{ .stack_offset = .{ .value = offset, .references = 1 } };
15751579}
......@@ -1583,14 +1587,14 @@ fn toWasmBits(bits: u16) ?u16 {
15831587
15841588/// Performs a copy of bytes for a given type. Copying all bytes
15851589/// 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 {
15871591 // When bulk_memory is enabled, we lower it to wasm's memcpy instruction.
15881592 // If not, we lower it ourselves manually
1589 if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory)) {
1590 try func.lowerToStack(dst);
1591 try func.lowerToStack(src);
1592 try func.emitWValue(len);
1593 try func.addExtended(.memory_copy);
1593 if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory)) {
1594 try cg.lowerToStack(dst);
1595 try cg.lowerToStack(src);
1596 try cg.emitWValue(len);
1597 try cg.addExtended(.memory_copy);
15941598 return;
15951599 }
15961600
......@@ -1611,19 +1615,18 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
16111615 const rhs_base = src.offset();
16121616 while (offset < length) : (offset += 1) {
16131617 // get dst's address to store the result
1614 try func.emitWValue(dst);
1618 try cg.emitWValue(dst);
16151619 // load byte from src's address
1616 try func.emitWValue(src);
1617 switch (func.arch()) {
1620 try cg.emitWValue(src);
1621 switch (cg.ptr_size) {
16181622 .wasm32 => {
1619 try func.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });
1620 try func.addMemArg(.i32_store8, .{ .offset = lhs_base + offset, .alignment = 1 });
1623 try cg.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });
1624 try cg.addMemArg(.i32_store8, .{ .offset = lhs_base + offset, .alignment = 1 });
16211625 },
16221626 .wasm64 => {
1623 try func.addMemArg(.i64_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });
1624 try func.addMemArg(.i64_store8, .{ .offset = lhs_base + offset, .alignment = 1 });
1627 try cg.addMemArg(.i64_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });
1628 try cg.addMemArg(.i64_store8, .{ .offset = lhs_base + offset, .alignment = 1 });
16251629 },
1626 else => unreachable,
16271630 }
16281631 }
16291632 return;
......@@ -1633,94 +1636,84 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
16331636
16341637 // allocate a local for the offset, and set it to 0.
16351638 // This to ensure that inside loops we correctly re-set the counter.
1636 var offset = try func.allocLocal(Type.usize); // local for counter
1637 defer offset.free(func);
1638 switch (func.arch()) {
1639 .wasm32 => try func.addImm32(0),
1640 .wasm64 => try func.addImm64(0),
1641 else => unreachable,
1639 var offset = try cg.allocLocal(Type.usize); // local for counter
1640 defer offset.free(cg);
1641 switch (cg.ptr_size) {
1642 .wasm32 => try cg.addImm32(0),
1643 .wasm64 => try cg.addImm64(0),
16421644 }
1643 try func.addLabel(.local_set, offset.local.value);
1645 try cg.addLocal(.local_set, offset.local.value);
16441646
16451647 // outer block to jump to when loop is done
1646 try func.startBlock(.block, wasm.block_empty);
1647 try func.startBlock(.loop, wasm.block_empty);
1648 try cg.startBlock(.block, .empty);
1649 try cg.startBlock(.loop, .empty);
16481650
16491651 // loop condition (offset == length -> break)
16501652 {
1651 try func.emitWValue(offset);
1652 try func.emitWValue(len);
1653 switch (func.arch()) {
1654 .wasm32 => try func.addTag(.i32_eq),
1655 .wasm64 => try func.addTag(.i64_eq),
1656 else => unreachable,
1653 try cg.emitWValue(offset);
1654 try cg.emitWValue(len);
1655 switch (cg.ptr_size) {
1656 .wasm32 => try cg.addTag(.i32_eq),
1657 .wasm64 => try cg.addTag(.i64_eq),
16571658 }
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)
16591660 }
16601661
16611662 // get dst ptr
16621663 {
1663 try func.emitWValue(dst);
1664 try func.emitWValue(offset);
1665 switch (func.arch()) {
1666 .wasm32 => try func.addTag(.i32_add),
1667 .wasm64 => try func.addTag(.i64_add),
1668 else => unreachable,
1664 try cg.emitWValue(dst);
1665 try cg.emitWValue(offset);
1666 switch (cg.ptr_size) {
1667 .wasm32 => try cg.addTag(.i32_add),
1668 .wasm64 => try cg.addTag(.i64_add),
16691669 }
16701670 }
16711671
16721672 // get src value and also store in dst
16731673 {
1674 try func.emitWValue(src);
1675 try func.emitWValue(offset);
1676 switch (func.arch()) {
1674 try cg.emitWValue(src);
1675 try cg.emitWValue(offset);
1676 switch (cg.ptr_size) {
16771677 .wasm32 => {
1678 try func.addTag(.i32_add);
1679 try func.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1680 try func.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 });
1678 try cg.addTag(.i32_add);
1679 try cg.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1680 try cg.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 });
16811681 },
16821682 .wasm64 => {
1683 try func.addTag(.i64_add);
1684 try func.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1685 try func.addMemArg(.i64_store8, .{ .offset = dst.offset(), .alignment = 1 });
1683 try cg.addTag(.i64_add);
1684 try cg.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1685 try cg.addMemArg(.i64_store8, .{ .offset = dst.offset(), .alignment = 1 });
16861686 },
1687 else => unreachable,
16881687 }
16891688 }
16901689
16911690 // increment loop counter
16921691 {
1693 try func.emitWValue(offset);
1694 switch (func.arch()) {
1692 try cg.emitWValue(offset);
1693 switch (cg.ptr_size) {
16951694 .wasm32 => {
1696 try func.addImm32(1);
1697 try func.addTag(.i32_add);
1695 try cg.addImm32(1);
1696 try cg.addTag(.i32_add);
16981697 },
16991698 .wasm64 => {
1700 try func.addImm64(1);
1701 try func.addTag(.i64_add);
1699 try cg.addImm64(1);
1700 try cg.addTag(.i64_add);
17021701 },
1703 else => unreachable,
17041702 }
1705 try func.addLabel(.local_set, offset.local.value);
1706 try func.addLabel(.br, 0); // jump to start of loop
1703 try cg.addLocal(.local_set, offset.local.value);
1704 try cg.addLabel(.br, 0); // jump to start of loop
17071705 }
1708 try func.endBlock(); // close off loop block
1709 try func.endBlock(); // close off outer block
1706 try cg.endBlock(); // close off loop block
1707 try cg.endBlock(); // close off outer block
17101708}
17111709
1712fn ptrSize(func: *const CodeGen) u16 {
1713 return @divExact(func.target.ptrBitWidth(), 8);
1714}
1715
1716fn arch(func: *const CodeGen) std.Target.Cpu.Arch {
1717 return func.target.cpu.arch;
1710fn ptrSize(cg: *const CodeGen) u16 {
1711 return @divExact(cg.target.ptrBitWidth(), 8);
17181712}
17191713
17201714/// For a given `Type`, will return true when the type will be passed
17211715/// by reference, rather than by value
1722fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool {
1723 const zcu = pt.zcu;
1716fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool {
17241717 const ip = &zcu.intern_pool;
17251718 switch (ty.zigTypeTag(zcu)) {
17261719 .type,
......@@ -1753,14 +1746,14 @@ fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool {
17531746 },
17541747 .@"struct" => {
17551748 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);
17571750 }
17581751 return ty.hasRuntimeBitsIgnoreComptime(zcu);
17591752 },
17601753 .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled,
17611754 .int => return ty.intInfo(zcu).bits > 64,
17621755 .@"enum" => return ty.intInfo(zcu).bits > 64,
1763 .float => return ty.floatBits(target) > 64,
1756 .float => return ty.floatBits(target.*) > 64,
17641757 .error_union => {
17651758 const pl_ty = ty.errorUnionPayload(zcu);
17661759 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
......@@ -1791,8 +1784,8 @@ const SimdStoreStrategy = enum {
17911784/// This means when a given type is 128 bits and either the simd128 or relaxed-simd
17921785/// features are enabled, the function will return `.direct`. This would allow to store
17931786/// it using a instruction, rather than an unrolled version.
1794fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStoreStrategy {
1795 std.debug.assert(ty.zigTypeTag(zcu) == .vector);
1787pub fn determineSimdStoreStrategy(ty: Type, zcu: *const Zcu, target: *const std.Target) SimdStoreStrategy {
1788 assert(ty.zigTypeTag(zcu) == .vector);
17961789 if (ty.bitSize(zcu) != 128) return .unrolled;
17971790 const hasFeature = std.Target.wasm.featureSetHas;
17981791 const features = target.cpu.features;
......@@ -1806,215 +1799,214 @@ fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStore
18061799/// This can be used to get a pointer to a struct field, error payload, etc.
18071800/// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new
18081801/// 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 {
18101803 // do not perform arithmetic when offset is 0.
18111804 if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value;
18121805 const result_ptr: WValue = switch (action) {
1813 .new => try func.ensureAllocLocal(Type.usize),
1806 .new => try cg.ensureAllocLocal(Type.usize),
18141807 .modify => ptr_value,
18151808 };
1816 try func.emitWValue(ptr_value);
1809 try cg.emitWValue(ptr_value);
18171810 if (offset + ptr_value.offset() > 0) {
1818 switch (func.arch()) {
1811 switch (cg.ptr_size) {
18191812 .wasm32 => {
1820 try func.addImm32(@intCast(offset + ptr_value.offset()));
1821 try func.addTag(.i32_add);
1813 try cg.addImm32(@intCast(offset + ptr_value.offset()));
1814 try cg.addTag(.i32_add);
18221815 },
18231816 .wasm64 => {
1824 try func.addImm64(offset + ptr_value.offset());
1825 try func.addTag(.i64_add);
1817 try cg.addImm64(offset + ptr_value.offset());
1818 try cg.addTag(.i64_add);
18261819 },
1827 else => unreachable,
18281820 }
18291821 }
1830 try func.addLabel(.local_set, result_ptr.local.value);
1822 try cg.addLocal(.local_set, result_ptr.local.value);
18311823 return result_ptr;
18321824}
18331825
1834fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1835 const air_tags = func.air.instructions.items(.tag);
1826fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1827 const air_tags = cg.air.instructions.items(.tag);
18361828 return switch (air_tags[@intFromEnum(inst)]) {
18371829 .inferred_alloc, .inferred_alloc_comptime => unreachable,
18381830
1839 .add => func.airBinOp(inst, .add),
1840 .add_sat => func.airSatBinOp(inst, .add),
1841 .add_wrap => func.airWrapBinOp(inst, .add),
1842 .sub => func.airBinOp(inst, .sub),
1843 .sub_sat => func.airSatBinOp(inst, .sub),
1844 .sub_wrap => func.airWrapBinOp(inst, .sub),
1845 .mul => func.airBinOp(inst, .mul),
1846 .mul_sat => func.airSatMul(inst),
1847 .mul_wrap => func.airWrapBinOp(inst, .mul),
1848 .div_float, .div_exact => func.airDiv(inst),
1849 .div_trunc => func.airDivTrunc(inst),
1850 .div_floor => func.airDivFloor(inst),
1851 .bit_and => func.airBinOp(inst, .@"and"),
1852 .bit_or => func.airBinOp(inst, .@"or"),
1853 .bool_and => func.airBinOp(inst, .@"and"),
1854 .bool_or => func.airBinOp(inst, .@"or"),
1855 .rem => func.airRem(inst),
1856 .mod => func.airMod(inst),
1857 .shl => func.airWrapBinOp(inst, .shl),
1858 .shl_exact => func.airBinOp(inst, .shl),
1859 .shl_sat => func.airShlSat(inst),
1860 .shr, .shr_exact => func.airBinOp(inst, .shr),
1861 .xor => func.airBinOp(inst, .xor),
1862 .max => func.airMaxMin(inst, .max),
1863 .min => func.airMaxMin(inst, .min),
1864 .mul_add => func.airMulAdd(inst),
1865
1866 .sqrt => func.airUnaryFloatOp(inst, .sqrt),
1867 .sin => func.airUnaryFloatOp(inst, .sin),
1868 .cos => func.airUnaryFloatOp(inst, .cos),
1869 .tan => func.airUnaryFloatOp(inst, .tan),
1870 .exp => func.airUnaryFloatOp(inst, .exp),
1871 .exp2 => func.airUnaryFloatOp(inst, .exp2),
1872 .log => func.airUnaryFloatOp(inst, .log),
1873 .log2 => func.airUnaryFloatOp(inst, .log2),
1874 .log10 => func.airUnaryFloatOp(inst, .log10),
1875 .floor => func.airUnaryFloatOp(inst, .floor),
1876 .ceil => func.airUnaryFloatOp(inst, .ceil),
1877 .round => func.airUnaryFloatOp(inst, .round),
1878 .trunc_float => func.airUnaryFloatOp(inst, .trunc),
1879 .neg => func.airUnaryFloatOp(inst, .neg),
1880
1881 .abs => func.airAbs(inst),
1882
1883 .add_with_overflow => func.airAddSubWithOverflow(inst, .add),
1884 .sub_with_overflow => func.airAddSubWithOverflow(inst, .sub),
1885 .shl_with_overflow => func.airShlWithOverflow(inst),
1886 .mul_with_overflow => func.airMulWithOverflow(inst),
1887
1888 .clz => func.airClz(inst),
1889 .ctz => func.airCtz(inst),
1890
1891 .cmp_eq => func.airCmp(inst, .eq),
1892 .cmp_gte => func.airCmp(inst, .gte),
1893 .cmp_gt => func.airCmp(inst, .gt),
1894 .cmp_lte => func.airCmp(inst, .lte),
1895 .cmp_lt => func.airCmp(inst, .lt),
1896 .cmp_neq => func.airCmp(inst, .neq),
1897
1898 .cmp_vector => func.airCmpVector(inst),
1899 .cmp_lt_errors_len => func.airCmpLtErrorsLen(inst),
1900
1901 .array_elem_val => func.airArrayElemVal(inst),
1902 .array_to_slice => func.airArrayToSlice(inst),
1903 .alloc => func.airAlloc(inst),
1904 .arg => func.airArg(inst),
1905 .bitcast => func.airBitcast(inst),
1906 .block => func.airBlock(inst),
1907 .trap => func.airTrap(inst),
1908 .breakpoint => func.airBreakpoint(inst),
1909 .br => func.airBr(inst),
1910 .repeat => func.airRepeat(inst),
1911 .switch_dispatch => return func.fail("TODO implement `switch_dispatch`", .{}),
1912 .int_from_bool => func.airIntFromBool(inst),
1913 .cond_br => func.airCondBr(inst),
1914 .intcast => func.airIntcast(inst),
1915 .fptrunc => func.airFptrunc(inst),
1916 .fpext => func.airFpext(inst),
1917 .int_from_float => func.airIntFromFloat(inst),
1918 .float_from_int => func.airFloatFromInt(inst),
1919 .get_union_tag => func.airGetUnionTag(inst),
1920
1921 .@"try" => func.airTry(inst),
1922 .try_cold => func.airTry(inst),
1923 .try_ptr => func.airTryPtr(inst),
1924 .try_ptr_cold => func.airTryPtr(inst),
1925
1926 .dbg_stmt => func.airDbgStmt(inst),
1927 .dbg_empty_stmt => try func.finishAir(inst, .none, &.{}),
1928 .dbg_inline_block => func.airDbgInlineBlock(inst),
1929 .dbg_var_ptr => func.airDbgVar(inst, .local_var, true),
1930 .dbg_var_val => func.airDbgVar(inst, .local_var, false),
1931 .dbg_arg_inline => func.airDbgVar(inst, .local_arg, false),
1932
1933 .call => func.airCall(inst, .auto),
1934 .call_always_tail => func.airCall(inst, .always_tail),
1935 .call_never_tail => func.airCall(inst, .never_tail),
1936 .call_never_inline => func.airCall(inst, .never_inline),
1937
1938 .is_err => func.airIsErr(inst, .i32_ne),
1939 .is_non_err => func.airIsErr(inst, .i32_eq),
1940
1941 .is_null => func.airIsNull(inst, .i32_eq, .value),
1942 .is_non_null => func.airIsNull(inst, .i32_ne, .value),
1943 .is_null_ptr => func.airIsNull(inst, .i32_eq, .ptr),
1944 .is_non_null_ptr => func.airIsNull(inst, .i32_ne, .ptr),
1945
1946 .load => func.airLoad(inst),
1947 .loop => func.airLoop(inst),
1948 .memset => func.airMemset(inst, false),
1949 .memset_safe => func.airMemset(inst, true),
1950 .not => func.airNot(inst),
1951 .optional_payload => func.airOptionalPayload(inst),
1952 .optional_payload_ptr => func.airOptionalPayloadPtr(inst),
1953 .optional_payload_ptr_set => func.airOptionalPayloadPtrSet(inst),
1954 .ptr_add => func.airPtrBinOp(inst, .add),
1955 .ptr_sub => func.airPtrBinOp(inst, .sub),
1956 .ptr_elem_ptr => func.airPtrElemPtr(inst),
1957 .ptr_elem_val => func.airPtrElemVal(inst),
1958 .int_from_ptr => func.airIntFromPtr(inst),
1959 .ret => func.airRet(inst),
1960 .ret_safe => func.airRet(inst), // TODO
1961 .ret_ptr => func.airRetPtr(inst),
1962 .ret_load => func.airRetLoad(inst),
1963 .splat => func.airSplat(inst),
1964 .select => func.airSelect(inst),
1965 .shuffle => func.airShuffle(inst),
1966 .reduce => func.airReduce(inst),
1967 .aggregate_init => func.airAggregateInit(inst),
1968 .union_init => func.airUnionInit(inst),
1969 .prefetch => func.airPrefetch(inst),
1970 .popcount => func.airPopcount(inst),
1971 .byte_swap => func.airByteSwap(inst),
1972 .bit_reverse => func.airBitReverse(inst),
1973
1974 .slice => func.airSlice(inst),
1975 .slice_len => func.airSliceLen(inst),
1976 .slice_elem_val => func.airSliceElemVal(inst),
1977 .slice_elem_ptr => func.airSliceElemPtr(inst),
1978 .slice_ptr => func.airSlicePtr(inst),
1979 .ptr_slice_len_ptr => func.airPtrSliceFieldPtr(inst, func.ptrSize()),
1980 .ptr_slice_ptr_ptr => func.airPtrSliceFieldPtr(inst, 0),
1981 .store => func.airStore(inst, false),
1982 .store_safe => func.airStore(inst, true),
1983
1984 .set_union_tag => func.airSetUnionTag(inst),
1985 .struct_field_ptr => func.airStructFieldPtr(inst),
1986 .struct_field_ptr_index_0 => func.airStructFieldPtrIndex(inst, 0),
1987 .struct_field_ptr_index_1 => func.airStructFieldPtrIndex(inst, 1),
1988 .struct_field_ptr_index_2 => func.airStructFieldPtrIndex(inst, 2),
1989 .struct_field_ptr_index_3 => func.airStructFieldPtrIndex(inst, 3),
1990 .struct_field_val => func.airStructFieldVal(inst),
1991 .field_parent_ptr => func.airFieldParentPtr(inst),
1992
1993 .switch_br => func.airSwitchBr(inst),
1994 .loop_switch_br => return func.fail("TODO implement `loop_switch_br`", .{}),
1995 .trunc => func.airTrunc(inst),
1996 .unreach => func.airUnreachable(inst),
1997
1998 .wrap_optional => func.airWrapOptional(inst),
1999 .unwrap_errunion_payload => func.airUnwrapErrUnionPayload(inst, false),
2000 .unwrap_errunion_payload_ptr => func.airUnwrapErrUnionPayload(inst, true),
2001 .unwrap_errunion_err => func.airUnwrapErrUnionError(inst, false),
2002 .unwrap_errunion_err_ptr => func.airUnwrapErrUnionError(inst, true),
2003 .wrap_errunion_payload => func.airWrapErrUnionPayload(inst),
2004 .wrap_errunion_err => func.airWrapErrUnionErr(inst),
2005 .errunion_payload_ptr_set => func.airErrUnionPayloadPtrSet(inst),
2006 .error_name => func.airErrorName(inst),
2007
2008 .wasm_memory_size => func.airWasmMemorySize(inst),
2009 .wasm_memory_grow => func.airWasmMemoryGrow(inst),
2010
2011 .memcpy => func.airMemcpy(inst),
2012
2013 .ret_addr => func.airRetAddr(inst),
2014 .tag_name => func.airTagName(inst),
2015
2016 .error_set_has_value => func.airErrorSetHasValue(inst),
2017 .frame_addr => func.airFrameAddress(inst),
1831 .add => cg.airBinOp(inst, .add),
1832 .add_sat => cg.airSatBinOp(inst, .add),
1833 .add_wrap => cg.airWrapBinOp(inst, .add),
1834 .sub => cg.airBinOp(inst, .sub),
1835 .sub_sat => cg.airSatBinOp(inst, .sub),
1836 .sub_wrap => cg.airWrapBinOp(inst, .sub),
1837 .mul => cg.airBinOp(inst, .mul),
1838 .mul_sat => cg.airSatMul(inst),
1839 .mul_wrap => cg.airWrapBinOp(inst, .mul),
1840 .div_float, .div_exact => cg.airDiv(inst),
1841 .div_trunc => cg.airDivTrunc(inst),
1842 .div_floor => cg.airDivFloor(inst),
1843 .bit_and => cg.airBinOp(inst, .@"and"),
1844 .bit_or => cg.airBinOp(inst, .@"or"),
1845 .bool_and => cg.airBinOp(inst, .@"and"),
1846 .bool_or => cg.airBinOp(inst, .@"or"),
1847 .rem => cg.airRem(inst),
1848 .mod => cg.airMod(inst),
1849 .shl => cg.airWrapBinOp(inst, .shl),
1850 .shl_exact => cg.airBinOp(inst, .shl),
1851 .shl_sat => cg.airShlSat(inst),
1852 .shr, .shr_exact => cg.airBinOp(inst, .shr),
1853 .xor => cg.airBinOp(inst, .xor),
1854 .max => cg.airMaxMin(inst, .fmax, .gt),
1855 .min => cg.airMaxMin(inst, .fmin, .lt),
1856 .mul_add => cg.airMulAdd(inst),
1857
1858 .sqrt => cg.airUnaryFloatOp(inst, .sqrt),
1859 .sin => cg.airUnaryFloatOp(inst, .sin),
1860 .cos => cg.airUnaryFloatOp(inst, .cos),
1861 .tan => cg.airUnaryFloatOp(inst, .tan),
1862 .exp => cg.airUnaryFloatOp(inst, .exp),
1863 .exp2 => cg.airUnaryFloatOp(inst, .exp2),
1864 .log => cg.airUnaryFloatOp(inst, .log),
1865 .log2 => cg.airUnaryFloatOp(inst, .log2),
1866 .log10 => cg.airUnaryFloatOp(inst, .log10),
1867 .floor => cg.airUnaryFloatOp(inst, .floor),
1868 .ceil => cg.airUnaryFloatOp(inst, .ceil),
1869 .round => cg.airUnaryFloatOp(inst, .round),
1870 .trunc_float => cg.airUnaryFloatOp(inst, .trunc),
1871 .neg => cg.airUnaryFloatOp(inst, .neg),
1872
1873 .abs => cg.airAbs(inst),
1874
1875 .add_with_overflow => cg.airAddSubWithOverflow(inst, .add),
1876 .sub_with_overflow => cg.airAddSubWithOverflow(inst, .sub),
1877 .shl_with_overflow => cg.airShlWithOverflow(inst),
1878 .mul_with_overflow => cg.airMulWithOverflow(inst),
1879
1880 .clz => cg.airClz(inst),
1881 .ctz => cg.airCtz(inst),
1882
1883 .cmp_eq => cg.airCmp(inst, .eq),
1884 .cmp_gte => cg.airCmp(inst, .gte),
1885 .cmp_gt => cg.airCmp(inst, .gt),
1886 .cmp_lte => cg.airCmp(inst, .lte),
1887 .cmp_lt => cg.airCmp(inst, .lt),
1888 .cmp_neq => cg.airCmp(inst, .neq),
1889
1890 .cmp_vector => cg.airCmpVector(inst),
1891 .cmp_lt_errors_len => cg.airCmpLtErrorsLen(inst),
1892
1893 .array_elem_val => cg.airArrayElemVal(inst),
1894 .array_to_slice => cg.airArrayToSlice(inst),
1895 .alloc => cg.airAlloc(inst),
1896 .arg => cg.airArg(inst),
1897 .bitcast => cg.airBitcast(inst),
1898 .block => cg.airBlock(inst),
1899 .trap => cg.airTrap(inst),
1900 .breakpoint => cg.airBreakpoint(inst),
1901 .br => cg.airBr(inst),
1902 .repeat => cg.airRepeat(inst),
1903 .switch_dispatch => return cg.fail("TODO implement `switch_dispatch`", .{}),
1904 .int_from_bool => cg.airIntFromBool(inst),
1905 .cond_br => cg.airCondBr(inst),
1906 .intcast => cg.airIntcast(inst),
1907 .fptrunc => cg.airFptrunc(inst),
1908 .fpext => cg.airFpext(inst),
1909 .int_from_float => cg.airIntFromFloat(inst),
1910 .float_from_int => cg.airFloatFromInt(inst),
1911 .get_union_tag => cg.airGetUnionTag(inst),
1912
1913 .@"try" => cg.airTry(inst),
1914 .try_cold => cg.airTry(inst),
1915 .try_ptr => cg.airTryPtr(inst),
1916 .try_ptr_cold => cg.airTryPtr(inst),
1917
1918 .dbg_stmt => cg.airDbgStmt(inst),
1919 .dbg_empty_stmt => try cg.finishAir(inst, .none, &.{}),
1920 .dbg_inline_block => cg.airDbgInlineBlock(inst),
1921 .dbg_var_ptr => cg.airDbgVar(inst, .local_var, true),
1922 .dbg_var_val => cg.airDbgVar(inst, .local_var, false),
1923 .dbg_arg_inline => cg.airDbgVar(inst, .local_arg, false),
1924
1925 .call => cg.airCall(inst, .auto),
1926 .call_always_tail => cg.airCall(inst, .always_tail),
1927 .call_never_tail => cg.airCall(inst, .never_tail),
1928 .call_never_inline => cg.airCall(inst, .never_inline),
1929
1930 .is_err => cg.airIsErr(inst, .i32_ne),
1931 .is_non_err => cg.airIsErr(inst, .i32_eq),
1932
1933 .is_null => cg.airIsNull(inst, .i32_eq, .value),
1934 .is_non_null => cg.airIsNull(inst, .i32_ne, .value),
1935 .is_null_ptr => cg.airIsNull(inst, .i32_eq, .ptr),
1936 .is_non_null_ptr => cg.airIsNull(inst, .i32_ne, .ptr),
1937
1938 .load => cg.airLoad(inst),
1939 .loop => cg.airLoop(inst),
1940 .memset => cg.airMemset(inst, false),
1941 .memset_safe => cg.airMemset(inst, true),
1942 .not => cg.airNot(inst),
1943 .optional_payload => cg.airOptionalPayload(inst),
1944 .optional_payload_ptr => cg.airOptionalPayloadPtr(inst),
1945 .optional_payload_ptr_set => cg.airOptionalPayloadPtrSet(inst),
1946 .ptr_add => cg.airPtrBinOp(inst, .add),
1947 .ptr_sub => cg.airPtrBinOp(inst, .sub),
1948 .ptr_elem_ptr => cg.airPtrElemPtr(inst),
1949 .ptr_elem_val => cg.airPtrElemVal(inst),
1950 .int_from_ptr => cg.airIntFromPtr(inst),
1951 .ret => cg.airRet(inst),
1952 .ret_safe => cg.airRet(inst), // TODO
1953 .ret_ptr => cg.airRetPtr(inst),
1954 .ret_load => cg.airRetLoad(inst),
1955 .splat => cg.airSplat(inst),
1956 .select => cg.airSelect(inst),
1957 .shuffle => cg.airShuffle(inst),
1958 .reduce => cg.airReduce(inst),
1959 .aggregate_init => cg.airAggregateInit(inst),
1960 .union_init => cg.airUnionInit(inst),
1961 .prefetch => cg.airPrefetch(inst),
1962 .popcount => cg.airPopcount(inst),
1963 .byte_swap => cg.airByteSwap(inst),
1964 .bit_reverse => cg.airBitReverse(inst),
1965
1966 .slice => cg.airSlice(inst),
1967 .slice_len => cg.airSliceLen(inst),
1968 .slice_elem_val => cg.airSliceElemVal(inst),
1969 .slice_elem_ptr => cg.airSliceElemPtr(inst),
1970 .slice_ptr => cg.airSlicePtr(inst),
1971 .ptr_slice_len_ptr => cg.airPtrSliceFieldPtr(inst, cg.ptrSize()),
1972 .ptr_slice_ptr_ptr => cg.airPtrSliceFieldPtr(inst, 0),
1973 .store => cg.airStore(inst, false),
1974 .store_safe => cg.airStore(inst, true),
1975
1976 .set_union_tag => cg.airSetUnionTag(inst),
1977 .struct_field_ptr => cg.airStructFieldPtr(inst),
1978 .struct_field_ptr_index_0 => cg.airStructFieldPtrIndex(inst, 0),
1979 .struct_field_ptr_index_1 => cg.airStructFieldPtrIndex(inst, 1),
1980 .struct_field_ptr_index_2 => cg.airStructFieldPtrIndex(inst, 2),
1981 .struct_field_ptr_index_3 => cg.airStructFieldPtrIndex(inst, 3),
1982 .struct_field_val => cg.airStructFieldVal(inst),
1983 .field_parent_ptr => cg.airFieldParentPtr(inst),
1984
1985 .switch_br => cg.airSwitchBr(inst),
1986 .loop_switch_br => return cg.fail("TODO implement `loop_switch_br`", .{}),
1987 .trunc => cg.airTrunc(inst),
1988 .unreach => cg.airUnreachable(inst),
1989
1990 .wrap_optional => cg.airWrapOptional(inst),
1991 .unwrap_errunion_payload => cg.airUnwrapErrUnionPayload(inst, false),
1992 .unwrap_errunion_payload_ptr => cg.airUnwrapErrUnionPayload(inst, true),
1993 .unwrap_errunion_err => cg.airUnwrapErrUnionError(inst, false),
1994 .unwrap_errunion_err_ptr => cg.airUnwrapErrUnionError(inst, true),
1995 .wrap_errunion_payload => cg.airWrapErrUnionPayload(inst),
1996 .wrap_errunion_err => cg.airWrapErrUnionErr(inst),
1997 .errunion_payload_ptr_set => cg.airErrUnionPayloadPtrSet(inst),
1998 .error_name => cg.airErrorName(inst),
1999
2000 .wasm_memory_size => cg.airWasmMemorySize(inst),
2001 .wasm_memory_grow => cg.airWasmMemoryGrow(inst),
2002
2003 .memcpy => cg.airMemcpy(inst),
2004
2005 .ret_addr => cg.airRetAddr(inst),
2006 .tag_name => cg.airTagName(inst),
2007
2008 .error_set_has_value => cg.airErrorSetHasValue(inst),
2009 .frame_addr => cg.airFrameAddress(inst),
20182010
20192011 .assembly,
20202012 .is_err_ptr,
......@@ -2030,18 +2022,18 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
20302022 .c_va_copy,
20312023 .c_va_end,
20322024 .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),
20362028 .atomic_store_unordered,
20372029 .atomic_store_monotonic,
20382030 .atomic_store_release,
20392031 .atomic_store_seq_cst,
20402032 // in WebAssembly, all atomic instructions are sequentially ordered.
2041 => func.airAtomicStore(inst),
2042 .atomic_rmw => func.airAtomicRmw(inst),
2043 .cmpxchg_weak => func.airCmpxchg(inst),
2044 .cmpxchg_strong => func.airCmpxchg(inst),
2033 => cg.airAtomicStore(inst),
2034 .atomic_rmw => cg.airAtomicRmw(inst),
2035 .cmpxchg_weak => cg.airCmpxchg(inst),
2036 .cmpxchg_strong => cg.airCmpxchg(inst),
20452037
20462038 .add_optimized,
20472039 .sub_optimized,
......@@ -2062,12 +2054,12 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
20622054 .cmp_vector_optimized,
20632055 .reduce_optimized,
20642056 .int_from_float_optimized,
2065 => return func.fail("TODO implement optimized float mode", .{}),
2057 => return cg.fail("TODO implement optimized float mode", .{}),
20662058
20672059 .add_safe,
20682060 .sub_safe,
20692061 .mul_safe,
2070 => return func.fail("TODO implement safety_checked_instructions", .{}),
2062 => return cg.fail("TODO implement safety_checked_instructions", .{}),
20712063
20722064 .work_item_id,
20732065 .work_group_size,
......@@ -2076,123 +2068,120 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
20762068 };
20772069}
20782070
2079fn genBody(func: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
2080 const pt = func.pt;
2081 const zcu = pt.zcu;
2071fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
2072 const zcu = cg.pt.zcu;
20822073 const ip = &zcu.intern_pool;
20832074
20842075 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)) {
20862077 continue;
20872078 }
2088 const old_bookkeeping_value = func.air_bookkeeping;
2089 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, Liveness.bpi);
2090 try func.genInst(inst);
2079 const old_bookkeeping_value = cg.air_bookkeeping;
2080 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, Liveness.bpi);
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) {
20932084 std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{}')", .{
20942085 inst,
2095 func.air.instructions.items(.tag)[@intFromEnum(inst)],
2086 cg.air.instructions.items(.tag)[@intFromEnum(inst)],
20962087 });
20972088 }
20982089 }
20992090}
21002091
2101fn airRet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2102 const pt = func.pt;
2103 const zcu = pt.zcu;
2104 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2105 const operand = try func.resolveInst(un_op);
2106 const fn_info = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?;
2092fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2093 const zcu = cg.pt.zcu;
2094 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2095 const operand = try cg.resolveInst(un_op);
2096 const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?;
21072097 const ret_ty = Type.fromInterned(fn_info.return_type);
21082098
21092099 // result must be stored in the stack and we return a pointer
21102100 // to the stack instead
2111 if (func.return_value != .none) {
2112 try func.store(func.return_value, operand, ret_ty, 0);
2101 if (cg.return_value != .none) {
2102 try cg.store(cg.return_value, operand, ret_ty, 0);
21132103 } else if (fn_info.cc == .wasm_watc and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
21142104 switch (ret_ty.zigTypeTag(zcu)) {
21152105 // Aggregate types can be lowered as a singular value
21162106 .@"struct", .@"union" => {
21172107 const scalar_type = abi.scalarType(ret_ty, zcu);
2118 try func.emitWValue(operand);
2108 try cg.emitWValue(operand);
21192109 const opcode = buildOpcode(.{
21202110 .op = .load,
21212111 .width = @as(u8, @intCast(scalar_type.abiSize(zcu) * 8)),
21222112 .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),
21242114 });
2125 try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
2115 try cg.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
21262116 .offset = operand.offset(),
21272117 .alignment = @intCast(scalar_type.abiAlignment(zcu).toByteUnits().?),
21282118 });
21292119 },
2130 else => try func.emitWValue(operand),
2120 else => try cg.emitWValue(operand),
21312121 }
21322122 } else {
21332123 if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and ret_ty.isError(zcu)) {
2134 try func.addImm32(0);
2124 try cg.addImm32(0);
21352125 } else {
2136 try func.emitWValue(operand);
2126 try cg.emitWValue(operand);
21372127 }
21382128 }
2139 try func.restoreStackPointer();
2140 try func.addTag(.@"return");
2129 try cg.restoreStackPointer();
2130 try cg.addTag(.@"return");
21412131
2142 return func.finishAir(inst, .none, &.{un_op});
2132 return cg.finishAir(inst, .none, &.{un_op});
21432133}
21442134
2145fn airRetPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2146 const pt = func.pt;
2147 const zcu = pt.zcu;
2148 const child_type = func.typeOfIndex(inst).childType(zcu);
2135fn airRetPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2136 const zcu = cg.pt.zcu;
2137 const child_type = cg.typeOfIndex(inst).childType(zcu);
21492138
21502139 const result = result: {
21512140 if (!child_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {
2152 break :result try func.allocStack(Type.usize); // create pointer to void
2141 break :result try cg.allocStack(Type.usize); // create pointer to void
21532142 }
21542143
2155 const fn_info = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?;
2156 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) {
2157 break :result func.return_value;
2144 const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?;
2145 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, cg.target)) {
2146 break :result cg.return_value;
21582147 }
21592148
2160 break :result try func.allocStackPtr(inst);
2149 break :result try cg.allocStackPtr(inst);
21612150 };
21622151
2163 return func.finishAir(inst, result, &.{});
2152 return cg.finishAir(inst, result, &.{});
21642153}
21652154
2166fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2167 const pt = func.pt;
2168 const zcu = pt.zcu;
2169 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2170 const operand = try func.resolveInst(un_op);
2171 const ret_ty = func.typeOf(un_op).childType(zcu);
2155fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2156 const zcu = cg.pt.zcu;
2157 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2158 const operand = try cg.resolveInst(un_op);
2159 const ret_ty = cg.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)).?;
21742162 if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
21752163 if (ret_ty.isError(zcu)) {
2176 try func.addImm32(0);
2164 try cg.addImm32(0);
21772165 }
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)) {
21792167 // leave on the stack
2180 _ = try func.load(operand, ret_ty, 0);
2168 _ = try cg.load(operand, ret_ty, 0);
21812169 }
21822170
2183 try func.restoreStackPointer();
2184 try func.addTag(.@"return");
2185 return func.finishAir(inst, .none, &.{un_op});
2171 try cg.restoreStackPointer();
2172 try cg.addTag(.@"return");
2173 return cg.finishAir(inst, .none, &.{un_op});
21862174}
21872175
2188fn airCall(func: *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", .{});
2190 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
2191 const extra = func.air.extraData(Air.Call, pl_op.payload);
2192 const args = @as([]const Air.Inst.Ref, @ptrCast(func.air.extra[extra.end..][0..extra.data.args_len]));
2193 const ty = func.typeOf(pl_op.operand);
2176fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {
2177 const wasm = cg.wasm;
2178 if (modifier == .always_tail) return cg.fail("TODO implement tail calls for wasm", .{});
2179 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
2180 const extra = cg.air.extraData(Air.Call, pl_op.payload);
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;
21962185 const zcu = pt.zcu;
21972186 const ip = &zcu.intern_pool;
21982187 const fn_ty = switch (ty.zigTypeTag(zcu)) {
......@@ -2202,142 +2191,109 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
22022191 };
22032192 const ret_ty = fn_ty.fnReturnType(zcu);
22042193 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
22072196 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
22102199 switch (ip.indexToKey(func_val.toIntern())) {
2211 .func => |function| {
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 },
2200 inline .func, .@"extern" => |x| break :blk x.owner_nav,
22382201 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
2239 .nav => |nav| {
2240 _ = try func.bin_file.getOrCreateAtomForNav(pt, nav);
2241 break :blk nav;
2242 },
2202 .nav => |nav| break :blk nav,
22432203 else => {},
22442204 },
22452205 else => {},
22462206 }
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", .{});
22482208 };
22492209
22502210 const sret: WValue = if (first_param_sret) blk: {
2251 const sret_local = try func.allocStack(ret_ty);
2252 try func.lowerToStack(sret_local);
2211 const sret_local = try cg.allocStack(ret_ty);
2212 try cg.lowerToStack(sret_local);
22532213 break :blk sret_local;
22542214 } else .none;
22552215
22562216 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);
22602220 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);
22632223 }
22642224
2265 if (callee) |direct| {
2266 const atom_index = func.bin_file.zig_object.?.navs.get(direct).?.atom;
2267 try func.addLabel(.call, @intFromEnum(func.bin_file.getAtom(atom_index).sym_index));
2225 if (callee) |nav_index| {
2226 try cg.addInst(.{ .tag = .call_nav, .data = .{ .nav_index = nav_index } });
22682227 } else {
22692228 // in this case we call a function pointer
22702229 // so load its value onto the stack
2271 std.debug.assert(ty.zigTypeTag(zcu) == .pointer);
2272 const operand = try func.resolveInst(pl_op.operand);
2273 try func.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);
2230 assert(ty.zigTypeTag(zcu) == .pointer);
2231 const operand = try cg.resolveInst(pl_op.operand);
2232 try cg.emitWValue(operand);
22772233
2278 const fn_type_index = try func.bin_file.zig_object.?.putOrGetFuncType(func.gpa, fn_type);
2279 try func.addLabel(.call_indirect, fn_type_index);
2234 const fn_type_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), cg.target);
2235 try cg.addFuncTy(.call_indirect, fn_type_index);
22802236 }
22812237
22822238 const result_value = result_value: {
22832239 if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) {
22842240 break :result_value .none;
22852241 } else if (ret_ty.isNoReturn(zcu)) {
2286 try func.addTag(.@"unreachable");
2242 try cg.addTag(.@"unreachable");
22872243 break :result_value .none;
22882244 } else if (first_param_sret) {
22892245 break :result_value sret;
22902246 // TODO: Make this less fragile and optimize
22912247 } 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);
2293 try func.addLabel(.local_set, result_local.local.value);
2248 const result_local = try cg.allocLocal(ret_ty);
2249 try cg.addLocal(.local_set, result_local.local.value);
22942250 const scalar_type = abi.scalarType(ret_ty, zcu);
2295 const result = try func.allocStack(scalar_type);
2296 try func.store(result, result_local, scalar_type, 0);
2251 const result = try cg.allocStack(scalar_type);
2252 try cg.store(result, result_local, scalar_type, 0);
22972253 break :result_value result;
22982254 } else {
2299 const result_local = try func.allocLocal(ret_ty);
2300 try func.addLabel(.local_set, result_local.local.value);
2255 const result_local = try cg.allocLocal(ret_ty);
2256 try cg.addLocal(.local_set, result_local.local.value);
23012257 break :result_value result_local;
23022258 }
23032259 };
23042260
2305 var bt = try func.iterateBigTomb(inst, 1 + args.len);
2261 var bt = try cg.iterateBigTomb(inst, 1 + args.len);
23062262 bt.feed(pl_op.operand);
23072263 for (args) |arg| bt.feed(arg);
23082264 return bt.finishAir(result_value);
23092265}
23102266
2311fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2312 const value = try func.allocStackPtr(inst);
2313 return func.finishAir(inst, value, &.{});
2267fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2268 const value = try cg.allocStackPtr(inst);
2269 return cg.finishAir(inst, value, &.{});
23142270}
23152271
2316fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
2317 const pt = func.pt;
2272fn airStore(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
2273 const pt = cg.pt;
23182274 const zcu = pt.zcu;
23192275 if (safety) {
23202276 // TODO if the value is undef, write 0xaa bytes to dest
23212277 } else {
23222278 // TODO if the value is undef, don't lower this instruction
23232279 }
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);
2327 const rhs = try func.resolveInst(bin_op.rhs);
2328 const ptr_ty = func.typeOf(bin_op.lhs);
2282 const lhs = try cg.resolveInst(bin_op.lhs);
2283 const rhs = try cg.resolveInst(bin_op.rhs);
2284 const ptr_ty = cg.typeOf(bin_op.lhs);
23292285 const ptr_info = ptr_ty.ptrInfo(zcu);
23302286 const ty = ptr_ty.childType(zcu);
23312287
23322288 if (ptr_info.packed_offset.host_size == 0) {
2333 try func.store(lhs, rhs, ty, 0);
2289 try cg.store(lhs, rhs, ty, 0);
23342290 } else {
23352291 // at this point we have a non-natural alignment, we must
23362292 // load the value, and then shift+or the rhs into the result location.
23372293 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.*)) {
2340 return func.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{});
2295 if (isByRef(int_elem_ty, zcu, cg.target)) {
2296 return cg.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{});
23412297 }
23422298
23432299 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
23562312 else
23572313 .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - ty.bitSize(zcu)) };
23582314
2359 try func.emitWValue(lhs);
2360 const loaded = try func.load(lhs, int_elem_ty, 0);
2361 const anded = try func.binOp(loaded, mask_val, int_elem_ty, .@"and");
2362 const extended_value = try func.intcast(rhs, ty, int_elem_ty);
2363 const masked_value = try func.binOp(extended_value, wrap_mask_val, int_elem_ty, .@"and");
2315 try cg.emitWValue(lhs);
2316 const loaded = try cg.load(lhs, int_elem_ty, 0);
2317 const anded = try cg.binOp(loaded, mask_val, int_elem_ty, .@"and");
2318 const extended_value = try cg.intcast(rhs, ty, int_elem_ty);
2319 const masked_value = try cg.binOp(extended_value, wrap_mask_val, int_elem_ty, .@"and");
23642320 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);
23662322 } 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");
23682324 // 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());
23702326 }
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 });
23732329}
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 {
23762332 assert(!(lhs != .stack and rhs == .stack));
2377 const pt = func.pt;
2333 const pt = cg.pt;
23782334 const zcu = pt.zcu;
23792335 const abi_size = ty.abiSize(zcu);
23802336 switch (ty.zigTypeTag(zcu)) {
23812337 .error_union => {
23822338 const pl_ty = ty.errorUnionPayload(zcu);
23832339 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2384 return func.store(lhs, rhs, Type.anyerror, 0);
2340 return cg.store(lhs, rhs, Type.anyerror, 0);
23852341 }
23862342
23872343 const len = @as(u32, @intCast(abi_size));
2388 return func.memcpy(lhs, rhs, .{ .imm32 = len });
2344 return cg.memcpy(lhs, rhs, .{ .imm32 = len });
23892345 },
23902346 .optional => {
23912347 if (ty.isPtrLikeOptional(zcu)) {
2392 return func.store(lhs, rhs, Type.usize, 0);
2348 return cg.store(lhs, rhs, Type.usize, 0);
23932349 }
23942350 const pl_ty = ty.optionalChild(zcu);
23952351 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2396 return func.store(lhs, rhs, Type.u8, 0);
2352 return cg.store(lhs, rhs, Type.u8, 0);
23972353 }
23982354 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);
24002356 }
24012357
24022358 const len = @as(u32, @intCast(abi_size));
2403 return func.memcpy(lhs, rhs, .{ .imm32 = len });
2359 return cg.memcpy(lhs, rhs, .{ .imm32 = len });
24042360 },
2405 .@"struct", .array, .@"union" => if (isByRef(ty, pt, func.target.*)) {
2361 .@"struct", .array, .@"union" => if (isByRef(ty, zcu, cg.target)) {
24062362 const len = @as(u32, @intCast(abi_size));
2407 return func.memcpy(lhs, rhs, .{ .imm32 = len });
2363 return cg.memcpy(lhs, rhs, .{ .imm32 = len });
24082364 },
2409 .vector => switch (determineSimdStoreStrategy(ty, zcu, func.target.*)) {
2365 .vector => switch (determineSimdStoreStrategy(ty, zcu, cg.target)) {
24102366 .unrolled => {
24112367 const len: u32 = @intCast(abi_size);
2412 return func.memcpy(lhs, rhs, .{ .imm32 = len });
2368 return cg.memcpy(lhs, rhs, .{ .imm32 = len });
24132369 },
24142370 .direct => {
2415 try func.emitWValue(lhs);
2416 try func.lowerToStack(rhs);
2371 try cg.emitWValue(lhs);
2372 try cg.lowerToStack(rhs);
24172373 // TODO: Add helper functions for simd opcodes
2418 const extra_index: u32 = @intCast(func.mir_extra.items.len);
2374 const extra_index = cg.extraLen();
24192375 // stores as := opcode, offset, alignment (opcode::memarg)
2420 try func.mir_extra.appendSlice(func.gpa, &[_]u32{
2421 std.wasm.simdOpcode(.v128_store),
2376 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
2377 @intFromEnum(std.wasm.SimdOpcode.v128_store),
24222378 offset + lhs.offset(),
24232379 @intCast(ty.abiAlignment(zcu).toByteUnits() orelse 0),
24242380 });
2425 return func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
2381 return cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
24262382 },
24272383 },
24282384 .pointer => {
24292385 if (ty.isSlice(zcu)) {
24302386 // store pointer first
24312387 // lower it to the stack so we do not have to store rhs into a local first
2432 try func.emitWValue(lhs);
2433 const ptr_local = try func.load(rhs, Type.usize, 0);
2434 try func.store(.stack, ptr_local, Type.usize, 0 + lhs.offset());
2388 try cg.emitWValue(lhs);
2389 const ptr_local = try cg.load(rhs, Type.usize, 0);
2390 try cg.store(.stack, ptr_local, Type.usize, 0 + lhs.offset());
24352391
24362392 // retrieve length from rhs, and store that alongside lhs as well
2437 try func.emitWValue(lhs);
2438 const len_local = try func.load(rhs, Type.usize, func.ptrSize());
2439 try func.store(.stack, len_local, Type.usize, func.ptrSize() + lhs.offset());
2393 try cg.emitWValue(lhs);
2394 const len_local = try cg.load(rhs, Type.usize, cg.ptrSize());
2395 try cg.store(.stack, len_local, Type.usize, cg.ptrSize() + lhs.offset());
24402396 return;
24412397 }
24422398 },
24432399 .int, .@"enum", .float => if (abi_size > 8 and abi_size <= 16) {
2444 try func.emitWValue(lhs);
2445 const lsb = try func.load(rhs, Type.u64, 0);
2446 try func.store(.stack, lsb, Type.u64, 0 + lhs.offset());
2400 try cg.emitWValue(lhs);
2401 const lsb = try cg.load(rhs, Type.u64, 0);
2402 try cg.store(.stack, lsb, Type.u64, 0 + lhs.offset());
24472403
2448 try func.emitWValue(lhs);
2449 const msb = try func.load(rhs, Type.u64, 8);
2450 try func.store(.stack, msb, Type.u64, 8 + lhs.offset());
2404 try cg.emitWValue(lhs);
2405 const msb = try cg.load(rhs, Type.u64, 8);
2406 try cg.store(.stack, msb, Type.u64, 8 + lhs.offset());
24512407 return;
24522408 } 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))) });
24542410 },
24552411 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}`", .{
24572413 ty.fmt(pt),
24582414 abi_size,
24592415 });
24602416 },
24612417 }
2462 try func.emitWValue(lhs);
2418 try cg.emitWValue(lhs);
24632419 // In this case we're actually interested in storing the stack position
24642420 // 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);
24682424 const opcode = buildOpcode(.{
24692425 .valtype1 = valtype,
24702426 .width = @as(u8, @intCast(abi_size * 8)),
......@@ -2472,7 +2428,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
24722428 });
24732429
24742430 // store rhs value at stack pointer's location in memory
2475 try func.addMemArg(
2431 try cg.addMemArg(
24762432 Mir.Inst.Tag.fromOpcode(opcode),
24772433 .{
24782434 .offset = offset + lhs.offset(),
......@@ -2481,26 +2437,26 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
24812437 );
24822438}
24832439
2484fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2485 const pt = func.pt;
2440fn airLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2441 const pt = cg.pt;
24862442 const zcu = pt.zcu;
2487 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2488 const operand = try func.resolveInst(ty_op.operand);
2443 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2444 const operand = try cg.resolveInst(ty_op.operand);
24892445 const ty = ty_op.ty.toType();
2490 const ptr_ty = func.typeOf(ty_op.operand);
2446 const ptr_ty = cg.typeOf(ty_op.operand);
24912447 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
24952451 const result = result: {
2496 if (isByRef(ty, pt, func.target.*)) {
2497 const new_local = try func.allocStack(ty);
2498 try func.store(new_local, operand, ty, 0);
2452 if (isByRef(ty, zcu, cg.target)) {
2453 const new_local = try cg.allocStack(ty);
2454 try cg.store(new_local, operand, ty, 0);
24992455 break :result new_local;
25002456 }
25012457
25022458 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);
25042460 }
25052461
25062462 // 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 {
25112467 else if (ptr_info.packed_offset.host_size <= 8)
25122468 .{ .imm64 = ptr_info.packed_offset.bit_offset }
25132469 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);
2517 const shifted = try func.binOp(stack_loaded, shift_val, int_elem_ty, .shr);
2518 break :result try func.trunc(shifted, ty, int_elem_ty);
2472 const stack_loaded = try cg.load(operand, int_elem_ty, 0);
2473 const shifted = try cg.binOp(stack_loaded, shift_val, int_elem_ty, .shr);
2474 break :result try cg.trunc(shifted, ty, int_elem_ty);
25192475 };
2520 return func.finishAir(inst, result, &.{ty_op.operand});
2476 return cg.finishAir(inst, result, &.{ty_op.operand});
25212477}
25222478
25232479/// Loads an operand from the linear memory section.
25242480/// NOTE: Leaves the value on the stack.
2525fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
2526 const pt = func.pt;
2527 const zcu = pt.zcu;
2481fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
2482 const zcu = cg.pt.zcu;
25282483 // load local's value from memory by its stack position
2529 try func.emitWValue(operand);
2484 try cg.emitWValue(operand);
25302485
25312486 if (ty.zigTypeTag(zcu) == .vector) {
25322487 // 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();
25342489 // stores as := opcode, offset, alignment (opcode::memarg)
2535 try func.mir_extra.appendSlice(func.gpa, &[_]u32{
2536 std.wasm.simdOpcode(.v128_load),
2490 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
2491 @intFromEnum(std.wasm.SimdOpcode.v128_load),
25372492 offset + operand.offset(),
25382493 @intCast(ty.abiAlignment(zcu).toByteUnits().?),
25392494 });
2540 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
2495 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
25412496 return .stack;
25422497 }
25432498
25442499 const abi_size: u8 = @intCast(ty.abiSize(zcu));
25452500 const opcode = buildOpcode(.{
2546 .valtype1 = typeToValtype(ty, pt, func.target.*),
2501 .valtype1 = typeToValtype(ty, zcu, cg.target),
25472502 .width = abi_size * 8,
25482503 .op = .load,
25492504 .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned,
25502505 });
25512506
2552 try func.addMemArg(
2507 try cg.addMemArg(
25532508 Mir.Inst.Tag.fromOpcode(opcode),
25542509 .{
25552510 .offset = offset + operand.offset(),
......@@ -2560,18 +2515,18 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
25602515 return .stack;
25612516}
25622517
2563fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2564 const pt = func.pt;
2518fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2519 const pt = cg.pt;
25652520 const zcu = pt.zcu;
2566 const arg_index = func.arg_index;
2567 const arg = func.args[arg_index];
2568 const cc = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?.cc;
2569 const arg_ty = func.typeOfIndex(inst);
2521 const arg_index = cg.arg_index;
2522 const arg = cg.args[arg_index];
2523 const cc = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?.cc;
2524 const arg_ty = cg.typeOfIndex(inst);
25702525 if (cc == .wasm_watc) {
25712526 const arg_classes = abi.classifyType(arg_ty, zcu);
25722527 for (arg_classes) |class| {
25732528 if (class != .none) {
2574 func.arg_index += 1;
2529 cg.arg_index += 1;
25752530 }
25762531 }
25772532
......@@ -2579,44 +2534,30 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
25792534 // we combine them into a single stack value
25802535 if (arg_classes[0] == .direct and arg_classes[1] == .direct) {
25812536 if (arg_ty.zigTypeTag(zcu) != .int and arg_ty.zigTypeTag(zcu) != .float) {
2582 return func.fail(
2537 return cg.fail(
25832538 "TODO: Implement C-ABI argument for type '{}'",
25842539 .{arg_ty.fmt(pt)},
25852540 );
25862541 }
2587 const result = try func.allocStack(arg_ty);
2588 try func.store(result, arg, Type.u64, 0);
2589 try func.store(result, func.args[arg_index + 1], Type.u64, 8);
2590 return func.finishAir(inst, result, &.{});
2542 const result = try cg.allocStack(arg_ty);
2543 try cg.store(result, arg, Type.u64, 0);
2544 try cg.store(result, cg.args[arg_index + 1], Type.u64, 8);
2545 return cg.finishAir(inst, result, &.{});
25912546 }
25922547 } else {
2593 func.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 => {},
2548 cg.arg_index += 1;
26072549 }
26082550
2609 return func.finishAir(inst, arg, &.{});
2551 return cg.finishAir(inst, arg, &.{});
26102552}
26112553
2612fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
2613 const pt = func.pt;
2614 const zcu = pt.zcu;
2615 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2616 const lhs = try func.resolveInst(bin_op.lhs);
2617 const rhs = try func.resolveInst(bin_op.rhs);
2618 const lhs_ty = func.typeOf(bin_op.lhs);
2619 const rhs_ty = func.typeOf(bin_op.rhs);
2554fn airBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
2555 const zcu = cg.pt.zcu;
2556 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2557 const lhs = try cg.resolveInst(bin_op.lhs);
2558 const rhs = try cg.resolveInst(bin_op.rhs);
2559 const lhs_ty = cg.typeOf(bin_op.lhs);
2560 const rhs_ty = cg.typeOf(bin_op.rhs);
26202561
26212562 // For certain operations, such as shifting, the types are different.
26222563 // 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 {
26262567 const result = switch (op) {
26272568 .shr, .shl => result: {
26282569 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)});
26302571 };
26312572 const rhs_wasm_bits = toWasmBits(@intCast(rhs_ty.bitSize(zcu))).?;
26322573 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)
26342575 else
26352576 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);
26372578 },
2638 else => try func.binOp(lhs, rhs, lhs_ty, op),
2579 else => try cg.binOp(lhs, rhs, lhs_ty, op),
26392580 };
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 });
26422583}
26432584
26442585/// Performs a binary operation on the given `WValue`'s
26452586/// NOTE: THis leaves the value on top of the stack.
2646fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2647 const pt = func.pt;
2587fn binOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2588 const pt = cg.pt;
26482589 const zcu = pt.zcu;
26492590 assert(!(lhs != .stack and rhs == .stack));
26502591
26512592 if (ty.isAnyFloat()) {
26522593 const float_op = FloatOp.fromOp(op);
2653 return func.floatOp(float_op, ty, &.{ lhs, rhs });
2594 return cg.floatOp(float_op, ty, &.{ lhs, rhs });
26542595 }
26552596
2656 if (isByRef(ty, pt, func.target.*)) {
2597 if (isByRef(ty, zcu, cg.target)) {
26572598 if (ty.zigTypeTag(zcu) == .int) {
2658 return func.binOpBigInt(lhs, rhs, ty, op);
2599 return cg.binOpBigInt(lhs, rhs, ty, op);
26592600 } else {
2660 return func.fail(
2601 return cg.fail(
26612602 "TODO: Implement binary operation for type: {}",
26622603 .{ty.fmt(pt)},
26632604 );
26642605 }
26652606 }
26662607
2667 const opcode: wasm.Opcode = buildOpcode(.{
2608 const opcode: std.wasm.Opcode = buildOpcode(.{
26682609 .op = op,
2669 .valtype1 = typeToValtype(ty, pt, func.target.*),
2610 .valtype1 = typeToValtype(ty, zcu, cg.target),
26702611 .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned,
26712612 });
2672 try func.emitWValue(lhs);
2673 try func.emitWValue(rhs);
2613 try cg.emitWValue(lhs);
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
26772618 return .stack;
26782619}
26792620
2680fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2681 const pt = func.pt;
2682 const zcu = pt.zcu;
2621fn binOpBigInt(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2622 const zcu = cg.pt.zcu;
26832623 const int_info = ty.intInfo(zcu);
26842624 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", .{});
26862626 }
26872627
26882628 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 }),
26902630 .div => switch (int_info.signedness) {
2691 .signed => return func.callIntrinsic("__divti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2692 .unsigned => return func.callIntrinsic("__udivti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2631 .signed => return cg.callIntrinsic(.__divti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2632 .unsigned => return cg.callIntrinsic(.__udivti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
26932633 },
26942634 .rem => switch (int_info.signedness) {
2695 .signed => return func.callIntrinsic("__modti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2696 .unsigned => return func.callIntrinsic("__umodti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2635 .signed => return cg.callIntrinsic(.__modti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2636 .unsigned => return cg.callIntrinsic(.__umodti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
26972637 },
26982638 .shr => switch (int_info.signedness) {
2699 .signed => return func.callIntrinsic("__ashrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2700 .unsigned => return func.callIntrinsic("__lshrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2639 .signed => return cg.callIntrinsic(.__ashrti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2640 .unsigned => return cg.callIntrinsic(.__lshrti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
27012641 },
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 }),
27032643 .@"and", .@"or", .xor => {
2704 const result = try func.allocStack(ty);
2705 try func.emitWValue(result);
2706 const lhs_lsb = try func.load(lhs, Type.u64, 0);
2707 const rhs_lsb = try func.load(rhs, Type.u64, 0);
2708 const op_lsb = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op);
2709 try func.store(.stack, op_lsb, Type.u64, result.offset());
2710
2711 try func.emitWValue(result);
2712 const lhs_msb = try func.load(lhs, Type.u64, 8);
2713 const rhs_msb = try func.load(rhs, Type.u64, 8);
2714 const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op);
2715 try func.store(.stack, op_msb, Type.u64, result.offset() + 8);
2644 const result = try cg.allocStack(ty);
2645 try cg.emitWValue(result);
2646 const lhs_lsb = try cg.load(lhs, Type.u64, 0);
2647 const rhs_lsb = try cg.load(rhs, Type.u64, 0);
2648 const op_lsb = try cg.binOp(lhs_lsb, rhs_lsb, Type.u64, op);
2649 try cg.store(.stack, op_lsb, Type.u64, result.offset());
2650
2651 try cg.emitWValue(result);
2652 const lhs_msb = try cg.load(lhs, Type.u64, 8);
2653 const rhs_msb = try cg.load(rhs, Type.u64, 8);
2654 const op_msb = try cg.binOp(lhs_msb, rhs_msb, Type.u64, op);
2655 try cg.store(.stack, op_msb, Type.u64, result.offset() + 8);
27162656 return result;
27172657 },
27182658 .add, .sub => {
2719 const result = try func.allocStack(ty);
2720 var lhs_lsb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
2721 defer lhs_lsb.free(func);
2722 var rhs_lsb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
2723 defer rhs_lsb.free(func);
2724 var op_lsb = try (try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op)).toLocal(func, Type.u64);
2725 defer op_lsb.free(func);
2726
2727 const lhs_msb = try func.load(lhs, Type.u64, 8);
2728 const rhs_msb = try func.load(rhs, Type.u64, 8);
2729 const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op);
2659 const result = try cg.allocStack(ty);
2660 var lhs_lsb = try (try cg.load(lhs, Type.u64, 0)).toLocal(cg, Type.u64);
2661 defer lhs_lsb.free(cg);
2662 var rhs_lsb = try (try cg.load(rhs, Type.u64, 0)).toLocal(cg, Type.u64);
2663 defer rhs_lsb.free(cg);
2664 var op_lsb = try (try cg.binOp(lhs_lsb, rhs_lsb, Type.u64, op)).toLocal(cg, Type.u64);
2665 defer op_lsb.free(cg);
2666
2667 const lhs_msb = try cg.load(lhs, Type.u64, 8);
2668 const rhs_msb = try cg.load(rhs, Type.u64, 8);
2669 const op_msb = try cg.binOp(lhs_msb, rhs_msb, Type.u64, op);
27302670
27312671 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);
27332673 } 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);
27352675 } else unreachable;
2736 const tmp = try func.intcast(lt, Type.u32, Type.u64);
2737 var tmp_op = try (try func.binOp(op_msb, tmp, Type.u64, op)).toLocal(func, Type.u64);
2738 defer tmp_op.free(func);
2676 const tmp = try cg.intcast(lt, Type.u32, Type.u64);
2677 var tmp_op = try (try cg.binOp(op_msb, tmp, Type.u64, op)).toLocal(cg, Type.u64);
2678 defer tmp_op.free(cg);
27392679
2740 try func.store(result, op_lsb, Type.u64, 0);
2741 try func.store(result, tmp_op, Type.u64, 8);
2680 try cg.store(result, op_lsb, Type.u64, 0);
2681 try cg.store(result, tmp_op, Type.u64, 8);
27422682 return result;
27432683 },
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)}),
27452685 }
27462686}
27472687
......@@ -2819,199 +2759,214 @@ const FloatOp = enum {
28192759 => null,
28202760 };
28212761 }
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 }
28222810};
28232811
2824fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2825 const pt = func.pt;
2812fn airAbs(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2813 const pt = cg.pt;
28262814 const zcu = pt.zcu;
2827 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2828 const operand = try func.resolveInst(ty_op.operand);
2829 const ty = func.typeOf(ty_op.operand);
2815 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2816 const operand = try cg.resolveInst(ty_op.operand);
2817 const ty = cg.typeOf(ty_op.operand);
28302818 const scalar_ty = ty.scalarType(zcu);
28312819
28322820 switch (scalar_ty.zigTypeTag(zcu)) {
28332821 .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)});
28352823 } else {
28362824 const int_bits = ty.intInfo(zcu).bits;
28372825 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});
28392827 };
28402828
28412829 switch (wasm_bits) {
28422830 32 => {
2843 try func.emitWValue(operand);
2831 try cg.emitWValue(operand);
28442832
2845 try func.addImm32(31);
2846 try func.addTag(.i32_shr_s);
2833 try cg.addImm32(31);
2834 try cg.addTag(.i32_shr_s);
28472835
2848 var tmp = try func.allocLocal(ty);
2849 defer tmp.free(func);
2850 try func.addLabel(.local_tee, tmp.local.value);
2836 var tmp = try cg.allocLocal(ty);
2837 defer tmp.free(cg);
2838 try cg.addLocal(.local_tee, tmp.local.value);
28512839
2852 try func.emitWValue(operand);
2853 try func.addTag(.i32_xor);
2854 try func.emitWValue(tmp);
2855 try func.addTag(.i32_sub);
2856 return func.finishAir(inst, .stack, &.{ty_op.operand});
2840 try cg.emitWValue(operand);
2841 try cg.addTag(.i32_xor);
2842 try cg.emitWValue(tmp);
2843 try cg.addTag(.i32_sub);
2844 return cg.finishAir(inst, .stack, &.{ty_op.operand});
28572845 },
28582846 64 => {
2859 try func.emitWValue(operand);
2847 try cg.emitWValue(operand);
28602848
2861 try func.addImm64(63);
2862 try func.addTag(.i64_shr_s);
2849 try cg.addImm64(63);
2850 try cg.addTag(.i64_shr_s);
28632851
2864 var tmp = try func.allocLocal(ty);
2865 defer tmp.free(func);
2866 try func.addLabel(.local_tee, tmp.local.value);
2852 var tmp = try cg.allocLocal(ty);
2853 defer tmp.free(cg);
2854 try cg.addLocal(.local_tee, tmp.local.value);
28672855
2868 try func.emitWValue(operand);
2869 try func.addTag(.i64_xor);
2870 try func.emitWValue(tmp);
2871 try func.addTag(.i64_sub);
2872 return func.finishAir(inst, .stack, &.{ty_op.operand});
2856 try cg.emitWValue(operand);
2857 try cg.addTag(.i64_xor);
2858 try cg.emitWValue(tmp);
2859 try cg.addTag(.i64_sub);
2860 return cg.finishAir(inst, .stack, &.{ty_op.operand});
28732861 },
28742862 128 => {
2875 const mask = try func.allocStack(Type.u128);
2876 try func.emitWValue(mask);
2877 try func.emitWValue(mask);
2863 const mask = try cg.allocStack(Type.u128);
2864 try cg.emitWValue(mask);
2865 try cg.emitWValue(mask);
28782866
2879 _ = try func.load(operand, Type.u64, 8);
2880 try func.addImm64(63);
2881 try func.addTag(.i64_shr_s);
2867 _ = try cg.load(operand, Type.u64, 8);
2868 try cg.addImm64(63);
2869 try cg.addTag(.i64_shr_s);
28822870
2883 var tmp = try func.allocLocal(Type.u64);
2884 defer tmp.free(func);
2885 try func.addLabel(.local_tee, tmp.local.value);
2886 try func.store(.stack, .stack, Type.u64, mask.offset() + 0);
2887 try func.emitWValue(tmp);
2888 try func.store(.stack, .stack, Type.u64, mask.offset() + 8);
2871 var tmp = try cg.allocLocal(Type.u64);
2872 defer tmp.free(cg);
2873 try cg.addLocal(.local_tee, tmp.local.value);
2874 try cg.store(.stack, .stack, Type.u64, mask.offset() + 0);
2875 try cg.emitWValue(tmp);
2876 try cg.store(.stack, .stack, Type.u64, mask.offset() + 8);
28892877
2890 const a = try func.binOpBigInt(operand, mask, Type.u128, .xor);
2891 const b = try func.binOpBigInt(a, mask, Type.u128, .sub);
2878 const a = try cg.binOpBigInt(operand, mask, Type.u128, .xor);
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});
28942882 },
28952883 else => unreachable,
28962884 }
28972885 },
28982886 .float => {
2899 const result = try func.floatOp(.fabs, ty, &.{operand});
2900 return func.finishAir(inst, result, &.{ty_op.operand});
2887 const result = try cg.floatOp(.fabs, ty, &.{operand});
2888 return cg.finishAir(inst, result, &.{ty_op.operand});
29012889 },
29022890 else => unreachable,
29032891 }
29042892}
29052893
2906fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void {
2907 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2908 const operand = try func.resolveInst(un_op);
2909 const ty = func.typeOf(un_op);
2894fn airUnaryFloatOp(cg: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void {
2895 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2896 const operand = try cg.resolveInst(un_op);
2897 const ty = cg.typeOf(un_op);
29102898
2911 const result = try func.floatOp(op, ty, &.{operand});
2912 return func.finishAir(inst, result, &.{un_op});
2899 const result = try cg.floatOp(op, ty, &.{operand});
2900 return cg.finishAir(inst, result, &.{un_op});
29132901}
29142902
2915fn floatOp(func: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) InnerError!WValue {
2916 const pt = func.pt;
2917 const zcu = pt.zcu;
2903fn floatOp(cg: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) InnerError!WValue {
2904 const zcu = cg.pt.zcu;
29182905 if (ty.zigTypeTag(zcu) == .vector) {
2919 return func.fail("TODO: Implement floatOps for vectors", .{});
2906 return cg.fail("TODO: Implement floatOps for vectors", .{});
29202907 }
29212908
2922 const float_bits = ty.floatBits(func.target.*);
2909 const float_bits = ty.floatBits(cg.target.*);
29232910
29242911 if (float_op == .neg) {
2925 return func.floatNeg(ty, args[0]);
2912 return cg.floatNeg(ty, args[0]);
29262913 }
29272914
29282915 if (float_bits == 32 or float_bits == 64) {
29292916 if (float_op.toOp()) |op| {
29302917 for (args) |operand| {
2931 try func.emitWValue(operand);
2918 try cg.emitWValue(operand);
29322919 }
2933 const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, pt, func.target.*) });
2934 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
2920 const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, zcu, cg.target) });
2921 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
29352922 return .stack;
29362923 }
29372924 }
29382925
2939 var fn_name_buf: [64]u8 = undefined;
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 };
2926 const intrinsic = float_op.intrinsic(float_bits);
29722927
29732928 // fma requires three operands
29742929 var param_types_buffer: [3]InternPool.Index = .{ ty.ip_index, ty.ip_index, ty.ip_index };
29752930 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);
29772932}
29782933
29792934/// NOTE: The result value remains on top of the stack.
2980fn floatNeg(func: *CodeGen, ty: Type, arg: WValue) InnerError!WValue {
2981 const float_bits = ty.floatBits(func.target.*);
2935fn floatNeg(cg: *CodeGen, ty: Type, arg: WValue) InnerError!WValue {
2936 const float_bits = ty.floatBits(cg.target.*);
29822937 switch (float_bits) {
29832938 16 => {
2984 try func.emitWValue(arg);
2985 try func.addImm32(0x8000);
2986 try func.addTag(.i32_xor);
2939 try cg.emitWValue(arg);
2940 try cg.addImm32(0x8000);
2941 try cg.addTag(.i32_xor);
29872942 return .stack;
29882943 },
29892944 32, 64 => {
2990 try func.emitWValue(arg);
2991 const val_type: wasm.Valtype = if (float_bits == 32) .f32 else .f64;
2945 try cg.emitWValue(arg);
2946 const val_type: std.wasm.Valtype = if (float_bits == 32) .f32 else .f64;
29922947 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));
29942949 return .stack;
29952950 },
29962951 80, 128 => {
2997 const result = try func.allocStack(ty);
2998 try func.emitWValue(result);
2999 try func.emitWValue(arg);
3000 try func.addMemArg(.i64_load, .{ .offset = 0 + arg.offset(), .alignment = 2 });
3001 try func.addMemArg(.i64_store, .{ .offset = 0 + result.offset(), .alignment = 2 });
2952 const result = try cg.allocStack(ty);
2953 try cg.emitWValue(result);
2954 try cg.emitWValue(arg);
2955 try cg.addMemArg(.i64_load, .{ .offset = 0 + arg.offset(), .alignment = 2 });
2956 try cg.addMemArg(.i64_store, .{ .offset = 0 + result.offset(), .alignment = 2 });
30022957
3003 try func.emitWValue(result);
3004 try func.emitWValue(arg);
3005 try func.addMemArg(.i64_load, .{ .offset = 8 + arg.offset(), .alignment = 2 });
2958 try cg.emitWValue(result);
2959 try cg.emitWValue(arg);
2960 try cg.addMemArg(.i64_load, .{ .offset = 8 + arg.offset(), .alignment = 2 });
30062961
30072962 if (float_bits == 80) {
3008 try func.addImm64(0x8000);
3009 try func.addTag(.i64_xor);
3010 try func.addMemArg(.i64_store16, .{ .offset = 8 + result.offset(), .alignment = 2 });
2963 try cg.addImm64(0x8000);
2964 try cg.addTag(.i64_xor);
2965 try cg.addMemArg(.i64_store16, .{ .offset = 8 + result.offset(), .alignment = 2 });
30112966 } else {
3012 try func.addImm64(0x8000000000000000);
3013 try func.addTag(.i64_xor);
3014 try func.addMemArg(.i64_store, .{ .offset = 8 + result.offset(), .alignment = 2 });
2967 try cg.addImm64(0x8000000000000000);
2968 try cg.addTag(.i64_xor);
2969 try cg.addMemArg(.i64_store, .{ .offset = 8 + result.offset(), .alignment = 2 });
30152970 }
30162971 return result;
30172972 },
......@@ -3019,18 +2974,17 @@ fn floatNeg(func: *CodeGen, ty: Type, arg: WValue) InnerError!WValue {
30192974 }
30202975}
30212976
3022fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
3023 const pt = func.pt;
3024 const zcu = pt.zcu;
3025 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2977fn airWrapBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
2978 const zcu = cg.pt.zcu;
2979 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
30262980
3027 const lhs = try func.resolveInst(bin_op.lhs);
3028 const rhs = try func.resolveInst(bin_op.rhs);
3029 const lhs_ty = func.typeOf(bin_op.lhs);
3030 const rhs_ty = func.typeOf(bin_op.rhs);
2981 const lhs = try cg.resolveInst(bin_op.lhs);
2982 const rhs = try cg.resolveInst(bin_op.rhs);
2983 const lhs_ty = cg.typeOf(bin_op.lhs);
2984 const rhs_ty = cg.typeOf(bin_op.rhs);
30312985
30322986 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", .{});
30342988 }
30352989
30362990 // 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 {
30412995 const result = switch (op) {
30422996 .shr, .shl => result: {
30432997 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)});
30452999 };
30463000 const rhs_wasm_bits = toWasmBits(@intCast(rhs_ty.bitSize(zcu))).?;
30473001 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)
30493003 else
30503004 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);
30523006 },
3053 else => try func.wrapBinOp(lhs, rhs, lhs_ty, op),
3007 else => try cg.wrapBinOp(lhs, rhs, lhs_ty, op),
30543008 };
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 });
30573011}
30583012
30593013/// Performs a wrapping binary operation.
30603014/// Asserts rhs is not a stack value when lhs also isn't.
30613015/// 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 {
3063 const bin_local = try func.binOp(lhs, rhs, ty, op);
3064 return func.wrapOperand(bin_local, ty);
3016fn wrapBinOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
3017 const bin_local = try cg.binOp(lhs, rhs, ty, op);
3018 return cg.wrapOperand(bin_local, ty);
30653019}
30663020
30673021/// Wraps an operand based on a given type's bitsize.
30683022/// Asserts `Type` is <= 128 bits.
30693023/// 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 {
3071 const pt = func.pt;
3072 const zcu = pt.zcu;
3024fn wrapOperand(cg: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
3025 const zcu = cg.pt.zcu;
30733026 assert(ty.abiSize(zcu) <= 16);
30743027 const int_bits: u16 = @intCast(ty.bitSize(zcu)); // TODO use ty.intInfo(zcu).bits
30753028 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});
30773030 };
30783031
30793032 if (wasm_bits == int_bits) return operand;
30803033
30813034 switch (wasm_bits) {
30823035 32 => {
3083 try func.emitWValue(operand);
3036 try cg.emitWValue(operand);
30843037 if (ty.isSignedInt(zcu)) {
3085 try func.addImm32(32 - int_bits);
3086 try func.addTag(.i32_shl);
3087 try func.addImm32(32 - int_bits);
3088 try func.addTag(.i32_shr_s);
3038 try cg.addImm32(32 - int_bits);
3039 try cg.addTag(.i32_shl);
3040 try cg.addImm32(32 - int_bits);
3041 try cg.addTag(.i32_shr_s);
30893042 } else {
3090 try func.addImm32(~@as(u32, 0) >> @intCast(32 - int_bits));
3091 try func.addTag(.i32_and);
3043 try cg.addImm32(~@as(u32, 0) >> @intCast(32 - int_bits));
3044 try cg.addTag(.i32_and);
30923045 }
30933046 return .stack;
30943047 },
30953048 64 => {
3096 try func.emitWValue(operand);
3049 try cg.emitWValue(operand);
30973050 if (ty.isSignedInt(zcu)) {
3098 try func.addImm64(64 - int_bits);
3099 try func.addTag(.i64_shl);
3100 try func.addImm64(64 - int_bits);
3101 try func.addTag(.i64_shr_s);
3051 try cg.addImm64(64 - int_bits);
3052 try cg.addTag(.i64_shl);
3053 try cg.addImm64(64 - int_bits);
3054 try cg.addTag(.i64_shr_s);
31023055 } else {
3103 try func.addImm64(~@as(u64, 0) >> @intCast(64 - int_bits));
3104 try func.addTag(.i64_and);
3056 try cg.addImm64(~@as(u64, 0) >> @intCast(64 - int_bits));
3057 try cg.addTag(.i64_and);
31053058 }
31063059 return .stack;
31073060 },
31083061 128 => {
31093062 assert(operand != .stack);
3110 const result = try func.allocStack(ty);
3063 const result = try cg.allocStack(ty);
31113064
3112 try func.emitWValue(result);
3113 _ = try func.load(operand, Type.u64, 0);
3114 try func.store(.stack, .stack, Type.u64, result.offset());
3065 try cg.emitWValue(result);
3066 _ = try cg.load(operand, Type.u64, 0);
3067 try cg.store(.stack, .stack, Type.u64, result.offset());
31153068
3116 try func.emitWValue(result);
3117 _ = try func.load(operand, Type.u64, 8);
3069 try cg.emitWValue(result);
3070 _ = try cg.load(operand, Type.u64, 8);
31183071 if (ty.isSignedInt(zcu)) {
3119 try func.addImm64(128 - int_bits);
3120 try func.addTag(.i64_shl);
3121 try func.addImm64(128 - int_bits);
3122 try func.addTag(.i64_shr_s);
3072 try cg.addImm64(128 - int_bits);
3073 try cg.addTag(.i64_shl);
3074 try cg.addImm64(128 - int_bits);
3075 try cg.addTag(.i64_shr_s);
31233076 } else {
3124 try func.addImm64(~@as(u64, 0) >> @intCast(128 - int_bits));
3125 try func.addTag(.i64_and);
3077 try cg.addImm64(~@as(u64, 0) >> @intCast(128 - int_bits));
3078 try cg.addTag(.i64_and);
31263079 }
3127 try func.store(.stack, .stack, Type.u64, result.offset() + 8);
3080 try cg.store(.stack, .stack, Type.u64, result.offset() + 8);
31283081
31293082 return result;
31303083 },
......@@ -3132,17 +3085,17 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
31323085 }
31333086}
31343087
3135fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerError!WValue {
3136 const pt = func.pt;
3088fn lowerPtr(cg: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerError!WValue {
3089 const pt = cg.pt;
31373090 const zcu = pt.zcu;
31383091 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;
31393092 const offset: u64 = prev_offset + ptr.byte_offset;
31403093 return switch (ptr.base_addr) {
3141 .nav => |nav| return func.lowerNavRef(nav, @intCast(offset)),
3142 .uav => |uav| return func.lowerUavRef(uav, @intCast(offset)),
3143 .int => return func.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize),
3144 .eu_payload => return func.fail("Wasm TODO: lower error union payload pointer", .{}),
3145 .opt_payload => |opt_ptr| return func.lowerPtr(opt_ptr, offset),
3094 .nav => |nav| return .{ .nav_ref = .{ .nav_index = nav, .offset = @intCast(offset) } },
3095 .uav => |uav| return .{ .uav_ref = .{ .ip_index = uav.val, .offset = @intCast(offset), .orig_ptr_ty = uav.orig_ty } },
3096 .int => return cg.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize),
3097 .eu_payload => return cg.fail("Wasm TODO: lower error union payload pointer", .{}),
3098 .opt_payload => |opt_ptr| return cg.lowerPtr(opt_ptr, offset),
31463099 .field => |field| {
31473100 const base_ptr = Value.fromInterned(field.base);
31483101 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
31513104 assert(base_ty.isSlice(zcu));
31523105 break :off switch (field.index) {
31533106 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),
31553108 else => unreachable,
31563109 };
31573110 },
......@@ -3177,70 +3130,19 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr
31773130 },
31783131 else => unreachable,
31793132 };
3180 return func.lowerPtr(field.base, offset + field_off);
3133 return cg.lowerPtr(field.base, offset + field_off);
31813134 },
31823135 .arr_elem, .comptime_field, .comptime_alloc => unreachable,
31833136 };
31843137}
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
32373139/// Asserts that `isByRef` returns `false` for `ty`.
3238fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3239 const pt = func.pt;
3140fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3141 const pt = cg.pt;
32403142 const zcu = pt.zcu;
3241 assert(!isByRef(ty, pt, func.target.*));
3143 assert(!isByRef(ty, zcu, cg.target));
32423144 const ip = &zcu.intern_pool;
3243 if (val.isUndefDeep(zcu)) return func.emitUndefined(ty);
3145 if (val.isUndefDeep(zcu)) return cg.emitUndefined(ty);
32443146
32453147 switch (ip.indexToKey(val.ip_index)) {
32463148 .int_type,
......@@ -3319,14 +3221,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
33193221 const payload_type = ty.errorUnionPayload(zcu);
33203222 if (!payload_type.hasRuntimeBitsIgnoreComptime(zcu)) {
33213223 // 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);
33233225 }
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", .{});
33263228 },
33273229 .enum_tag => |enum_tag| {
33283230 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));
33303232 },
33313233 .float => |float| switch (float.storage) {
33323234 .f16 => |f16_val| return .{ .imm32 = @as(u16, @bitCast(f16_val)) },
......@@ -3334,18 +3236,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
33343236 .f64 => |f64_val| return .{ .float64 = f64_val },
33353237 else => unreachable,
33363238 },
3337 .slice => switch (try func.bin_file.lowerUav(pt, val.toIntern(), .none, func.src_loc)) {
3338 .mcv => |mcv| return .{ .memory = mcv.load_symbol },
3339 .fail => |err_msg| {
3340 func.err_msg = err_msg;
3341 return error.CodegenFail;
3342 },
3343 },
3344 .ptr => return func.lowerPtr(val.toIntern(), 0),
3239 .slice => unreachable, // isByRef == true
3240 .ptr => return cg.lowerPtr(val.toIntern(), 0),
33453241 .opt => if (ty.optionalReprIsPayload(zcu)) {
33463242 const pl_ty = ty.optionalChild(zcu);
33473243 if (val.optionalValue(zcu)) |payload| {
3348 return func.lowerConstant(payload, pl_ty);
3244 return cg.lowerConstant(payload, pl_ty);
33493245 } else {
33503246 return .{ .imm32 = 0 };
33513247 }
......@@ -3353,12 +3249,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
33533249 return .{ .imm32 = @intFromBool(!val.isNull(zcu)) };
33543250 },
33553251 .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)}),
33573253 .vector_type => {
3358 assert(determineSimdStoreStrategy(ty, zcu, func.target.*) == .direct);
3254 assert(determineSimdStoreStrategy(ty, zcu, cg.target) == .direct);
33593255 var buf: [16]u8 = undefined;
33603256 val.writeToMemory(pt, &buf) catch unreachable;
3361 return func.storeSimdImmd(buf);
3257 return cg.storeSimdImmd(buf);
33623258 },
33633259 .struct_type => {
33643260 const struct_type = ip.loadStructType(ty.toIntern());
......@@ -3372,7 +3268,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
33723268 backing_int_ty,
33733269 mem.readInt(u64, &buf, .little),
33743270 );
3375 return func.lowerConstant(int_val, backing_int_ty);
3271 return cg.lowerConstant(int_val, backing_int_ty);
33763272 },
33773273 else => unreachable,
33783274 },
......@@ -3385,7 +3281,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
33853281 const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?;
33863282 break :field_ty Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
33873283 };
3388 return func.lowerConstant(Value.fromInterned(un.val), constant_ty);
3284 return cg.lowerConstant(Value.fromInterned(un.val), constant_ty);
33893285 },
33903286 .memoized_call => unreachable,
33913287 }
......@@ -3393,15 +3289,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
33933289
33943290/// Stores the value as a 128bit-immediate value by storing it inside
33953291/// the list and returning the index into this list as `WValue`.
3396fn storeSimdImmd(func: *CodeGen, value: [16]u8) !WValue {
3397 const index = @as(u32, @intCast(func.simd_immediates.items.len));
3398 try func.simd_immediates.append(func.gpa, value);
3292fn storeSimdImmd(cg: *CodeGen, value: [16]u8) !WValue {
3293 const index = @as(u32, @intCast(cg.simd_immediates.items.len));
3294 try cg.simd_immediates.append(cg.gpa, value);
33993295 return .{ .imm128 = index };
34003296}
34013297
3402fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {
3403 const pt = func.pt;
3404 const zcu = pt.zcu;
3298fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {
3299 const zcu = cg.pt.zcu;
34053300 const ip = &zcu.intern_pool;
34063301 switch (ty.zigTypeTag(zcu)) {
34073302 .bool, .error_set => return .{ .imm32 = 0xaaaaaaaa },
......@@ -3410,21 +3305,20 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {
34103305 33...64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa },
34113306 else => unreachable,
34123307 },
3413 .float => switch (ty.floatBits(func.target.*)) {
3308 .float => switch (ty.floatBits(cg.target.*)) {
34143309 16 => return .{ .imm32 = 0xaaaaaaaa },
34153310 32 => return .{ .float32 = @as(f32, @bitCast(@as(u32, 0xaaaaaaaa))) },
34163311 64 => return .{ .float64 = @as(f64, @bitCast(@as(u64, 0xaaaaaaaaaaaaaaaa))) },
34173312 else => unreachable,
34183313 },
3419 .pointer => switch (func.arch()) {
3314 .pointer => switch (cg.ptr_size) {
34203315 .wasm32 => return .{ .imm32 = 0xaaaaaaaa },
34213316 .wasm64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa },
3422 else => unreachable,
34233317 },
34243318 .optional => {
34253319 const pl_ty = ty.optionalChild(zcu);
34263320 if (ty.optionalReprIsPayload(zcu)) {
3427 return func.emitUndefined(pl_ty);
3321 return cg.emitUndefined(pl_ty);
34283322 }
34293323 return .{ .imm32 = 0xaaaaaaaa };
34303324 },
......@@ -3433,26 +3327,25 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {
34333327 },
34343328 .@"struct" => {
34353329 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)));
34373331 },
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)}),
34393333 }
34403334}
34413335
34423336/// Returns a `Value` as a signed 32 bit value.
34433337/// It's illegal to provide a value with a type that cannot be represented
34443338/// as an integer value.
3445fn valueAsI32(func: *const CodeGen, val: Value) i32 {
3446 const pt = func.pt;
3447 const zcu = pt.zcu;
3339fn valueAsI32(cg: *const CodeGen, val: Value) i32 {
3340 const zcu = cg.pt.zcu;
34483341 const ip = &zcu.intern_pool;
34493342
34503343 switch (val.toIntern()) {
34513344 .bool_true => return 1,
34523345 .bool_false => return 0,
34533346 else => return switch (ip.indexToKey(val.ip_index)) {
3454 .enum_tag => |enum_tag| intIndexAsI32(ip, enum_tag.int, pt),
3455 .int => |int| intStorageAsI32(int.storage, pt),
3347 .enum_tag => |enum_tag| intIndexAsI32(ip, enum_tag.int, zcu),
3348 .int => |int| intStorageAsI32(int.storage, zcu),
34563349 .ptr => |ptr| {
34573350 assert(ptr.base_addr == .int);
34583351 return @intCast(ptr.byte_offset);
......@@ -3463,12 +3356,11 @@ fn valueAsI32(func: *const CodeGen, val: Value) i32 {
34633356 }
34643357}
34653358
3466fn intIndexAsI32(ip: *const InternPool, int: InternPool.Index, pt: Zcu.PerThread) i32 {
3467 return intStorageAsI32(ip.indexToKey(int).int.storage, pt);
3359fn intIndexAsI32(ip: *const InternPool, int: InternPool.Index, zcu: *const Zcu) i32 {
3360 return intStorageAsI32(ip.indexToKey(int).int.storage, zcu);
34683361}
34693362
3470fn intStorageAsI32(storage: InternPool.Key.Int.Storage, pt: Zcu.PerThread) i32 {
3471 const zcu = pt.zcu;
3363fn intStorageAsI32(storage: InternPool.Key.Int.Storage, zcu: *const Zcu) i32 {
34723364 return switch (storage) {
34733365 .i64 => |x| @as(i32, @intCast(x)),
34743366 .u64 => |x| @as(i32, @bitCast(@as(u32, @intCast(x)))),
......@@ -3478,145 +3370,144 @@ fn intStorageAsI32(storage: InternPool.Key.Int.Storage, pt: Zcu.PerThread) i32 {
34783370 };
34793371}
34803372
3481fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3482 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3483 const extra = func.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]));
3373fn airBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3374 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3375 const extra = cg.air.extraData(Air.Block, ty_pl.payload);
3376 try cg.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len]));
34853377}
34863378
3487fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void {
3488 const pt = func.pt;
3489 const wasm_block_ty = genBlockType(block_ty, pt, func.target.*);
3379fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void {
3380 const zcu = cg.pt.zcu;
3381 const wasm_block_ty = genBlockType(block_ty, zcu, cg.target);
34903382
34913383 // 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: {
3493 const ty: Type = if (isByRef(block_ty, pt, func.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 overwritten
3384 const block_result: WValue = if (wasm_block_ty != .empty) blk: {
3385 const ty: Type = if (isByRef(block_ty, zcu, cg.target)) Type.u32 else block_ty;
3386 break :blk try cg.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten
34953387 } else .none;
34963388
3497 try func.startBlock(.block, wasm.block_empty);
3389 try cg.startBlock(.block, .empty);
34983390 // Here we set the current block idx, so breaks know the depth to jump
34993391 // to when breaking out.
3500 try func.blocks.putNoClobber(func.gpa, inst, .{
3501 .label = func.block_depth,
3392 try cg.blocks.putNoClobber(cg.gpa, inst, .{
3393 .label = cg.block_depth,
35023394 .value = block_result,
35033395 });
35043396
3505 try func.genBody(body);
3506 try func.endBlock();
3397 try cg.genBody(body);
3398 try cg.endBlock();
35073399
3508 const liveness = func.liveness.getBlock(inst);
3509 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths.len);
3400 const liveness = cg.liveness.getBlock(inst);
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, &.{});
35123404}
35133405
35143406/// 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 {
3516 func.block_depth += 1;
3517 try func.addInst(.{
3407fn startBlock(cg: *CodeGen, block_tag: std.wasm.Opcode, block_type: std.wasm.BlockType) !void {
3408 cg.block_depth += 1;
3409 try cg.addInst(.{
35183410 .tag = Mir.Inst.Tag.fromOpcode(block_tag),
3519 .data = .{ .block_type = valtype },
3411 .data = .{ .block_type = block_type },
35203412 });
35213413}
35223414
35233415/// Ends the current wasm block and decreases the `block_depth` by 1
3524fn endBlock(func: *CodeGen) !void {
3525 try func.addTag(.end);
3526 func.block_depth -= 1;
3416fn endBlock(cg: *CodeGen) !void {
3417 try cg.addTag(.end);
3418 cg.block_depth -= 1;
35273419}
35283420
3529fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3530 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3531 const loop = func.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]);
3421fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3422 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3423 const loop = cg.air.extraData(Air.Block, ty_pl.payload);
3424 const body: []const Air.Inst.Index = @ptrCast(cg.air.extra[loop.end..][0..loop.data.body_len]);
35333425
35343426 // result type of loop is always 'noreturn', meaning we can always
35353427 // 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);
3539 defer assert(func.loops.remove(inst));
3430 try cg.loops.putNoClobber(cg.gpa, inst, cg.block_depth);
3431 defer assert(cg.loops.remove(inst));
35403432
3541 try func.genBody(body);
3542 try func.endBlock();
3433 try cg.genBody(body);
3434 try cg.endBlock();
35433435
3544 return func.finishAir(inst, .none, &.{});
3436 return cg.finishAir(inst, .none, &.{});
35453437}
35463438
3547fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3548 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
3549 const condition = try func.resolveInst(pl_op.operand);
3550 const extra = func.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]);
3552 const else_body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]);
3553 const liveness_condbr = func.liveness.getCondBr(inst);
3439fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3440 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
3441 const condition = try cg.resolveInst(pl_op.operand);
3442 const extra = cg.air.extraData(Air.CondBr, pl_op.payload);
3443 const then_body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end..][0..extra.data.then_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]);
3445 const liveness_condbr = cg.liveness.getCondBr(inst);
35543446
35553447 // 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);
35573449 // emit the conditional value
3558 try func.emitWValue(condition);
3450 try cg.emitWValue(condition);
35593451
35603452 // we inserted the block in front of the condition
35613453 // so now check if condition matches. If not, break outside this block
35623454 // 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);
35663458 {
3567 func.branches.appendAssumeCapacity(.{});
3568 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @as(u32, @intCast(liveness_condbr.else_deaths.len)));
3459 cg.branches.appendAssumeCapacity(.{});
3460 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, @as(u32, @intCast(liveness_condbr.else_deaths.len)));
35693461 defer {
3570 var else_stack = func.branches.pop();
3571 else_stack.deinit(func.gpa);
3462 var else_stack = cg.branches.pop();
3463 else_stack.deinit(cg.gpa);
35723464 }
3573 try func.genBody(else_body);
3574 try func.endBlock();
3465 try cg.genBody(else_body);
3466 try cg.endBlock();
35753467 }
35763468
35773469 // Outer block that matches the condition
35783470 {
3579 func.branches.appendAssumeCapacity(.{});
3580 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @as(u32, @intCast(liveness_condbr.then_deaths.len)));
3471 cg.branches.appendAssumeCapacity(.{});
3472 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, @as(u32, @intCast(liveness_condbr.then_deaths.len)));
35813473 defer {
3582 var then_stack = func.branches.pop();
3583 then_stack.deinit(func.gpa);
3474 var then_stack = cg.branches.pop();
3475 then_stack.deinit(cg.gpa);
35843476 }
3585 try func.genBody(then_body);
3477 try cg.genBody(then_body);
35863478 }
35873479
3588 return func.finishAir(inst, .none, &.{});
3480 return cg.finishAir(inst, .none, &.{});
35893481}
35903482
3591fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {
3592 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3483fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {
3484 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
35933485
3594 const lhs = try func.resolveInst(bin_op.lhs);
3595 const rhs = try func.resolveInst(bin_op.rhs);
3596 const operand_ty = func.typeOf(bin_op.lhs);
3597 const result = try func.cmp(lhs, rhs, operand_ty, op);
3598 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
3486 const lhs = try cg.resolveInst(bin_op.lhs);
3487 const rhs = try cg.resolveInst(bin_op.rhs);
3488 const operand_ty = cg.typeOf(bin_op.lhs);
3489 const result = try cg.cmp(lhs, rhs, operand_ty, op);
3490 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
35993491}
36003492
36013493/// Compares two operands.
36023494/// Asserts rhs is not a stack value when the lhs isn't a stack value either
36033495/// 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 {
36053497 assert(!(lhs != .stack and rhs == .stack));
3606 const pt = func.pt;
3607 const zcu = pt.zcu;
3498 const zcu = cg.pt.zcu;
36083499 if (ty.zigTypeTag(zcu) == .optional and !ty.optionalReprIsPayload(zcu)) {
36093500 const payload_ty = ty.optionalChild(zcu);
36103501 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
36113502 // When we hit this case, we must check the value of optionals
36123503 // that are not pointers. This means first checking against non-null for
36133504 // 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);
36153506 }
36163507 } else if (ty.isAnyFloat()) {
3617 return func.cmpFloat(ty, lhs, rhs, op);
3618 } else if (isByRef(ty, pt, func.target.*)) {
3619 return func.cmpBigInt(lhs, rhs, ty, op);
3508 return cg.cmpFloat(ty, lhs, rhs, op);
3509 } else if (isByRef(ty, zcu, cg.target)) {
3510 return cg.cmpBigInt(lhs, rhs, ty, op);
36203511 }
36213512
36223513 const signedness: std.builtin.Signedness = blk: {
......@@ -3629,11 +3520,11 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO
36293520
36303521 // ensure that when we compare pointers, we emit
36313522 // the true pointer of a stack value, rather than the stack pointer.
3632 try func.lowerToStack(lhs);
3633 try func.lowerToStack(rhs);
3523 try cg.lowerToStack(lhs);
3524 try cg.lowerToStack(rhs);
36343525
3635 const opcode: wasm.Opcode = buildOpcode(.{
3636 .valtype1 = typeToValtype(ty, pt, func.target.*),
3526 const opcode: std.wasm.Opcode = buildOpcode(.{
3527 .valtype1 = typeToValtype(ty, zcu, cg.target),
36373528 .op = switch (op) {
36383529 .lt => .lt,
36393530 .lte => .le,
......@@ -3644,15 +3535,15 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO
36443535 },
36453536 .signedness = signedness,
36463537 });
3647 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3538 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
36483539
36493540 return .stack;
36503541}
36513542
36523543/// Compares two floats.
36533544/// 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 {
3655 const float_bits = ty.floatBits(func.target.*);
3545fn cmpFloat(cg: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math.CompareOperator) InnerError!WValue {
3546 const float_bits = ty.floatBits(cg.target.*);
36563547
36573548 const op: Op = switch (cmp_op) {
36583549 .lt => .lt,
......@@ -3665,143 +3556,137 @@ fn cmpFloat(func: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math
36653556
36663557 switch (float_bits) {
36673558 16 => {
3668 _ = try func.fpext(lhs, Type.f16, Type.f32);
3669 _ = try func.fpext(rhs, Type.f16, Type.f32);
3559 _ = try cg.fpext(lhs, Type.f16, Type.f32);
3560 _ = try cg.fpext(rhs, Type.f16, Type.f32);
36703561 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));
36723563 return .stack;
36733564 },
36743565 32, 64 => {
3675 try func.emitWValue(lhs);
3676 try func.emitWValue(rhs);
3677 const val_type: wasm.Valtype = if (float_bits == 32) .f32 else .f64;
3566 try cg.emitWValue(lhs);
3567 try cg.emitWValue(rhs);
3568 const val_type: std.wasm.Valtype = if (float_bits == 32) .f32 else .f64;
36783569 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));
36803571 return .stack;
36813572 },
36823573 80, 128 => {
3683 var fn_name_buf: [32]u8 = undefined;
3684 const fn_name = std.fmt.bufPrint(&fn_name_buf, "__{s}{s}f2", .{
3685 @tagName(op), target_util.compilerRtFloatAbbrev(float_bits),
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);
3574 const intrinsic = floatCmpIntrinsic(cmp_op, float_bits);
3575 const result = try cg.callIntrinsic(intrinsic, &.{ ty.ip_index, ty.ip_index }, Type.bool, &.{ lhs, rhs });
3576 return cg.cmp(result, .{ .imm32 = 0 }, Type.i32, cmp_op);
36903577 },
36913578 else => unreachable,
36923579 }
36933580}
36943581
3695fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3582fn airCmpVector(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
36963583 _ = inst;
3697 return func.fail("TODO implement airCmpVector for wasm", .{});
3584 return cg.fail("TODO implement airCmpVector for wasm", .{});
36983585}
36993586
3700fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3701 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3702 const operand = try func.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) };
3587fn airCmpLtErrorsLen(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3588 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3589 const operand = try cg.resolveInst(un_op);
37053590
3706 try func.emitWValue(operand);
3707 const pt = func.pt;
3591 try cg.emitWValue(operand);
3592 const pt = cg.pt;
37083593 const err_int_ty = try pt.errorIntType();
3709 const errors_len_val = try func.load(errors_len, err_int_ty, 0);
3710 const result = try func.cmp(.stack, errors_len_val, err_int_ty, .lt);
3594 try cg.addTag(.errors_len);
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});
37133598}
37143599
3715fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3716 const zcu = func.pt.zcu;
3717 const br = func.air.instructions.items(.data)[@intFromEnum(inst)].br;
3718 const block = func.blocks.get(br.block_inst).?;
3600fn airBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3601 const zcu = cg.pt.zcu;
3602 const br = cg.air.instructions.items(.data)[@intFromEnum(inst)].br;
3603 const block = cg.blocks.get(br.block_inst).?;
37193604
37203605 // if operand has codegen bits we should break with a value
3721 if (func.typeOf(br.operand).hasRuntimeBitsIgnoreComptime(zcu)) {
3722 const operand = try func.resolveInst(br.operand);
3723 try func.lowerToStack(operand);
3606 if (cg.typeOf(br.operand).hasRuntimeBitsIgnoreComptime(zcu)) {
3607 const operand = try cg.resolveInst(br.operand);
3608 try cg.lowerToStack(operand);
37243609
37253610 if (block.value != .none) {
3726 try func.addLabel(.local_set, block.value.local.value);
3611 try cg.addLocal(.local_set, block.value.local.value);
37273612 }
37283613 }
37293614
37303615 // We map every block to its block index.
37313616 // 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;
3733 try func.addLabel(.br, idx);
3617 const idx: u32 = cg.block_depth - block.label;
3618 try cg.addLabel(.br, idx);
37343619
3735 return func.finishAir(inst, .none, &.{br.operand});
3620 return cg.finishAir(inst, .none, &.{br.operand});
37363621}
37373622
3738fn airRepeat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3739 const repeat = func.air.instructions.items(.data)[@intFromEnum(inst)].repeat;
3740 const loop_label = func.loops.get(repeat.loop_inst).?;
3623fn airRepeat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3624 const repeat = cg.air.instructions.items(.data)[@intFromEnum(inst)].repeat;
3625 const loop_label = cg.loops.get(repeat.loop_inst).?;
37413626
3742 const idx: u32 = func.block_depth - loop_label;
3743 try func.addLabel(.br, idx);
3627 const idx: u32 = cg.block_depth - loop_label;
3628 try cg.addLabel(.br, idx);
37443629
3745 return func.finishAir(inst, .none, &.{});
3630 return cg.finishAir(inst, .none, &.{});
37463631}
37473632
3748fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3749 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3633fn airNot(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3634 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
37503635
3751 const operand = try func.resolveInst(ty_op.operand);
3752 const operand_ty = func.typeOf(ty_op.operand);
3753 const pt = func.pt;
3636 const operand = try cg.resolveInst(ty_op.operand);
3637 const operand_ty = cg.typeOf(ty_op.operand);
3638 const pt = cg.pt;
37543639 const zcu = pt.zcu;
37553640
37563641 const result = result: {
37573642 if (operand_ty.zigTypeTag(zcu) == .bool) {
3758 try func.emitWValue(operand);
3759 try func.addTag(.i32_eqz);
3760 const not_tmp = try func.allocLocal(operand_ty);
3761 try func.addLabel(.local_set, not_tmp.local.value);
3643 try cg.emitWValue(operand);
3644 try cg.addTag(.i32_eqz);
3645 const not_tmp = try cg.allocLocal(operand_ty);
3646 try cg.addLocal(.local_set, not_tmp.local.value);
37623647 break :result not_tmp;
37633648 } else {
37643649 const int_info = operand_ty.intInfo(zcu);
37653650 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)});
37673652 };
37683653
37693654 switch (wasm_bits) {
37703655 32 => {
3771 try func.emitWValue(operand);
3772 try func.addImm32(switch (int_info.signedness) {
3656 try cg.emitWValue(operand);
3657 try cg.addImm32(switch (int_info.signedness) {
37733658 .unsigned => ~@as(u32, 0) >> @intCast(32 - int_info.bits),
37743659 .signed => ~@as(u32, 0),
37753660 });
3776 try func.addTag(.i32_xor);
3661 try cg.addTag(.i32_xor);
37773662 break :result .stack;
37783663 },
37793664 64 => {
3780 try func.emitWValue(operand);
3781 try func.addImm64(switch (int_info.signedness) {
3665 try cg.emitWValue(operand);
3666 try cg.addImm64(switch (int_info.signedness) {
37823667 .unsigned => ~@as(u64, 0) >> @intCast(64 - int_info.bits),
37833668 .signed => ~@as(u64, 0),
37843669 });
3785 try func.addTag(.i64_xor);
3670 try cg.addTag(.i64_xor);
37863671 break :result .stack;
37873672 },
37883673 128 => {
3789 const ptr = try func.allocStack(operand_ty);
3674 const ptr = try cg.allocStack(operand_ty);
37903675
3791 try func.emitWValue(ptr);
3792 _ = try func.load(operand, Type.u64, 0);
3793 try func.addImm64(~@as(u64, 0));
3794 try func.addTag(.i64_xor);
3795 try func.store(.stack, .stack, Type.u64, ptr.offset());
3676 try cg.emitWValue(ptr);
3677 _ = try cg.load(operand, Type.u64, 0);
3678 try cg.addImm64(~@as(u64, 0));
3679 try cg.addTag(.i64_xor);
3680 try cg.store(.stack, .stack, Type.u64, ptr.offset());
37963681
3797 try func.emitWValue(ptr);
3798 _ = try func.load(operand, Type.u64, 8);
3799 try func.addImm64(switch (int_info.signedness) {
3682 try cg.emitWValue(ptr);
3683 _ = try cg.load(operand, Type.u64, 8);
3684 try cg.addImm64(switch (int_info.signedness) {
38003685 .unsigned => ~@as(u64, 0) >> @intCast(128 - int_info.bits),
38013686 .signed => ~@as(u64, 0),
38023687 });
3803 try func.addTag(.i64_xor);
3804 try func.store(.stack, .stack, Type.u64, ptr.offset() + 8);
3688 try cg.addTag(.i64_xor);
3689 try cg.store(.stack, .stack, Type.u64, ptr.offset() + 8);
38053690
38063691 break :result ptr;
38073692 },
......@@ -3809,33 +3694,32 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
38093694 }
38103695 }
38113696 };
3812 return func.finishAir(inst, result, &.{ty_op.operand});
3697 return cg.finishAir(inst, result, &.{ty_op.operand});
38133698}
38143699
3815fn airTrap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3816 try func.addTag(.@"unreachable");
3817 return func.finishAir(inst, .none, &.{});
3700fn airTrap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3701 try cg.addTag(.@"unreachable");
3702 return cg.finishAir(inst, .none, &.{});
38183703}
38193704
3820fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3705fn airBreakpoint(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
38213706 // unsupported by wasm itfunc. Can be implemented once we support DWARF
38223707 // for wasm
3823 try func.addTag(.@"unreachable");
3824 return func.finishAir(inst, .none, &.{});
3708 try cg.addTag(.@"unreachable");
3709 return cg.finishAir(inst, .none, &.{});
38253710}
38263711
3827fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3828 try func.addTag(.@"unreachable");
3829 return func.finishAir(inst, .none, &.{});
3712fn airUnreachable(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3713 try cg.addTag(.@"unreachable");
3714 return cg.finishAir(inst, .none, &.{});
38303715}
38313716
3832fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3833 const pt = func.pt;
3834 const zcu = pt.zcu;
3835 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3836 const operand = try func.resolveInst(ty_op.operand);
3837 const wanted_ty = func.typeOfIndex(inst);
3838 const given_ty = func.typeOf(ty_op.operand);
3717fn airBitcast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3718 const zcu = cg.pt.zcu;
3719 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3720 const operand = try cg.resolveInst(ty_op.operand);
3721 const wanted_ty = cg.typeOfIndex(inst);
3722 const given_ty = cg.typeOf(ty_op.operand);
38393723
38403724 const bit_size = given_ty.bitSize(zcu);
38413725 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 {
38433727
38443728 const result = result: {
38453729 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);
38473731 }
38483732
3849 if (isByRef(given_ty, pt, func.target.*) and !isByRef(wanted_ty, pt, func.target.*)) {
3850 const loaded_memory = try func.load(operand, wanted_ty, 0);
3733 if (isByRef(given_ty, zcu, cg.target) and !isByRef(wanted_ty, zcu, cg.target)) {
3734 const loaded_memory = try cg.load(operand, wanted_ty, 0);
38513735 if (needs_wrapping) {
3852 break :result try func.wrapOperand(loaded_memory, wanted_ty);
3736 break :result try cg.wrapOperand(loaded_memory, wanted_ty);
38533737 } else {
38543738 break :result loaded_memory;
38553739 }
38563740 }
3857 if (!isByRef(given_ty, pt, func.target.*) and isByRef(wanted_ty, pt, func.target.*)) {
3858 const stack_memory = try func.allocStack(wanted_ty);
3859 try func.store(stack_memory, operand, given_ty, 0);
3741 if (!isByRef(given_ty, zcu, cg.target) and isByRef(wanted_ty, zcu, cg.target)) {
3742 const stack_memory = try cg.allocStack(wanted_ty);
3743 try cg.store(stack_memory, operand, given_ty, 0);
38603744 if (needs_wrapping) {
3861 break :result try func.wrapOperand(stack_memory, wanted_ty);
3745 break :result try cg.wrapOperand(stack_memory, wanted_ty);
38623746 } else {
38633747 break :result stack_memory;
38643748 }
38653749 }
38663750
38673751 if (needs_wrapping) {
3868 break :result try func.wrapOperand(operand, wanted_ty);
3752 break :result try cg.wrapOperand(operand, wanted_ty);
38693753 }
38703754
3871 break :result func.reuseOperand(ty_op.operand, operand);
3755 break :result cg.reuseOperand(ty_op.operand, operand);
38723756 };
3873 return func.finishAir(inst, result, &.{ty_op.operand});
3757 return cg.finishAir(inst, result, &.{ty_op.operand});
38743758}
38753759
3876fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue {
3877 const pt = func.pt;
3878 const zcu = pt.zcu;
3760fn bitcast(cg: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue {
3761 const zcu = cg.pt.zcu;
38793762 // if we bitcast a float to or from an integer we must use the 'reinterpret' instruction
38803763 if (!(wanted_ty.isAnyFloat() or given_ty.isAnyFloat())) return operand;
38813764 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
38843767
38853768 const opcode = buildOpcode(.{
38863769 .op = .reinterpret,
3887 .valtype1 = typeToValtype(wanted_ty, pt, func.target.*),
3888 .valtype2 = typeToValtype(given_ty, pt, func.target.*),
3770 .valtype1 = typeToValtype(wanted_ty, zcu, cg.target),
3771 .valtype2 = typeToValtype(given_ty, zcu, cg.target),
38893772 });
3890 try func.emitWValue(operand);
3891 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3773 try cg.emitWValue(operand);
3774 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
38923775 return .stack;
38933776}
38943777
3895fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3896 const pt = func.pt;
3897 const zcu = pt.zcu;
3898 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3899 const extra = func.air.extraData(Air.StructField, ty_pl.payload);
3778fn airStructFieldPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3779 const zcu = cg.pt.zcu;
3780 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3781 const extra = cg.air.extraData(Air.StructField, ty_pl.payload);
39003782
3901 const struct_ptr = try func.resolveInst(extra.data.struct_operand);
3902 const struct_ptr_ty = func.typeOf(extra.data.struct_operand);
3783 const struct_ptr = try cg.resolveInst(extra.data.struct_operand);
3784 const struct_ptr_ty = cg.typeOf(extra.data.struct_operand);
39033785 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);
3905 return func.finishAir(inst, result, &.{extra.data.struct_operand});
3786 const result = try cg.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ptr_ty, struct_ty, extra.data.field_index);
3787 return cg.finishAir(inst, result, &.{extra.data.struct_operand});
39063788}
39073789
3908fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void {
3909 const pt = func.pt;
3910 const zcu = pt.zcu;
3911 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3912 const struct_ptr = try func.resolveInst(ty_op.operand);
3913 const struct_ptr_ty = func.typeOf(ty_op.operand);
3790fn airStructFieldPtrIndex(cg: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void {
3791 const zcu = cg.pt.zcu;
3792 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3793 const struct_ptr = try cg.resolveInst(ty_op.operand);
3794 const struct_ptr_ty = cg.typeOf(ty_op.operand);
39143795 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);
3917 return func.finishAir(inst, result, &.{ty_op.operand});
3797 const result = try cg.structFieldPtr(inst, ty_op.operand, struct_ptr, struct_ptr_ty, struct_ty, index);
3798 return cg.finishAir(inst, result, &.{ty_op.operand});
39183799}
39193800
39203801fn structFieldPtr(
3921 func: *CodeGen,
3802 cg: *CodeGen,
39223803 inst: Air.Inst.Index,
39233804 ref: Air.Inst.Ref,
39243805 struct_ptr: WValue,
......@@ -3926,9 +3807,9 @@ fn structFieldPtr(
39263807 struct_ty: Type,
39273808 index: u32,
39283809) InnerError!WValue {
3929 const pt = func.pt;
3810 const pt = cg.pt;
39303811 const zcu = pt.zcu;
3931 const result_ty = func.typeOfIndex(inst);
3812 const result_ty = cg.typeOfIndex(inst);
39323813 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu);
39333814
39343815 const offset = switch (struct_ty.containerLayout(zcu)) {
......@@ -3947,28 +3828,28 @@ fn structFieldPtr(
39473828 };
39483829 // save a load and store when we can simply reuse the operand
39493830 if (offset == 0) {
3950 return func.reuseOperand(ref, struct_ptr);
3831 return cg.reuseOperand(ref, struct_ptr);
39513832 }
39523833 switch (struct_ptr) {
39533834 .stack_offset => |stack_offset| {
39543835 return .{ .stack_offset = .{ .value = stack_offset.value + @as(u32, @intCast(offset)), .references = 1 } };
39553836 },
3956 else => return func.buildPointerOffset(struct_ptr, offset, .new),
3837 else => return cg.buildPointerOffset(struct_ptr, offset, .new),
39573838 }
39583839}
39593840
3960fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3961 const pt = func.pt;
3841fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3842 const pt = cg.pt;
39623843 const zcu = pt.zcu;
39633844 const ip = &zcu.intern_pool;
3964 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3965 const struct_field = func.air.extraData(Air.StructField, ty_pl.payload).data;
3845 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3846 const struct_field = cg.air.extraData(Air.StructField, ty_pl.payload).data;
39663847
3967 const struct_ty = func.typeOf(struct_field.struct_operand);
3968 const operand = try func.resolveInst(struct_field.struct_operand);
3848 const struct_ty = cg.typeOf(struct_field.struct_operand);
3849 const operand = try cg.resolveInst(struct_field.struct_operand);
39693850 const field_index = struct_field.field_index;
39703851 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
39733854 const result: WValue = switch (struct_ty.containerLayout(zcu)) {
39743855 .@"packed" => switch (struct_ty.zigTypeTag(zcu)) {
......@@ -3977,42 +3858,42 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
39773858 const offset = pt.structPackedFieldBitOffset(packed_struct, field_index);
39783859 const backing_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip));
39793860 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", .{});
39813862 };
39823863 const const_wvalue: WValue = if (wasm_bits == 32)
39833864 .{ .imm32 = offset }
39843865 else if (wasm_bits == 64)
39853866 .{ .imm64 = offset }
39863867 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
39893870 // for first field we don't require any shifting
39903871 const shifted_value = if (offset == 0)
39913872 operand
39923873 else
3993 try func.binOp(operand, const_wvalue, backing_ty, .shr);
3874 try cg.binOp(operand, const_wvalue, backing_ty, .shr);
39943875
39953876 if (field_ty.zigTypeTag(zcu) == .float) {
39963877 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);
3998 break :result try func.bitcast(field_ty, int_type, truncated);
3878 const truncated = try cg.trunc(shifted_value, int_type, backing_ty);
3879 break :result try cg.bitcast(field_ty, int_type, truncated);
39993880 } else if (field_ty.isPtrAtRuntime(zcu) and packed_struct.field_types.len == 1) {
40003881 // In this case we do not have to perform any transformations,
40013882 // 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);
40033884 } else if (field_ty.isPtrAtRuntime(zcu)) {
40043885 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);
40063887 }
4007 break :result try func.trunc(shifted_value, field_ty, backing_ty);
3888 break :result try cg.trunc(shifted_value, field_ty, backing_ty);
40083889 },
40093890 .@"union" => result: {
4010 if (isByRef(struct_ty, pt, func.target.*)) {
4011 if (!isByRef(field_ty, pt, func.target.*)) {
4012 break :result try func.load(operand, field_ty, 0);
3891 if (isByRef(struct_ty, zcu, cg.target)) {
3892 if (!isByRef(field_ty, zcu, cg.target)) {
3893 break :result try cg.load(operand, field_ty, 0);
40133894 } else {
4014 const new_stack_val = try func.allocStack(field_ty);
4015 try func.store(new_stack_val, operand, field_ty, 0);
3895 const new_stack_val = try cg.allocStack(field_ty);
3896 try cg.store(new_stack_val, operand, field_ty, 0);
40163897 break :result new_stack_val;
40173898 }
40183899 }
......@@ -4020,45 +3901,45 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
40203901 const union_int_type = try pt.intType(.unsigned, @as(u16, @intCast(struct_ty.bitSize(zcu))));
40213902 if (field_ty.zigTypeTag(zcu) == .float) {
40223903 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);
4024 break :result try func.bitcast(field_ty, int_type, truncated);
3904 const truncated = try cg.trunc(operand, int_type, union_int_type);
3905 break :result try cg.bitcast(field_ty, int_type, truncated);
40253906 } else if (field_ty.isPtrAtRuntime(zcu)) {
40263907 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);
40283909 }
4029 break :result try func.trunc(operand, field_ty, union_int_type);
3910 break :result try cg.trunc(operand, field_ty, union_int_type);
40303911 },
40313912 else => unreachable,
40323913 },
40333914 else => result: {
40343915 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)});
40363917 };
4037 if (isByRef(field_ty, pt, func.target.*)) {
3918 if (isByRef(field_ty, zcu, cg.target)) {
40383919 switch (operand) {
40393920 .stack_offset => |stack_offset| {
40403921 break :result .{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
40413922 },
4042 else => break :result try func.buildPointerOffset(operand, offset, .new),
3923 else => break :result try cg.buildPointerOffset(operand, offset, .new),
40433924 }
40443925 }
4045 break :result try func.load(operand, field_ty, offset);
3926 break :result try cg.load(operand, field_ty, offset);
40463927 },
40473928 };
40483929
4049 return func.finishAir(inst, result, &.{struct_field.struct_operand});
3930 return cg.finishAir(inst, result, &.{struct_field.struct_operand});
40503931}
40513932
4052fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4053 const pt = func.pt;
3933fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3934 const pt = cg.pt;
40543935 const zcu = pt.zcu;
40553936 // result type is always 'noreturn'
4056 const blocktype = wasm.block_empty;
4057 const switch_br = func.air.unwrapSwitch(inst);
4058 const target = try func.resolveInst(switch_br.operand);
4059 const target_ty = func.typeOf(switch_br.operand);
4060 const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.cases_len + 1);
4061 defer func.gpa.free(liveness.deaths);
3937 const blocktype: std.wasm.BlockType = .empty;
3938 const switch_br = cg.air.unwrapSwitch(inst);
3939 const target = try cg.resolveInst(switch_br.operand);
3940 const target_ty = cg.typeOf(switch_br.operand);
3941 const liveness = try cg.liveness.getSwitchBr(cg.gpa, inst, switch_br.cases_len + 1);
3942 defer cg.gpa.free(liveness.deaths);
40623943
40633944 // a list that maps each value with its value and body based on the order inside the list.
40643945 const CaseValue = union(enum) {
......@@ -4068,21 +3949,21 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
40683949 var case_list = try std.ArrayList(struct {
40693950 values: []const CaseValue,
40703951 body: []const Air.Inst.Index,
4071 }).initCapacity(func.gpa, switch_br.cases_len);
3952 }).initCapacity(cg.gpa, switch_br.cases_len);
40723953 defer for (case_list.items) |case| {
4073 func.gpa.free(case.values);
3954 cg.gpa.free(case.values);
40743955 } else case_list.deinit();
40753956
40763957 var lowest_maybe: ?i32 = null;
40773958 var highest_maybe: ?i32 = null;
40783959 var it = switch_br.iterateCases();
40793960 while (it.next()) |case| {
4080 const values = try func.gpa.alloc(CaseValue, case.items.len + case.ranges.len);
4081 errdefer func.gpa.free(values);
3961 const values = try cg.gpa.alloc(CaseValue, case.items.len + case.ranges.len);
3962 errdefer cg.gpa.free(values);
40823963
40833964 for (case.items, 0..) |ref, i| {
4084 const item_val = (try func.air.value(ref, pt)).?;
4085 const int_val = func.valueAsI32(item_val);
3965 const item_val = (try cg.air.value(ref, pt)).?;
3966 const int_val = cg.valueAsI32(item_val);
40863967 if (lowest_maybe == null or int_val < lowest_maybe.?) {
40873968 lowest_maybe = int_val;
40883969 }
......@@ -4093,15 +3974,15 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
40933974 }
40943975
40953976 for (case.ranges, 0..) |range, i| {
4096 const min_val = (try func.air.value(range[0], pt)).?;
4097 const int_min_val = func.valueAsI32(min_val);
3977 const min_val = (try cg.air.value(range[0], pt)).?;
3978 const int_min_val = cg.valueAsI32(min_val);
40983979
40993980 if (lowest_maybe == null or int_min_val < lowest_maybe.?) {
41003981 lowest_maybe = int_min_val;
41013982 }
41023983
4103 const max_val = (try func.air.value(range[1], pt)).?;
4104 const int_max_val = func.valueAsI32(max_val);
3984 const max_val = (try cg.air.value(range[1], pt)).?;
3985 const int_max_val = cg.valueAsI32(max_val);
41053986
41063987 if (highest_maybe == null or int_max_val > highest_maybe.?) {
41073988 highest_maybe = int_max_val;
......@@ -4116,7 +3997,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41163997 }
41173998
41183999 case_list.appendAssumeCapacity(.{ .values = values, .body = case.body });
4119 try func.startBlock(.block, blocktype);
4000 try cg.startBlock(.block, blocktype);
41204001 }
41214002
41224003 // 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 {
41324013 const else_body = it.elseBody();
41334014 const has_else_body = else_body.len != 0;
41344015 if (has_else_body) {
4135 try func.startBlock(.block, blocktype);
4016 try cg.startBlock(.block, blocktype);
41364017 }
41374018
41384019 if (!is_sparse) {
......@@ -4140,25 +4021,25 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41404021 // The value 'target' represents the index into the table.
41414022 // Each index in the table represents a label to the branch
41424023 // to jump to.
4143 try func.startBlock(.block, blocktype);
4144 try func.emitWValue(target);
4024 try cg.startBlock(.block, blocktype);
4025 try cg.emitWValue(target);
41454026 if (lowest < 0) {
41464027 // since br_table works using indexes, starting from '0', we must ensure all values
41474028 // we put inside, are atleast 0.
4148 try func.addImm32(@bitCast(lowest * -1));
4149 try func.addTag(.i32_add);
4029 try cg.addImm32(@bitCast(lowest * -1));
4030 try cg.addTag(.i32_add);
41504031 } else if (lowest > 0) {
41514032 // make the index start from 0 by substracting the lowest value
4152 try func.addImm32(@bitCast(lowest));
4153 try func.addTag(.i32_sub);
4033 try cg.addImm32(@bitCast(lowest));
4034 try cg.addTag(.i32_sub);
41544035 }
41554036
41564037 // Account for default branch so always add '1'
41574038 const depth = @as(u32, @intCast(highest - lowest + @intFromBool(has_else_body))) + 1;
41584039 const jump_table: Mir.JumpTable = .{ .length = depth };
4159 const table_extra_index = try func.addExtra(jump_table);
4160 try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
4161 try func.mir_extra.ensureUnusedCapacity(func.gpa, depth);
4040 const table_extra_index = try cg.addExtra(jump_table);
4041 try cg.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
4042 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, depth);
41624043 var value = lowest;
41634044 while (value <= highest) : (value += 1) {
41644045 // idx represents the branch we jump to
......@@ -4179,78 +4060,77 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41794060 // by using a jump table for this instead of if-else chains.
41804061 break :blk if (has_else_body or target_ty.zigTypeTag(zcu) == .error_set) switch_br.cases_len else unreachable;
41814062 };
4182 func.mir_extra.appendAssumeCapacity(idx);
4063 cg.mir_extra.appendAssumeCapacity(idx);
41834064 } else if (has_else_body) {
4184 func.mir_extra.appendAssumeCapacity(switch_br.cases_len); // default branch
4065 cg.mir_extra.appendAssumeCapacity(switch_br.cases_len); // default branch
41854066 }
4186 try func.endBlock();
4067 try cg.endBlock();
41874068 }
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));
41904071 for (case_list.items, 0..) |case, index| {
41914072 // when sparse, we use if/else-chain, so emit conditional checks
41924073 if (is_sparse) {
41934074 // for single value prong we can emit a simple condition
41944075 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);
41964077 // 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);
4198 try func.addLabel(.br_if, 0);
4078 _ = try cg.cmp(target, val, target_ty, .neq);
4079 try cg.addLabel(.br_if, 0);
41994080 } else {
42004081 // 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);
42024083 for (case.values) |value| {
42034084 switch (value) {
42044085 .singular => |single_val| {
4205 const val = try func.lowerConstant(single_val.value, target_ty);
4206 _ = try func.cmp(target, val, target_ty, .eq);
4086 const val = try cg.lowerConstant(single_val.value, target_ty);
4087 _ = try cg.cmp(target, val, target_ty, .eq);
42074088 },
42084089 .range => |range| {
4209 const min_val = try func.lowerConstant(range.min_value, target_ty);
4210 const max_val = try func.lowerConstant(range.max_value, target_ty);
4090 const min_val = try cg.lowerConstant(range.min_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);
4213 const lte = try func.cmp(target, max_val, target_ty, .lte);
4214 _ = try func.binOp(gte, lte, Type.bool, .@"and");
4093 const gte = try cg.cmp(target, min_val, target_ty, .gte);
4094 const lte = try cg.cmp(target, max_val, target_ty, .lte);
4095 _ = try cg.binOp(gte, lte, Type.bool, .@"and");
42154096 },
42164097 }
4217 try func.addLabel(.br_if, 0);
4098 try cg.addLabel(.br_if, 0);
42184099 }
42194100 // value did not match any of the prong values
4220 try func.addLabel(.br, 1);
4221 try func.endBlock();
4101 try cg.addLabel(.br, 1);
4102 try cg.endBlock();
42224103 }
42234104 }
4224 func.branches.appendAssumeCapacity(.{});
4225 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[index].len);
4105 cg.branches.appendAssumeCapacity(.{});
4106 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, liveness.deaths[index].len);
42264107 defer {
4227 var case_branch = func.branches.pop();
4228 case_branch.deinit(func.gpa);
4108 var case_branch = cg.branches.pop();
4109 case_branch.deinit(cg.gpa);
42294110 }
4230 try func.genBody(case.body);
4231 try func.endBlock();
4111 try cg.genBody(case.body);
4112 try cg.endBlock();
42324113 }
42334114
42344115 if (has_else_body) {
4235 func.branches.appendAssumeCapacity(.{});
4116 cg.branches.appendAssumeCapacity(.{});
42364117 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);
42384119 defer {
4239 var else_branch = func.branches.pop();
4240 else_branch.deinit(func.gpa);
4120 var else_branch = cg.branches.pop();
4121 else_branch.deinit(cg.gpa);
42414122 }
4242 try func.genBody(else_body);
4243 try func.endBlock();
4123 try cg.genBody(else_body);
4124 try cg.endBlock();
42444125 }
4245 return func.finishAir(inst, .none, &.{});
4126 return cg.finishAir(inst, .none, &.{});
42464127}
42474128
4248fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!void {
4249 const pt = func.pt;
4250 const zcu = pt.zcu;
4251 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4252 const operand = try func.resolveInst(un_op);
4253 const err_union_ty = func.typeOf(un_op);
4129fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerError!void {
4130 const zcu = cg.pt.zcu;
4131 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4132 const operand = try cg.resolveInst(un_op);
4133 const err_union_ty = cg.typeOf(un_op);
42544134 const pl_ty = err_union_ty.errorUnionPayload(zcu);
42554135
42564136 const result: WValue = result: {
......@@ -4262,57 +4142,55 @@ fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerErro
42624142 }
42634143 }
42644144
4265 try func.emitWValue(operand);
4145 try cg.emitWValue(operand);
42664146 if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4267 try func.addMemArg(.i32_load16_u, .{
4147 try cg.addMemArg(.i32_load16_u, .{
42684148 .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))),
42694149 .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),
42704150 });
42714151 }
42724152
42734153 // Compare the error value with '0'
4274 try func.addImm32(0);
4275 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
4154 try cg.addImm32(0);
4155 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
42764156 break :result .stack;
42774157 };
4278 return func.finishAir(inst, result, &.{un_op});
4158 return cg.finishAir(inst, result, &.{un_op});
42794159}
42804160
4281fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
4282 const pt = func.pt;
4283 const zcu = pt.zcu;
4284 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4161fn airUnwrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
4162 const zcu = cg.pt.zcu;
4163 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
42854164
4286 const operand = try func.resolveInst(ty_op.operand);
4287 const op_ty = func.typeOf(ty_op.operand);
4165 const operand = try cg.resolveInst(ty_op.operand);
4166 const op_ty = cg.typeOf(ty_op.operand);
42884167 const err_ty = if (op_is_ptr) op_ty.childType(zcu) else op_ty;
42894168 const payload_ty = err_ty.errorUnionPayload(zcu);
42904169
42914170 const result: WValue = result: {
42924171 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
42934172 if (op_is_ptr) {
4294 break :result func.reuseOperand(ty_op.operand, operand);
4173 break :result cg.reuseOperand(ty_op.operand, operand);
42954174 }
42964175 break :result .none;
42974176 }
42984177
42994178 const pl_offset = @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu)));
4300 if (op_is_ptr or isByRef(payload_ty, pt, func.target.*)) {
4301 break :result try func.buildPointerOffset(operand, pl_offset, .new);
4179 if (op_is_ptr or isByRef(payload_ty, zcu, cg.target)) {
4180 break :result try cg.buildPointerOffset(operand, pl_offset, .new);
43024181 }
43034182
4304 break :result try func.load(operand, payload_ty, pl_offset);
4183 break :result try cg.load(operand, payload_ty, pl_offset);
43054184 };
4306 return func.finishAir(inst, result, &.{ty_op.operand});
4185 return cg.finishAir(inst, result, &.{ty_op.operand});
43074186}
43084187
4309fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
4310 const pt = func.pt;
4311 const zcu = pt.zcu;
4312 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4188fn airUnwrapErrUnionError(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
4189 const zcu = cg.pt.zcu;
4190 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
43134191
4314 const operand = try func.resolveInst(ty_op.operand);
4315 const op_ty = func.typeOf(ty_op.operand);
4192 const operand = try cg.resolveInst(ty_op.operand);
4193 const op_ty = cg.typeOf(ty_op.operand);
43164194 const err_ty = if (op_is_ptr) op_ty.childType(zcu) else op_ty;
43174195 const payload_ty = err_ty.errorUnionPayload(zcu);
43184196
......@@ -4322,104 +4200,101 @@ fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool)
43224200 }
43234201
43244202 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);
43264204 }
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)));
43294207 };
4330 return func.finishAir(inst, result, &.{ty_op.operand});
4208 return cg.finishAir(inst, result, &.{ty_op.operand});
43314209}
43324210
4333fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4334 const zcu = func.pt.zcu;
4335 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4211fn airWrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4212 const zcu = cg.pt.zcu;
4213 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
43364214
4337 const operand = try func.resolveInst(ty_op.operand);
4338 const err_ty = func.typeOfIndex(inst);
4215 const operand = try cg.resolveInst(ty_op.operand);
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);
43414219 const result = result: {
43424220 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4343 break :result func.reuseOperand(ty_op.operand, operand);
4221 break :result cg.reuseOperand(ty_op.operand, operand);
43444222 }
43454223
4346 const err_union = try func.allocStack(err_ty);
4347 const payload_ptr = try func.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new);
4348 try func.store(payload_ptr, operand, pl_ty, 0);
4224 const err_union = try cg.allocStack(err_ty);
4225 const payload_ptr = try cg.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new);
4226 try cg.store(payload_ptr, operand, pl_ty, 0);
43494227
43504228 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
4351 try func.emitWValue(err_union);
4352 try func.addImm32(0);
4229 try cg.emitWValue(err_union);
4230 try cg.addImm32(0);
43534231 const err_val_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
4354 try func.addMemArg(.i32_store16, .{
4232 try cg.addMemArg(.i32_store16, .{
43554233 .offset = err_union.offset() + err_val_offset,
43564234 .alignment = 2,
43574235 });
43584236 break :result err_union;
43594237 };
4360 return func.finishAir(inst, result, &.{ty_op.operand});
4238 return cg.finishAir(inst, result, &.{ty_op.operand});
43614239}
43624240
4363fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4364 const pt = func.pt;
4365 const zcu = pt.zcu;
4366 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4241fn airWrapErrUnionErr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4242 const zcu = cg.pt.zcu;
4243 const ty_op = cg.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);
43694246 const err_ty = ty_op.ty.toType();
43704247 const pl_ty = err_ty.errorUnionPayload(zcu);
43714248
43724249 const result = result: {
43734250 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4374 break :result func.reuseOperand(ty_op.operand, operand);
4251 break :result cg.reuseOperand(ty_op.operand, operand);
43754252 }
43764253
4377 const err_union = try func.allocStack(err_ty);
4254 const err_union = try cg.allocStack(err_ty);
43784255 // 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
43814258 // 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);
43834260 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
43864263 break :result err_union;
43874264 };
4388 return func.finishAir(inst, result, &.{ty_op.operand});
4265 return cg.finishAir(inst, result, &.{ty_op.operand});
43894266}
43904267
4391fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4392 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4268fn airIntcast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4269 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
43934270
43944271 const ty = ty_op.ty.toType();
4395 const operand = try func.resolveInst(ty_op.operand);
4396 const operand_ty = func.typeOf(ty_op.operand);
4397 const pt = func.pt;
4398 const zcu = pt.zcu;
4272 const operand = try cg.resolveInst(ty_op.operand);
4273 const operand_ty = cg.typeOf(ty_op.operand);
4274 const zcu = cg.pt.zcu;
43994275 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", .{});
44014277 }
44024278 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", .{});
44044280 }
44054281
44064282 const op_bits = toWasmBits(@intCast(operand_ty.bitSize(zcu))).?;
44074283 const wanted_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?;
44084284 const result = if (op_bits == wanted_bits)
4409 func.reuseOperand(ty_op.operand, operand)
4285 cg.reuseOperand(ty_op.operand, operand)
44104286 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});
44144290}
44154291
44164292/// Upcasts or downcasts an integer based on the given and wanted types,
44174293/// and stores the result in a new operand.
44184294/// Asserts type's bitsize <= 128
44194295/// NOTE: May leave the result on the top of the stack.
4420fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
4421 const pt = func.pt;
4422 const zcu = pt.zcu;
4296fn intcast(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
4297 const zcu = cg.pt.zcu;
44234298 const given_bitsize = @as(u16, @intCast(given.bitSize(zcu)));
44244299 const wanted_bitsize = @as(u16, @intCast(wanted.bitSize(zcu)));
44254300 assert(given_bitsize <= 128);
......@@ -4432,470 +4307,456 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
44324307 }
44334308
44344309 if (op_bits == 64 and wanted_bits == 32) {
4435 try func.emitWValue(operand);
4436 try func.addTag(.i32_wrap_i64);
4310 try cg.emitWValue(operand);
4311 try cg.addTag(.i32_wrap_i64);
44374312 return .stack;
44384313 } else if (op_bits == 32 and wanted_bits == 64) {
4439 try func.emitWValue(operand);
4440 try func.addTag(if (wanted.isSignedInt(zcu)) .i64_extend_i32_s else .i64_extend_i32_u);
4314 try cg.emitWValue(operand);
4315 try cg.addTag(if (wanted.isSignedInt(zcu)) .i64_extend_i32_s else .i64_extend_i32_u);
44414316 return .stack;
44424317 } else if (wanted_bits == 128) {
44434318 // for 128bit integers we store the integer in the virtual stack, rather than a local
4444 const stack_ptr = try func.allocStack(wanted);
4445 try func.emitWValue(stack_ptr);
4319 const stack_ptr = try cg.allocStack(wanted);
4320 try cg.emitWValue(stack_ptr);
44464321
44474322 // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it
44484323 // meaning less store operations are required.
44494324 const lhs = if (op_bits == 32) blk: {
44504325 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);
44524327 } else operand;
44534328
44544329 // 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
44574332 // For signed integers we shift lsb by 63 (64bit integer - 1 sign bit) and store remaining value
44584333 if (wanted.isSignedInt(zcu)) {
4459 try func.emitWValue(stack_ptr);
4460 const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
4461 try func.store(.stack, shr, Type.u64, 8 + stack_ptr.offset());
4334 try cg.emitWValue(stack_ptr);
4335 const shr = try cg.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
4336 try cg.store(.stack, shr, Type.u64, 8 + stack_ptr.offset());
44624337 } else {
44634338 // 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);
44654340 }
44664341 return stack_ptr;
4467 } else return func.load(operand, wanted, 0);
4342 } else return cg.load(operand, wanted, 0);
44684343}
44694344
4470fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
4471 const pt = func.pt;
4472 const zcu = pt.zcu;
4473 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4474 const operand = try func.resolveInst(un_op);
4345fn airIsNull(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
4346 const zcu = cg.pt.zcu;
4347 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4348 const operand = try cg.resolveInst(un_op);
44754349
4476 const op_ty = func.typeOf(un_op);
4350 const op_ty = cg.typeOf(un_op);
44774351 const optional_ty = if (op_kind == .ptr) op_ty.childType(zcu) else op_ty;
4478 const result = try func.isNull(operand, optional_ty, opcode);
4479 return func.finishAir(inst, result, &.{un_op});
4352 const result = try cg.isNull(operand, optional_ty, opcode);
4353 return cg.finishAir(inst, result, &.{un_op});
44804354}
44814355
44824356/// For a given type and operand, checks if it's considered `null`.
44834357/// NOTE: Leaves the result on the stack
4484fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {
4485 const pt = func.pt;
4358fn isNull(cg: *CodeGen, operand: WValue, optional_ty: Type, opcode: std.wasm.Opcode) InnerError!WValue {
4359 const pt = cg.pt;
44864360 const zcu = pt.zcu;
4487 try func.emitWValue(operand);
4361 try cg.emitWValue(operand);
44884362 const payload_ty = optional_ty.optionalChild(zcu);
44894363 if (!optional_ty.optionalReprIsPayload(zcu)) {
44904364 // When payload is zero-bits, we can treat operand as a value, rather than
44914365 // a pointer to the stack value
44924366 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
44934367 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)});
44954369 };
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 });
44974371 }
44984372 } else if (payload_ty.isSlice(zcu)) {
4499 switch (func.arch()) {
4500 .wasm32 => try func.addMemArg(.i32_load, .{ .offset = operand.offset(), .alignment = 4 }),
4501 .wasm64 => try func.addMemArg(.i64_load, .{ .offset = operand.offset(), .alignment = 8 }),
4502 else => unreachable,
4373 switch (cg.ptr_size) {
4374 .wasm32 => try cg.addMemArg(.i32_load, .{ .offset = operand.offset(), .alignment = 4 }),
4375 .wasm64 => try cg.addMemArg(.i64_load, .{ .offset = operand.offset(), .alignment = 8 }),
45034376 }
45044377 }
45054378
45064379 // Compare the null value with '0'
4507 try func.addImm32(0);
4508 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
4380 try cg.addImm32(0);
4381 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
45094382
45104383 return .stack;
45114384}
45124385
4513fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4514 const pt = func.pt;
4515 const zcu = pt.zcu;
4516 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4517 const opt_ty = func.typeOf(ty_op.operand);
4518 const payload_ty = func.typeOfIndex(inst);
4386fn airOptionalPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4387 const zcu = cg.pt.zcu;
4388 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4389 const opt_ty = cg.typeOf(ty_op.operand);
4390 const payload_ty = cg.typeOfIndex(inst);
45194391 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4520 return func.finishAir(inst, .none, &.{ty_op.operand});
4392 return cg.finishAir(inst, .none, &.{ty_op.operand});
45214393 }
45224394
45234395 const result = result: {
4524 const operand = try func.resolveInst(ty_op.operand);
4525 if (opt_ty.optionalReprIsPayload(zcu)) break :result func.reuseOperand(ty_op.operand, operand);
4396 const operand = try cg.resolveInst(ty_op.operand);
4397 if (opt_ty.optionalReprIsPayload(zcu)) break :result cg.reuseOperand(ty_op.operand, operand);
45264398
4527 if (isByRef(payload_ty, pt, func.target.*)) {
4528 break :result try func.buildPointerOffset(operand, 0, .new);
4399 if (isByRef(payload_ty, zcu, cg.target)) {
4400 break :result try cg.buildPointerOffset(operand, 0, .new);
45294401 }
45304402
4531 break :result try func.load(operand, payload_ty, 0);
4403 break :result try cg.load(operand, payload_ty, 0);
45324404 };
4533 return func.finishAir(inst, result, &.{ty_op.operand});
4405 return cg.finishAir(inst, result, &.{ty_op.operand});
45344406}
45354407
4536fn airOptionalPayloadPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4537 const pt = func.pt;
4538 const zcu = pt.zcu;
4539 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4540 const operand = try func.resolveInst(ty_op.operand);
4541 const opt_ty = func.typeOf(ty_op.operand).childType(zcu);
4408fn airOptionalPayloadPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4409 const zcu = cg.pt.zcu;
4410 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4411 const operand = try cg.resolveInst(ty_op.operand);
4412 const opt_ty = cg.typeOf(ty_op.operand).childType(zcu);
45424413
45434414 const result = result: {
45444415 const payload_ty = opt_ty.optionalChild(zcu);
45454416 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);
45474418 }
45484419
4549 break :result try func.buildPointerOffset(operand, 0, .new);
4420 break :result try cg.buildPointerOffset(operand, 0, .new);
45504421 };
4551 return func.finishAir(inst, result, &.{ty_op.operand});
4422 return cg.finishAir(inst, result, &.{ty_op.operand});
45524423}
45534424
4554fn airOptionalPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4555 const pt = func.pt;
4425fn airOptionalPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4426 const pt = cg.pt;
45564427 const zcu = pt.zcu;
4557 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4558 const operand = try func.resolveInst(ty_op.operand);
4559 const opt_ty = func.typeOf(ty_op.operand).childType(zcu);
4428 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4429 const operand = try cg.resolveInst(ty_op.operand);
4430 const opt_ty = cg.typeOf(ty_op.operand).childType(zcu);
45604431 const payload_ty = opt_ty.optionalChild(zcu);
45614432 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()});
45634434 }
45644435
45654436 if (opt_ty.optionalReprIsPayload(zcu)) {
4566 return func.finishAir(inst, operand, &.{ty_op.operand});
4437 return cg.finishAir(inst, operand, &.{ty_op.operand});
45674438 }
45684439
45694440 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)});
45714442 };
45724443
4573 try func.emitWValue(operand);
4574 try func.addImm32(1);
4575 try func.addMemArg(.i32_store8, .{ .offset = operand.offset() + offset, .alignment = 1 });
4444 try cg.emitWValue(operand);
4445 try cg.addImm32(1);
4446 try cg.addMemArg(.i32_store8, .{ .offset = operand.offset() + offset, .alignment = 1 });
45764447
4577 const result = try func.buildPointerOffset(operand, 0, .new);
4578 return func.finishAir(inst, result, &.{ty_op.operand});
4448 const result = try cg.buildPointerOffset(operand, 0, .new);
4449 return cg.finishAir(inst, result, &.{ty_op.operand});
45794450}
45804451
4581fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4582 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4583 const payload_ty = func.typeOf(ty_op.operand);
4584 const pt = func.pt;
4452fn airWrapOptional(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4453 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4454 const payload_ty = cg.typeOf(ty_op.operand);
4455 const pt = cg.pt;
45854456 const zcu = pt.zcu;
45864457
45874458 const result = result: {
45884459 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4589 const non_null_bit = try func.allocStack(Type.u1);
4590 try func.emitWValue(non_null_bit);
4591 try func.addImm32(1);
4592 try func.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 });
4460 const non_null_bit = try cg.allocStack(Type.u1);
4461 try cg.emitWValue(non_null_bit);
4462 try cg.addImm32(1);
4463 try cg.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 });
45934464 break :result non_null_bit;
45944465 }
45954466
4596 const operand = try func.resolveInst(ty_op.operand);
4597 const op_ty = func.typeOfIndex(inst);
4467 const operand = try cg.resolveInst(ty_op.operand);
4468 const op_ty = cg.typeOfIndex(inst);
45984469 if (op_ty.optionalReprIsPayload(zcu)) {
4599 break :result func.reuseOperand(ty_op.operand, operand);
4470 break :result cg.reuseOperand(ty_op.operand, operand);
46004471 }
46014472 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)});
46034474 };
46044475
46054476 // 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);
4607 try func.emitWValue(result_ptr);
4608 try func.addImm32(1);
4609 try func.addMemArg(.i32_store8, .{ .offset = result_ptr.offset() + offset, .alignment = 1 });
4477 const result_ptr = try cg.allocStack(op_ty);
4478 try cg.emitWValue(result_ptr);
4479 try cg.addImm32(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);
4612 try func.store(payload_ptr, operand, payload_ty, 0);
4482 const payload_ptr = try cg.buildPointerOffset(result_ptr, 0, .new);
4483 try cg.store(payload_ptr, operand, payload_ty, 0);
46134484 break :result result_ptr;
46144485 };
46154486
4616 return func.finishAir(inst, result, &.{ty_op.operand});
4487 return cg.finishAir(inst, result, &.{ty_op.operand});
46174488}
46184489
4619fn airSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4620 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4621 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
4490fn airSlice(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4491 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4492 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
46224493
4623 const lhs = try func.resolveInst(bin_op.lhs);
4624 const rhs = try func.resolveInst(bin_op.rhs);
4625 const slice_ty = func.typeOfIndex(inst);
4494 const lhs = try cg.resolveInst(bin_op.lhs);
4495 const rhs = try cg.resolveInst(bin_op.rhs);
4496 const slice_ty = cg.typeOfIndex(inst);
46264497
4627 const slice = try func.allocStack(slice_ty);
4628 try func.store(slice, lhs, Type.usize, 0);
4629 try func.store(slice, rhs, Type.usize, func.ptrSize());
4498 const slice = try cg.allocStack(slice_ty);
4499 try cg.store(slice, lhs, Type.usize, 0);
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 });
46324503}
46334504
4634fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4635 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4505fn airSliceLen(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4506 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
46364507
4637 const operand = try func.resolveInst(ty_op.operand);
4638 return func.finishAir(inst, try func.sliceLen(operand), &.{ty_op.operand});
4508 const operand = try cg.resolveInst(ty_op.operand);
4509 return cg.finishAir(inst, try cg.sliceLen(operand), &.{ty_op.operand});
46394510}
46404511
4641fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4642 const pt = func.pt;
4643 const zcu = pt.zcu;
4644 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
4512fn airSliceElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4513 const zcu = cg.pt.zcu;
4514 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
46454515
4646 const slice_ty = func.typeOf(bin_op.lhs);
4647 const slice = try func.resolveInst(bin_op.lhs);
4648 const index = try func.resolveInst(bin_op.rhs);
4516 const slice_ty = cg.typeOf(bin_op.lhs);
4517 const slice = try cg.resolveInst(bin_op.lhs);
4518 const index = try cg.resolveInst(bin_op.rhs);
46494519 const elem_ty = slice_ty.childType(zcu);
46504520 const elem_size = elem_ty.abiSize(zcu);
46514521
46524522 // load pointer onto stack
4653 _ = try func.load(slice, Type.usize, 0);
4523 _ = try cg.load(slice, Type.usize, 0);
46544524
46554525 // calculate index into slice
4656 try func.emitWValue(index);
4657 try func.addImm32(@intCast(elem_size));
4658 try func.addTag(.i32_mul);
4659 try func.addTag(.i32_add);
4526 try cg.emitWValue(index);
4527 try cg.addImm32(@intCast(elem_size));
4528 try cg.addTag(.i32_mul);
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))
46624532 .stack
46634533 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 });
46674537}
46684538
4669fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4670 const pt = func.pt;
4671 const zcu = pt.zcu;
4672 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4673 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
4539fn airSliceElemPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4540 const zcu = cg.pt.zcu;
4541 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4542 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
46744543
46754544 const elem_ty = ty_pl.ty.toType().childType(zcu);
46764545 const elem_size = elem_ty.abiSize(zcu);
46774546
4678 const slice = try func.resolveInst(bin_op.lhs);
4679 const index = try func.resolveInst(bin_op.rhs);
4547 const slice = try cg.resolveInst(bin_op.lhs);
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
46834552 // calculate index into slice
4684 try func.emitWValue(index);
4685 try func.addImm32(@intCast(elem_size));
4686 try func.addTag(.i32_mul);
4687 try func.addTag(.i32_add);
4553 try cg.emitWValue(index);
4554 try cg.addImm32(@intCast(elem_size));
4555 try cg.addTag(.i32_mul);
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 });
46904559}
46914560
4692fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4693 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4694 const operand = try func.resolveInst(ty_op.operand);
4695 return func.finishAir(inst, try func.slicePtr(operand), &.{ty_op.operand});
4561fn airSlicePtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4562 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4563 const operand = try cg.resolveInst(ty_op.operand);
4564 return cg.finishAir(inst, try cg.slicePtr(operand), &.{ty_op.operand});
46964565}
46974566
4698fn slicePtr(func: *CodeGen, operand: WValue) InnerError!WValue {
4699 const ptr = try func.load(operand, Type.usize, 0);
4700 return ptr.toLocal(func, Type.usize);
4567fn slicePtr(cg: *CodeGen, operand: WValue) InnerError!WValue {
4568 const ptr = try cg.load(operand, Type.usize, 0);
4569 return ptr.toLocal(cg, Type.usize);
47014570}
47024571
4703fn sliceLen(func: *CodeGen, operand: WValue) InnerError!WValue {
4704 const len = try func.load(operand, Type.usize, func.ptrSize());
4705 return len.toLocal(func, Type.usize);
4572fn sliceLen(cg: *CodeGen, operand: WValue) InnerError!WValue {
4573 const len = try cg.load(operand, Type.usize, cg.ptrSize());
4574 return len.toLocal(cg, Type.usize);
47064575}
47074576
4708fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4709 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4577fn airTrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
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);
47124581 const wanted_ty: Type = ty_op.ty.toType();
4713 const op_ty = func.typeOf(ty_op.operand);
4714 const pt = func.pt;
4715 const zcu = pt.zcu;
4582 const op_ty = cg.typeOf(ty_op.operand);
4583 const zcu = cg.pt.zcu;
47164584
47174585 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", .{});
47194587 }
47204588
47214589 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)
47234591 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});
47274595}
47284596
47294597/// Truncates a given operand to a given type, discarding any overflown bits.
47304598/// NOTE: Resulting value is left on the stack.
4731fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) InnerError!WValue {
4732 const pt = func.pt;
4733 const zcu = pt.zcu;
4599fn trunc(cg: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) InnerError!WValue {
4600 const zcu = cg.pt.zcu;
47344601 const given_bits = @as(u16, @intCast(given_ty.bitSize(zcu)));
47354602 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});
47374604 }
47384605
4739 var result = try func.intcast(operand, given_ty, wanted_ty);
4606 var result = try cg.intcast(operand, given_ty, wanted_ty);
47404607 const wanted_bits = @as(u16, @intCast(wanted_ty.bitSize(zcu)));
47414608 const wasm_bits = toWasmBits(wanted_bits).?;
47424609 if (wasm_bits != wanted_bits) {
4743 result = try func.wrapOperand(result, wanted_ty);
4610 result = try cg.wrapOperand(result, wanted_ty);
47444611 }
47454612 return result;
47464613}
47474614
4748fn airIntFromBool(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4749 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4750 const operand = try func.resolveInst(un_op);
4751 const result = func.reuseOperand(un_op, operand);
4615fn airIntFromBool(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4616 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4617 const operand = try cg.resolveInst(un_op);
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});
47544621}
47554622
4756fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4757 const pt = func.pt;
4758 const zcu = pt.zcu;
4759 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4623fn airArrayToSlice(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4624 const zcu = cg.pt.zcu;
4625 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
47604626
4761 const operand = try func.resolveInst(ty_op.operand);
4762 const array_ty = func.typeOf(ty_op.operand).childType(zcu);
4627 const operand = try cg.resolveInst(ty_op.operand);
4628 const array_ty = cg.typeOf(ty_op.operand).childType(zcu);
47634629 const slice_ty = ty_op.ty.toType();
47644630
47654631 // 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
47684634 // store the array ptr in the slice
47694635 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);
47714637 }
47724638
47734639 // store the length of the array in the slice
47744640 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});
47784644}
47794645
4780fn airIntFromPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4781 const pt = func.pt;
4782 const zcu = pt.zcu;
4783 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4784 const operand = try func.resolveInst(un_op);
4785 const ptr_ty = func.typeOf(un_op);
4646fn airIntFromPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4647 const zcu = cg.pt.zcu;
4648 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4649 const operand = try cg.resolveInst(un_op);
4650 const ptr_ty = cg.typeOf(un_op);
47864651 const result = if (ptr_ty.isSlice(zcu))
4787 try func.slicePtr(operand)
4652 try cg.slicePtr(operand)
47884653 else switch (operand) {
47894654 // for stack offset, return a pointer to this offset.
4790 .stack_offset => try func.buildPointerOffset(operand, 0, .new),
4791 else => func.reuseOperand(un_op, operand),
4655 .stack_offset => try cg.buildPointerOffset(operand, 0, .new),
4656 else => cg.reuseOperand(un_op, operand),
47924657 };
4793 return func.finishAir(inst, result, &.{un_op});
4658 return cg.finishAir(inst, result, &.{un_op});
47944659}
47954660
4796fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4797 const pt = func.pt;
4798 const zcu = pt.zcu;
4799 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
4661fn airPtrElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4662 const zcu = cg.pt.zcu;
4663 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
48004664
4801 const ptr_ty = func.typeOf(bin_op.lhs);
4802 const ptr = try func.resolveInst(bin_op.lhs);
4803 const index = try func.resolveInst(bin_op.rhs);
4665 const ptr_ty = cg.typeOf(bin_op.lhs);
4666 const ptr = try cg.resolveInst(bin_op.lhs);
4667 const index = try cg.resolveInst(bin_op.rhs);
48044668 const elem_ty = ptr_ty.childType(zcu);
48054669 const elem_size = elem_ty.abiSize(zcu);
48064670
48074671 // load pointer onto the stack
48084672 if (ptr_ty.isSlice(zcu)) {
4809 _ = try func.load(ptr, Type.usize, 0);
4673 _ = try cg.load(ptr, Type.usize, 0);
48104674 } else {
4811 try func.lowerToStack(ptr);
4675 try cg.lowerToStack(ptr);
48124676 }
48134677
48144678 // calculate index into slice
4815 try func.emitWValue(index);
4816 try func.addImm32(@intCast(elem_size));
4817 try func.addTag(.i32_mul);
4818 try func.addTag(.i32_add);
4679 try cg.emitWValue(index);
4680 try cg.addImm32(@intCast(elem_size));
4681 try cg.addTag(.i32_mul);
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))
48214685 .stack
48224686 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 });
48264690}
48274691
4828fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4829 const pt = func.pt;
4830 const zcu = pt.zcu;
4831 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4832 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
4692fn airPtrElemPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4693 const zcu = cg.pt.zcu;
4694 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4695 const bin_op = cg.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);
48354698 const elem_ty = ty_pl.ty.toType().childType(zcu);
48364699 const elem_size = elem_ty.abiSize(zcu);
48374700
4838 const ptr = try func.resolveInst(bin_op.lhs);
4839 const index = try func.resolveInst(bin_op.rhs);
4701 const ptr = try cg.resolveInst(bin_op.lhs);
4702 const index = try cg.resolveInst(bin_op.rhs);
48404703
48414704 // load pointer onto the stack
48424705 if (ptr_ty.isSlice(zcu)) {
4843 _ = try func.load(ptr, Type.usize, 0);
4706 _ = try cg.load(ptr, Type.usize, 0);
48444707 } else {
4845 try func.lowerToStack(ptr);
4708 try cg.lowerToStack(ptr);
48464709 }
48474710
48484711 // calculate index into ptr
4849 try func.emitWValue(index);
4850 try func.addImm32(@intCast(elem_size));
4851 try func.addTag(.i32_mul);
4852 try func.addTag(.i32_add);
4712 try cg.emitWValue(index);
4713 try cg.addImm32(@intCast(elem_size));
4714 try cg.addTag(.i32_mul);
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 });
48554718}
48564719
4857fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
4858 const pt = func.pt;
4859 const zcu = pt.zcu;
4860 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4861 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
4720fn airPtrBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
4721 const zcu = cg.pt.zcu;
4722 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4723 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
48624724
4863 const ptr = try func.resolveInst(bin_op.lhs);
4864 const offset = try func.resolveInst(bin_op.rhs);
4865 const ptr_ty = func.typeOf(bin_op.lhs);
4725 const ptr = try cg.resolveInst(bin_op.lhs);
4726 const offset = try cg.resolveInst(bin_op.rhs);
4727 const ptr_ty = cg.typeOf(bin_op.lhs);
48664728 const pointee_ty = switch (ptr_ty.ptrSize(zcu)) {
48674729 .One => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type
48684730 else => ptr_ty.childType(zcu),
48694731 };
48704732
4871 const valtype = typeToValtype(Type.usize, pt, func.target.*);
4733 const valtype = typeToValtype(Type.usize, zcu, cg.target);
48724734 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });
48734735 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });
48744736
4875 try func.lowerToStack(ptr);
4876 try func.emitWValue(offset);
4877 try func.addImm32(@intCast(pointee_ty.abiSize(zcu)));
4878 try func.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode));
4879 try func.addTag(Mir.Inst.Tag.fromOpcode(bin_opcode));
4737 try cg.lowerToStack(ptr);
4738 try cg.emitWValue(offset);
4739 try cg.addImm32(@intCast(pointee_ty.abiSize(zcu)));
4740 try cg.addTag(Mir.Inst.Tag.fromOpcode(mul_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 });
48824744}
48834745
4884fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
4885 const pt = func.pt;
4886 const zcu = pt.zcu;
4746fn airMemset(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
4747 const zcu = cg.pt.zcu;
48874748 if (safety) {
48884749 // TODO if the value is undef, write 0xaa bytes to dest
48894750 } else {
48904751 // TODO if the value is undef, don't lower this instruction
48914752 }
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);
4895 const ptr_ty = func.typeOf(bin_op.lhs);
4896 const value = try func.resolveInst(bin_op.rhs);
4755 const ptr = try cg.resolveInst(bin_op.lhs);
4756 const ptr_ty = cg.typeOf(bin_op.lhs);
4757 const value = try cg.resolveInst(bin_op.rhs);
48974758 const len = switch (ptr_ty.ptrSize(zcu)) {
4898 .Slice => try func.sliceLen(ptr),
4759 .Slice => try cg.sliceLen(ptr),
48994760 .One => @as(WValue, .{ .imm32 = @as(u32, @intCast(ptr_ty.childType(zcu).arrayLen(zcu))) }),
49004761 .C, .Many => unreachable,
49014762 };
......@@ -4905,27 +4766,27 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void
49054766 else
49064767 ptr_ty.childType(zcu);
49074768
4908 const dst_ptr = try func.sliceOrArrayPtr(ptr, ptr_ty);
4909 try func.memset(elem_ty, dst_ptr, len, value);
4769 const dst_ptr = try cg.sliceOrArrayPtr(ptr, ptr_ty);
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 });
49124773}
49134774
49144775/// Sets a region of memory at `ptr` to the value of `value`
49154776/// When the user has enabled the bulk_memory feature, we lower
49164777/// this to wasm's memset instruction. When the feature is not present,
49174778/// we implement it manually.
4918fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) InnerError!void {
4919 const pt = func.pt;
4920 const abi_size = @as(u32, @intCast(elem_ty.abiSize(pt.zcu)));
4779fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) InnerError!void {
4780 const zcu = cg.pt.zcu;
4781 const abi_size = @as(u32, @intCast(elem_ty.abiSize(zcu)));
49214782
49224783 // When bulk_memory is enabled, we lower it to wasm's memset instruction.
49234784 // If not, we lower it ourselves.
4924 if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory) and abi_size == 1) {
4925 try func.lowerToStack(ptr);
4926 try func.emitWValue(value);
4927 try func.emitWValue(len);
4928 try func.addExtended(.memory_fill);
4785 if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory) and abi_size == 1) {
4786 try cg.lowerToStack(ptr);
4787 try cg.emitWValue(value);
4788 try cg.emitWValue(len);
4789 try cg.addExtended(.memory_fill);
49294790 return;
49304791 }
49314792
......@@ -4933,100 +4794,95 @@ fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue
49334794 .imm32 => |val| .{ .imm32 = val * abi_size },
49344795 .imm64 => |val| .{ .imm64 = val * abi_size },
49354796 else => if (abi_size != 1) blk: {
4936 const new_len = try func.ensureAllocLocal(Type.usize);
4937 try func.emitWValue(len);
4938 switch (func.arch()) {
4797 const new_len = try cg.ensureAllocLocal(Type.usize);
4798 try cg.emitWValue(len);
4799 switch (cg.ptr_size) {
49394800 .wasm32 => {
4940 try func.emitWValue(.{ .imm32 = abi_size });
4941 try func.addTag(.i32_mul);
4801 try cg.emitWValue(.{ .imm32 = abi_size });
4802 try cg.addTag(.i32_mul);
49424803 },
49434804 .wasm64 => {
4944 try func.emitWValue(.{ .imm64 = abi_size });
4945 try func.addTag(.i64_mul);
4805 try cg.emitWValue(.{ .imm64 = abi_size });
4806 try cg.addTag(.i64_mul);
49464807 },
4947 else => unreachable,
49484808 }
4949 try func.addLabel(.local_set, new_len.local.value);
4809 try cg.addLocal(.local_set, new_len.local.value);
49504810 break :blk new_len;
49514811 } else len,
49524812 };
49534813
4954 var end_ptr = try func.allocLocal(Type.usize);
4955 defer end_ptr.free(func);
4956 var new_ptr = try func.buildPointerOffset(ptr, 0, .new);
4957 defer new_ptr.free(func);
4814 var end_ptr = try cg.allocLocal(Type.usize);
4815 defer end_ptr.free(cg);
4816 var new_ptr = try cg.buildPointerOffset(ptr, 0, .new);
4817 defer new_ptr.free(cg);
49584818
49594819 // get the loop conditional: if current pointer address equals final pointer's address
4960 try func.lowerToStack(ptr);
4961 try func.emitWValue(final_len);
4962 switch (func.arch()) {
4963 .wasm32 => try func.addTag(.i32_add),
4964 .wasm64 => try func.addTag(.i64_add),
4965 else => unreachable,
4820 try cg.lowerToStack(ptr);
4821 try cg.emitWValue(final_len);
4822 switch (cg.ptr_size) {
4823 .wasm32 => try cg.addTag(.i32_add),
4824 .wasm64 => try cg.addTag(.i64_add),
49664825 }
4967 try func.addLabel(.local_set, end_ptr.local.value);
4826 try cg.addLocal(.local_set, end_ptr.local.value);
49684827
49694828 // outer block to jump to when loop is done
4970 try func.startBlock(.block, wasm.block_empty);
4971 try func.startBlock(.loop, wasm.block_empty);
4829 try cg.startBlock(.block, .empty);
4830 try cg.startBlock(.loop, .empty);
49724831
49734832 // check for condition for loop end
4974 try func.emitWValue(new_ptr);
4975 try func.emitWValue(end_ptr);
4976 switch (func.arch()) {
4977 .wasm32 => try func.addTag(.i32_eq),
4978 .wasm64 => try func.addTag(.i64_eq),
4979 else => unreachable,
4833 try cg.emitWValue(new_ptr);
4834 try cg.emitWValue(end_ptr);
4835 switch (cg.ptr_size) {
4836 .wasm32 => try cg.addTag(.i32_eq),
4837 .wasm64 => try cg.addTag(.i64_eq),
49804838 }
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
49834841 // 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
49864844 // move the pointer to the next element
4987 try func.emitWValue(new_ptr);
4988 switch (func.arch()) {
4845 try cg.emitWValue(new_ptr);
4846 switch (cg.ptr_size) {
49894847 .wasm32 => {
4990 try func.emitWValue(.{ .imm32 = abi_size });
4991 try func.addTag(.i32_add);
4848 try cg.emitWValue(.{ .imm32 = abi_size });
4849 try cg.addTag(.i32_add);
49924850 },
49934851 .wasm64 => {
4994 try func.emitWValue(.{ .imm64 = abi_size });
4995 try func.addTag(.i64_add);
4852 try cg.emitWValue(.{ .imm64 = abi_size });
4853 try cg.addTag(.i64_add);
49964854 },
4997 else => unreachable,
49984855 }
4999 try func.addLabel(.local_set, new_ptr.local.value);
4856 try cg.addLocal(.local_set, new_ptr.local.value);
50004857
50014858 // end of loop
5002 try func.addLabel(.br, 0); // jump to start of loop
5003 try func.endBlock();
5004 try func.endBlock();
4859 try cg.addLabel(.br, 0); // jump to start of loop
4860 try cg.endBlock();
4861 try cg.endBlock();
50054862}
50064863
5007fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5008 const pt = func.pt;
5009 const zcu = pt.zcu;
5010 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
4864fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4865 const zcu = cg.pt.zcu;
4866 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
50114867
5012 const array_ty = func.typeOf(bin_op.lhs);
5013 const array = try func.resolveInst(bin_op.lhs);
5014 const index = try func.resolveInst(bin_op.rhs);
4868 const array_ty = cg.typeOf(bin_op.lhs);
4869 const array = try cg.resolveInst(bin_op.lhs);
4870 const index = try cg.resolveInst(bin_op.rhs);
50154871 const elem_ty = array_ty.childType(zcu);
50164872 const elem_size = elem_ty.abiSize(zcu);
50174873
5018 if (isByRef(array_ty, pt, func.target.*)) {
5019 try func.lowerToStack(array);
5020 try func.emitWValue(index);
5021 try func.addImm32(@intCast(elem_size));
5022 try func.addTag(.i32_mul);
5023 try func.addTag(.i32_add);
4874 if (isByRef(array_ty, zcu, cg.target)) {
4875 try cg.lowerToStack(array);
4876 try cg.emitWValue(index);
4877 try cg.addImm32(@intCast(elem_size));
4878 try cg.addTag(.i32_mul);
4879 try cg.addTag(.i32_add);
50244880 } else {
5025 std.debug.assert(array_ty.zigTypeTag(zcu) == .vector);
4881 assert(array_ty.zigTypeTag(zcu) == .vector);
50264882
50274883 switch (index) {
50284884 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)) {
50304886 8 => if (elem_ty.isSignedInt(zcu)) .i8x16_extract_lane_s else .i8x16_extract_lane_u,
50314887 16 => if (elem_ty.isSignedInt(zcu)) .i16x8_extract_lane_s else .i16x8_extract_lane_u,
50324888 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 {
50344890 else => unreachable,
50354891 };
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));
5042 try func.mir_extra.appendSlice(func.gpa, &operands);
5043 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
4897 const extra_index = cg.extraLen();
4898 try cg.mir_extra.appendSlice(cg.gpa, &operands);
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 });
50464902 },
50474903 else => {
5048 const stack_vec = try func.allocStack(array_ty);
5049 try func.store(stack_vec, array, array_ty, 0);
4904 const stack_vec = try cg.allocStack(array_ty);
4905 try cg.store(stack_vec, array, array_ty, 0);
50504906
50514907 // Is a non-unrolled vector (v128)
5052 try func.lowerToStack(stack_vec);
5053 try func.emitWValue(index);
5054 try func.addImm32(@intCast(elem_size));
5055 try func.addTag(.i32_mul);
5056 try func.addTag(.i32_add);
4908 try cg.lowerToStack(stack_vec);
4909 try cg.emitWValue(index);
4910 try cg.addImm32(@intCast(elem_size));
4911 try cg.addTag(.i32_mul);
4912 try cg.addTag(.i32_add);
50574913 },
50584914 }
50594915 }
50604916
5061 const elem_result = if (isByRef(elem_ty, pt, func.target.*))
4917 const elem_result = if (isByRef(elem_ty, zcu, cg.target))
50624918 .stack
50634919 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 });
50674923}
50684924
5069fn airIntFromFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5070 const pt = func.pt;
5071 const zcu = pt.zcu;
5072 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4925fn airIntFromFloat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4926 const zcu = cg.pt.zcu;
4927 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
50734928
5074 const operand = try func.resolveInst(ty_op.operand);
5075 const op_ty = func.typeOf(ty_op.operand);
5076 const op_bits = op_ty.floatBits(func.target.*);
4929 const operand = try cg.resolveInst(ty_op.operand);
4930 const op_ty = cg.typeOf(ty_op.operand);
4931 const op_bits = op_ty.floatBits(cg.target.*);
50774932
5078 const dest_ty = func.typeOfIndex(inst);
4933 const dest_ty = cg.typeOfIndex(inst);
50794934 const dest_info = dest_ty.intInfo(zcu);
50804935
50814936 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});
50834938 }
50844939
50854940 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);
5087
5088 var fn_name_buf: [16]u8 = undefined;
5089 const fn_name = std.fmt.bufPrint(&fn_name_buf, "__fix{s}{s}f{s}i", .{
5090 switch (dest_info.signedness) {
5091 .signed => "",
5092 .unsigned => "uns",
4941 const dest_bitsize = if (dest_info.bits <= 32) 32 else std.math.ceilPowerOfTwoAssert(u16, dest_info.bits);
4942
4943 const intrinsic = switch (dest_info.signedness) {
4944 inline .signed, .unsigned => |ct_s| switch (op_bits) {
4945 inline 16, 32, 64, 80, 128 => |ct_op_bits| switch (dest_bitsize) {
4946 inline 32, 64, 128 => |ct_dest_bits| @field(
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,
50934958 },
5094 target_util.compilerRtFloatAbbrev(op_bits),
5095 target_util.compilerRtIntAbbrev(dest_bitsize),
5096 }) catch unreachable;
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});
4959 };
4960 const result = try cg.callIntrinsic(intrinsic, &.{op_ty.ip_index}, dest_ty, &.{operand});
4961 return cg.finishAir(inst, result, &.{ty_op.operand});
51004962 }
51014963
5102 try func.emitWValue(operand);
4964 try cg.emitWValue(operand);
51034965 const op = buildOpcode(.{
51044966 .op = .trunc,
5105 .valtype1 = typeToValtype(dest_ty, pt, func.target.*),
5106 .valtype2 = typeToValtype(op_ty, pt, func.target.*),
4967 .valtype1 = typeToValtype(dest_ty, zcu, cg.target),
4968 .valtype2 = typeToValtype(op_ty, zcu, cg.target),
51074969 .signedness = dest_info.signedness,
51084970 });
5109 try func.addTag(Mir.Inst.Tag.fromOpcode(op));
5110 const result = try func.wrapOperand(.stack, dest_ty);
5111 return func.finishAir(inst, result, &.{ty_op.operand});
4971 try cg.addTag(Mir.Inst.Tag.fromOpcode(op));
4972 const result = try cg.wrapOperand(.stack, dest_ty);
4973 return cg.finishAir(inst, result, &.{ty_op.operand});
51124974}
51134975
5114fn airFloatFromInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5115 const pt = func.pt;
5116 const zcu = pt.zcu;
5117 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4976fn airFloatFromInt(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4977 const zcu = cg.pt.zcu;
4978 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
51184979
5119 const operand = try func.resolveInst(ty_op.operand);
5120 const op_ty = func.typeOf(ty_op.operand);
4980 const operand = try cg.resolveInst(ty_op.operand);
4981 const op_ty = cg.typeOf(ty_op.operand);
51214982 const op_info = op_ty.intInfo(zcu);
51224983
5123 const dest_ty = func.typeOfIndex(inst);
5124 const dest_bits = dest_ty.floatBits(func.target.*);
4984 const dest_ty = cg.typeOfIndex(inst);
4985 const dest_bits = dest_ty.floatBits(cg.target.*);
51254986
51264987 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});
51284989 }
51294990
51304991 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);
5132
5133 var fn_name_buf: [16]u8 = undefined;
5134 const fn_name = std.fmt.bufPrint(&fn_name_buf, "__float{s}{s}i{s}f", .{
5135 switch (op_info.signedness) {
5136 .signed => "",
5137 .unsigned => "un",
4992 const op_bitsize = if (op_info.bits <= 32) 32 else std.math.ceilPowerOfTwoAssert(u16, op_info.bits);
4993
4994 const intrinsic = switch (op_info.signedness) {
4995 inline .signed, .unsigned => |ct_s| switch (op_bitsize) {
4996 inline 32, 64, 128 => |ct_int_bits| switch (dest_bits) {
4997 inline 16, 32, 64, 80, 128 => |ct_float_bits| @field(
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,
51385009 },
5139 target_util.compilerRtIntAbbrev(op_bitsize),
5140 target_util.compilerRtFloatAbbrev(dest_bits),
5141 }) catch unreachable;
5010 };
51425011
5143 const result = try func.callIntrinsic(fn_name, &.{op_ty.ip_index}, dest_ty, &.{operand});
5144 return func.finishAir(inst, result, &.{ty_op.operand});
5012 const result = try cg.callIntrinsic(intrinsic, &.{op_ty.ip_index}, dest_ty, &.{operand});
5013 return cg.finishAir(inst, result, &.{ty_op.operand});
51455014 }
51465015
5147 try func.emitWValue(operand);
5016 try cg.emitWValue(operand);
51485017 const op = buildOpcode(.{
51495018 .op = .convert,
5150 .valtype1 = typeToValtype(dest_ty, pt, func.target.*),
5151 .valtype2 = typeToValtype(op_ty, pt, func.target.*),
5019 .valtype1 = typeToValtype(dest_ty, zcu, cg.target),
5020 .valtype2 = typeToValtype(op_ty, zcu, cg.target),
51525021 .signedness = op_info.signedness,
51535022 });
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});
51575026}
51585027
5159fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5160 const pt = func.pt;
5161 const zcu = pt.zcu;
5162 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5163 const operand = try func.resolveInst(ty_op.operand);
5164 const ty = func.typeOfIndex(inst);
5028fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5029 const zcu = cg.pt.zcu;
5030 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5031 const operand = try cg.resolveInst(ty_op.operand);
5032 const ty = cg.typeOfIndex(inst);
51655033 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: {
51685036 switch (operand) {
51695037 // when the operand lives in the linear memory section, we can directly
51705038 // load and splat the value at once. Meaning we do not first have to load
51715039 // the scalar value onto the stack.
5172 .stack_offset, .memory, .memory_offset => {
5040 .stack_offset, .nav_ref, .uav_ref => {
51735041 const opcode = switch (elem_ty.bitSize(zcu)) {
5174 8 => std.wasm.simdOpcode(.v128_load8_splat),
5175 16 => std.wasm.simdOpcode(.v128_load16_splat),
5176 32 => std.wasm.simdOpcode(.v128_load32_splat),
5177 64 => std.wasm.simdOpcode(.v128_load64_splat),
5042 8 => @intFromEnum(std.wasm.SimdOpcode.v128_load8_splat),
5043 16 => @intFromEnum(std.wasm.SimdOpcode.v128_load16_splat),
5044 32 => @intFromEnum(std.wasm.SimdOpcode.v128_load32_splat),
5045 64 => @intFromEnum(std.wasm.SimdOpcode.v128_load64_splat),
51785046 else => break :blk, // Cannot make use of simd-instructions
51795047 };
5180 try func.emitWValue(operand);
5181 // TODO: Add helper functions for simd opcodes
5182 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));
5048 try cg.emitWValue(operand);
5049 const extra_index: u32 = cg.extraLen();
51835050 // 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{
51855052 opcode,
51865053 operand.offset(),
51875054 @intCast(elem_ty.abiAlignment(zcu).toByteUnits().?),
51885055 });
5189 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
5190 return func.finishAir(inst, .stack, &.{ty_op.operand});
5056 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
5057 return cg.finishAir(inst, .stack, &.{ty_op.operand});
51915058 },
51925059 .local => {
51935060 const opcode = switch (elem_ty.bitSize(zcu)) {
5194 8 => std.wasm.simdOpcode(.i8x16_splat),
5195 16 => std.wasm.simdOpcode(.i16x8_splat),
5196 32 => if (elem_ty.isInt(zcu)) std.wasm.simdOpcode(.i32x4_splat) else std.wasm.simdOpcode(.f32x4_splat),
5197 64 => if (elem_ty.isInt(zcu)) std.wasm.simdOpcode(.i64x2_splat) else std.wasm.simdOpcode(.f64x2_splat),
5061 8 => @intFromEnum(std.wasm.SimdOpcode.i8x16_splat),
5062 16 => @intFromEnum(std.wasm.SimdOpcode.i16x8_splat),
5063 32 => if (elem_ty.isInt(zcu)) @intFromEnum(std.wasm.SimdOpcode.i32x4_splat) else @intFromEnum(std.wasm.SimdOpcode.f32x4_splat),
5064 64 => if (elem_ty.isInt(zcu)) @intFromEnum(std.wasm.SimdOpcode.i64x2_splat) else @intFromEnum(std.wasm.SimdOpcode.f64x2_splat),
51985065 else => break :blk, // Cannot make use of simd-instructions
51995066 };
5200 try func.emitWValue(operand);
5201 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));
5202 try func.mir_extra.append(func.gpa, opcode);
5203 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
5204 return func.finishAir(inst, .stack, &.{ty_op.operand});
5067 try cg.emitWValue(operand);
5068 const extra_index = cg.extraLen();
5069 try cg.mir_extra.append(cg.gpa, opcode);
5070 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
5071 return cg.finishAir(inst, .stack, &.{ty_op.operand});
52055072 },
52065073 else => unreachable,
52075074 }
......@@ -5209,38 +5076,38 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52095076 const elem_size = elem_ty.bitSize(zcu);
52105077 const vector_len = @as(usize, @intCast(ty.vectorLen(zcu)));
52115078 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});
52135080 }
52145081
5215 const result = try func.allocStack(ty);
5082 const result = try cg.allocStack(ty);
52165083 const elem_byte_size = @as(u32, @intCast(elem_ty.abiSize(zcu)));
52175084 var index: usize = 0;
52185085 var offset: u32 = 0;
52195086 while (index < vector_len) : (index += 1) {
5220 try func.store(result, operand, elem_ty, offset);
5087 try cg.store(result, operand, elem_ty, offset);
52215088 offset += elem_byte_size;
52225089 }
52235090
5224 return func.finishAir(inst, result, &.{ty_op.operand});
5091 return cg.finishAir(inst, result, &.{ty_op.operand});
52255092}
52265093
5227fn airSelect(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5228 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
5229 const operand = try func.resolveInst(pl_op.operand);
5094fn airSelect(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5095 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
5096 const operand = try cg.resolveInst(pl_op.operand);
52305097
52315098 _ = operand;
5232 return func.fail("TODO: Implement wasm airSelect", .{});
5099 return cg.fail("TODO: Implement wasm airSelect", .{});
52335100}
52345101
5235fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5236 const pt = func.pt;
5102fn airShuffle(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5103 const pt = cg.pt;
52375104 const zcu = pt.zcu;
5238 const inst_ty = func.typeOfIndex(inst);
5239 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5240 const extra = func.air.extraData(Air.Shuffle, ty_pl.payload).data;
5105 const inst_ty = cg.typeOfIndex(inst);
5106 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5107 const extra = cg.air.extraData(Air.Shuffle, ty_pl.payload).data;
52415108
5242 const a = try func.resolveInst(extra.a);
5243 const b = try func.resolveInst(extra.b);
5109 const a = try cg.resolveInst(extra.a);
5110 const b = try cg.resolveInst(extra.b);
52445111 const mask = Value.fromInterned(extra.mask);
52455112 const mask_len = extra.mask_len;
52465113
......@@ -5248,26 +5115,26 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52485115 const elem_size = child_ty.abiSize(zcu);
52495116
52505117 // 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.*)) {
5252 const result = try func.allocStack(inst_ty);
5118 if (isByRef(cg.typeOf(extra.a), zcu, cg.target) or isByRef(inst_ty, zcu, cg.target)) {
5119 const result = try cg.allocStack(inst_ty);
52535120
52545121 for (0..mask_len) |index| {
52555122 const value = (try mask.elemValue(pt, index)).toSignedInt(zcu);
52565123
5257 try func.emitWValue(result);
5124 try cg.emitWValue(result);
52585125
52595126 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)))
52615128 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)));
52655132 }
52665133
5267 return func.finishAir(inst, result, &.{ extra.a, extra.b });
5134 return cg.finishAir(inst, result, &.{ extra.a, extra.b });
52685135 } else {
52695136 var operands = [_]u32{
5270 std.wasm.simdOpcode(.i8x16_shuffle),
5137 @intFromEnum(std.wasm.SimdOpcode.i8x16_shuffle),
52715138 } ++ [1]u32{undefined} ** 4;
52725139
52735140 var lanes = mem.asBytes(operands[1..]);
......@@ -5283,91 +5150,91 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52835150 }
52845151 }
52855152
5286 try func.emitWValue(a);
5287 try func.emitWValue(b);
5153 try cg.emitWValue(a);
5154 try cg.emitWValue(b);
52885155
5289 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));
5290 try func.mir_extra.appendSlice(func.gpa, &operands);
5291 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
5156 const extra_index = cg.extraLen();
5157 try cg.mir_extra.appendSlice(cg.gpa, &operands);
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 });
52945161 }
52955162}
52965163
5297fn airReduce(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5298 const reduce = func.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
5299 const operand = try func.resolveInst(reduce.operand);
5164fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5165 const reduce = cg.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
5166 const operand = try cg.resolveInst(reduce.operand);
53005167
53015168 _ = operand;
5302 return func.fail("TODO: Implement wasm airReduce", .{});
5169 return cg.fail("TODO: Implement wasm airReduce", .{});
53035170}
53045171
5305fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5306 const pt = func.pt;
5172fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5173 const pt = cg.pt;
53075174 const zcu = pt.zcu;
53085175 const ip = &zcu.intern_pool;
5309 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5310 const result_ty = func.typeOfIndex(inst);
5176 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5177 const result_ty = cg.typeOfIndex(inst);
53115178 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
53145181 const result: WValue = result_value: {
53155182 switch (result_ty.zigTypeTag(zcu)) {
53165183 .array => {
5317 const result = try func.allocStack(result_ty);
5184 const result = try cg.allocStack(result_ty);
53185185 const elem_ty = result_ty.childType(zcu);
53195186 const elem_size = @as(u32, @intCast(elem_ty.abiSize(zcu)));
53205187 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);
53225189 } else null;
53235190
53245191 // When the element type is by reference, we must copy the entire
53255192 // value. It is therefore safer to move the offset pointer and store
53265193 // each value individually, instead of using store offsets.
5327 if (isByRef(elem_ty, pt, func.target.*)) {
5194 if (isByRef(elem_ty, zcu, cg.target)) {
53285195 // copy stack pointer into a temporary local, which is
53295196 // 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);
53315198 for (elements, 0..) |elem, elem_index| {
5332 const elem_val = try func.resolveInst(elem);
5333 try func.store(offset, elem_val, elem_ty, 0);
5199 const elem_val = try cg.resolveInst(elem);
5200 try cg.store(offset, elem_val, elem_ty, 0);
53345201
53355202 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);
53375204 }
53385205 }
53395206 if (sentinel) |sent| {
5340 try func.store(offset, sent, elem_ty, 0);
5207 try cg.store(offset, sent, elem_ty, 0);
53415208 }
53425209 } else {
53435210 var offset: u32 = 0;
53445211 for (elements) |elem| {
5345 const elem_val = try func.resolveInst(elem);
5346 try func.store(result, elem_val, elem_ty, offset);
5212 const elem_val = try cg.resolveInst(elem);
5213 try cg.store(result, elem_val, elem_ty, offset);
53475214 offset += elem_size;
53485215 }
53495216 if (sentinel) |sent| {
5350 try func.store(result, sent, elem_ty, offset);
5217 try cg.store(result, sent, elem_ty, offset);
53515218 }
53525219 }
53535220 break :result_value result;
53545221 },
53555222 .@"struct" => switch (result_ty.containerLayout(zcu)) {
53565223 .@"packed" => {
5357 if (isByRef(result_ty, pt, func.target.*)) {
5358 return func.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{});
5224 if (isByRef(result_ty, zcu, cg.target)) {
5225 return cg.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{});
53595226 }
53605227 const packed_struct = zcu.typeToPackedStruct(result_ty).?;
53615228 const field_types = packed_struct.field_types;
53625229 const backing_type = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip));
53635230
53645231 // ensure the result is zero'd
5365 const result = try func.allocLocal(backing_type);
5232 const result = try cg.allocLocal(backing_type);
53665233 if (backing_type.bitSize(zcu) <= 32)
5367 try func.addImm32(0)
5234 try cg.addImm32(0)
53685235 else
5369 try func.addImm64(0);
5370 try func.addLabel(.local_set, result.local.value);
5236 try cg.addImm64(0);
5237 try cg.addLocal(.local_set, result.local.value);
53715238
53725239 var current_bit: u16 = 0;
53735240 for (elements, 0..) |elem, elem_index| {
......@@ -5379,46 +5246,46 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
53795246 else
53805247 .{ .imm64 = current_bit };
53815248
5382 const value = try func.resolveInst(elem);
5249 const value = try cg.resolveInst(elem);
53835250 const value_bit_size: u16 = @intCast(field_ty.bitSize(zcu));
53845251 const int_ty = try pt.intType(.unsigned, value_bit_size);
53855252
53865253 // load our current result on stack so we can perform all transformations
53875254 // using only stack values. Saving the cost of loads and stores.
5388 try func.emitWValue(result);
5389 const bitcasted = try func.bitcast(int_ty, field_ty, value);
5390 const extended_val = try func.intcast(bitcasted, int_ty, backing_type);
5255 try cg.emitWValue(result);
5256 const bitcasted = try cg.bitcast(int_ty, field_ty, value);
5257 const extended_val = try cg.intcast(bitcasted, int_ty, backing_type);
53915258 // no need to shift any values when the current offset is 0
53925259 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);
53945261 } else extended_val;
53955262 // 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");
5397 try func.addLabel(.local_set, result.local.value);
5263 _ = try cg.binOp(.stack, shifted, backing_type, .@"or");
5264 try cg.addLocal(.local_set, result.local.value);
53985265 current_bit += value_bit_size;
53995266 }
54005267 break :result_value result;
54015268 },
54025269 else => {
5403 const result = try func.allocStack(result_ty);
5404 const offset = try func.buildPointerOffset(result, 0, .new); // pointer to offset
5270 const result = try cg.allocStack(result_ty);
5271 const offset = try cg.buildPointerOffset(result, 0, .new); // pointer to offset
54055272 var prev_field_offset: u64 = 0;
54065273 for (elements, 0..) |elem, elem_index| {
54075274 if (try result_ty.structFieldValueComptime(pt, elem_index) != null) continue;
54085275
54095276 const elem_ty = result_ty.fieldType(elem_index, zcu);
54105277 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);
54125279 prev_field_offset = field_offset;
54135280
5414 const value = try func.resolveInst(elem);
5415 try func.store(offset, value, elem_ty, 0);
5281 const value = try cg.resolveInst(elem);
5282 try cg.store(offset, value, elem_ty, 0);
54165283 }
54175284
54185285 break :result_value result;
54195286 },
54205287 },
5421 .vector => return func.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
5288 .vector => return cg.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
54225289 else => unreachable,
54235290 }
54245291 };
......@@ -5426,22 +5293,22 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
54265293 if (elements.len <= Liveness.bpi - 1) {
54275294 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
54285295 @memcpy(buf[0..elements.len], elements);
5429 return func.finishAir(inst, result, &buf);
5296 return cg.finishAir(inst, result, &buf);
54305297 }
5431 var bt = try func.iterateBigTomb(inst, elements.len);
5298 var bt = try cg.iterateBigTomb(inst, elements.len);
54325299 for (elements) |arg| bt.feed(arg);
54335300 return bt.finishAir(result);
54345301}
54355302
5436fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5437 const pt = func.pt;
5303fn airUnionInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5304 const pt = cg.pt;
54385305 const zcu = pt.zcu;
54395306 const ip = &zcu.intern_pool;
5440 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5441 const extra = func.air.extraData(Air.UnionInit, ty_pl.payload).data;
5307 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5308 const extra = cg.air.extraData(Air.UnionInit, ty_pl.payload).data;
54425309
54435310 const result = result: {
5444 const union_ty = func.typeOfIndex(inst);
5311 const union_ty = cg.typeOfIndex(inst);
54455312 const layout = union_ty.unionGetLayout(zcu);
54465313 const union_obj = zcu.typeToUnion(union_ty).?;
54475314 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 {
54515318 const tag_ty = union_ty.unionTagTypeHypothetical(zcu);
54525319 const enum_field_index = tag_ty.enumFieldIndex(field_name, zcu).?;
54535320 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);
54555322 };
54565323 if (layout.payload_size == 0) {
54575324 if (layout.tag_size == 0) {
54585325 break :result .none;
54595326 }
5460 assert(!isByRef(union_ty, pt, func.target.*));
5327 assert(!isByRef(union_ty, zcu, cg.target));
54615328 break :result tag_int;
54625329 }
54635330
5464 if (isByRef(union_ty, pt, func.target.*)) {
5465 const result_ptr = try func.allocStack(union_ty);
5466 const payload = try func.resolveInst(extra.init);
5331 if (isByRef(union_ty, zcu, cg.target)) {
5332 const result_ptr = try cg.allocStack(union_ty);
5333 const payload = try cg.resolveInst(extra.init);
54675334 if (layout.tag_align.compare(.gte, layout.payload_align)) {
5468 if (isByRef(field_ty, pt, func.target.*)) {
5469 const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new);
5470 try func.store(payload_ptr, payload, field_ty, 0);
5335 if (isByRef(field_ty, zcu, cg.target)) {
5336 const payload_ptr = try cg.buildPointerOffset(result_ptr, layout.tag_size, .new);
5337 try cg.store(payload_ptr, payload, field_ty, 0);
54715338 } 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));
54735340 }
54745341
54755342 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);
54775344 }
54785345 } else {
5479 try func.store(result_ptr, payload, field_ty, 0);
5346 try cg.store(result_ptr, payload, field_ty, 0);
54805347 if (layout.tag_size > 0) {
5481 try func.store(
5348 try cg.store(
54825349 result_ptr,
54835350 tag_int,
54845351 Type.fromInterned(union_obj.enum_tag_ty),
......@@ -5488,138 +5355,136 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
54885355 }
54895356 break :result result_ptr;
54905357 } else {
5491 const operand = try func.resolveInst(extra.init);
5358 const operand = try cg.resolveInst(extra.init);
54925359 const union_int_type = try pt.intType(.unsigned, @as(u16, @intCast(union_ty.bitSize(zcu))));
54935360 if (field_ty.zigTypeTag(zcu) == .float) {
54945361 const int_type = try pt.intType(.unsigned, @intCast(field_ty.bitSize(zcu)));
5495 const bitcasted = try func.bitcast(field_ty, int_type, operand);
5496 break :result try func.trunc(bitcasted, int_type, union_int_type);
5362 const bitcasted = try cg.bitcast(field_ty, int_type, operand);
5363 break :result try cg.trunc(bitcasted, int_type, union_int_type);
54975364 } else if (field_ty.isPtrAtRuntime(zcu)) {
54985365 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);
55005367 }
5501 break :result try func.intcast(operand, field_ty, union_int_type);
5368 break :result try cg.intcast(operand, field_ty, union_int_type);
55025369 }
55035370 };
55045371
5505 return func.finishAir(inst, result, &.{extra.init});
5372 return cg.finishAir(inst, result, &.{extra.init});
55065373}
55075374
5508fn airPrefetch(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5509 const prefetch = func.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
5510 return func.finishAir(inst, .none, &.{prefetch.ptr});
5375fn airPrefetch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5376 const prefetch = cg.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
5377 return cg.finishAir(inst, .none, &.{prefetch.ptr});
55115378}
55125379
5513fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5514 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
5380fn airWasmMemorySize(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5381 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
55155382
5516 try func.addLabel(.memory_size, pl_op.payload);
5517 return func.finishAir(inst, .stack, &.{pl_op.operand});
5383 try cg.addLabel(.memory_size, pl_op.payload);
5384 return cg.finishAir(inst, .stack, &.{pl_op.operand});
55185385}
55195386
5520fn airWasmMemoryGrow(func: *CodeGen, inst: Air.Inst.Index) !void {
5521 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
5387fn airWasmMemoryGrow(cg: *CodeGen, inst: Air.Inst.Index) !void {
5388 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
55225389
5523 const operand = try func.resolveInst(pl_op.operand);
5524 try func.emitWValue(operand);
5525 try func.addLabel(.memory_grow, pl_op.payload);
5526 return func.finishAir(inst, .stack, &.{pl_op.operand});
5390 const operand = try cg.resolveInst(pl_op.operand);
5391 try cg.emitWValue(operand);
5392 try cg.addLabel(.memory_grow, pl_op.payload);
5393 return cg.finishAir(inst, .stack, &.{pl_op.operand});
55275394}
55285395
5529fn cmpOptionals(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
5530 const pt = func.pt;
5531 const zcu = pt.zcu;
5396fn cmpOptionals(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
5397 const zcu = cg.pt.zcu;
55325398 assert(operand_ty.hasRuntimeBitsIgnoreComptime(zcu));
55335399 assert(op == .eq or op == .neq);
55345400 const payload_ty = operand_ty.optionalChild(zcu);
55355401
55365402 // We store the final result in here that will be validated
55375403 // if the optional is truly equal.
5538 var result = try func.ensureAllocLocal(Type.i32);
5539 defer result.free(func);
5540
5541 try func.startBlock(.block, wasm.block_empty);
5542 _ = try func.isNull(lhs, operand_ty, .i32_eq);
5543 _ = try func.isNull(rhs, operand_ty, .i32_eq);
5544 try func.addTag(.i32_ne); // inverse so we can exit early
5545 try func.addLabel(.br_if, 0);
5546
5547 _ = try func.load(lhs, payload_ty, 0);
5548 _ = try func.load(rhs, payload_ty, 0);
5549 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, pt, func.target.*) });
5550 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
5551 try func.addLabel(.br_if, 0);
5552
5553 try func.addImm32(1);
5554 try func.addLabel(.local_set, result.local.value);
5555 try func.endBlock();
5556
5557 try func.emitWValue(result);
5558 try func.addImm32(0);
5559 try func.addTag(if (op == .eq) .i32_ne else .i32_eq);
5404 var result = try cg.ensureAllocLocal(Type.i32);
5405 defer result.free(cg);
5406
5407 try cg.startBlock(.block, .empty);
5408 _ = try cg.isNull(lhs, operand_ty, .i32_eq);
5409 _ = try cg.isNull(rhs, operand_ty, .i32_eq);
5410 try cg.addTag(.i32_ne); // inverse so we can exit early
5411 try cg.addLabel(.br_if, 0);
5412
5413 _ = try cg.load(lhs, payload_ty, 0);
5414 _ = try cg.load(rhs, payload_ty, 0);
5415 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, zcu, cg.target) });
5416 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
5417 try cg.addLabel(.br_if, 0);
5418
5419 try cg.addImm32(1);
5420 try cg.addLocal(.local_set, result.local.value);
5421 try cg.endBlock();
5422
5423 try cg.emitWValue(result);
5424 try cg.addImm32(0);
5425 try cg.addTag(if (op == .eq) .i32_ne else .i32_eq);
55605426 return .stack;
55615427}
55625428
55635429/// Compares big integers by checking both its high bits and low bits.
55645430/// NOTE: Leaves the result of the comparison on top of the stack.
55655431/// 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 {
5567 const pt = func.pt;
5568 const zcu = pt.zcu;
5432fn cmpBigInt(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
5433 const zcu = cg.pt.zcu;
55695434 assert(operand_ty.abiSize(zcu) >= 16);
55705435 assert(!(lhs != .stack and rhs == .stack));
55715436 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)});
55735438 }
55745439
5575 var lhs_msb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
5576 defer lhs_msb.free(func);
5577 var rhs_msb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);
5578 defer rhs_msb.free(func);
5440 var lhs_msb = try (try cg.load(lhs, Type.u64, 8)).toLocal(cg, Type.u64);
5441 defer lhs_msb.free(cg);
5442 var rhs_msb = try (try cg.load(rhs, Type.u64, 8)).toLocal(cg, Type.u64);
5443 defer rhs_msb.free(cg);
55795444
55805445 switch (op) {
55815446 .eq, .neq => {
5582 const xor_high = try func.binOp(lhs_msb, rhs_msb, Type.u64, .xor);
5583 const lhs_lsb = try func.load(lhs, Type.u64, 0);
5584 const rhs_lsb = try func.load(rhs, Type.u64, 0);
5585 const xor_low = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, .xor);
5586 const or_result = try func.binOp(xor_high, xor_low, Type.u64, .@"or");
5447 const xor_high = try cg.binOp(lhs_msb, rhs_msb, Type.u64, .xor);
5448 const lhs_lsb = try cg.load(lhs, Type.u64, 0);
5449 const rhs_lsb = try cg.load(rhs, Type.u64, 0);
5450 const xor_low = try cg.binOp(lhs_lsb, rhs_lsb, Type.u64, .xor);
5451 const or_result = try cg.binOp(xor_high, xor_low, Type.u64, .@"or");
55875452
55885453 switch (op) {
5589 .eq => return func.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq),
5590 .neq => return func.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq),
5454 .eq => return cg.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq),
5455 .neq => return cg.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq),
55915456 else => unreachable,
55925457 }
55935458 },
55945459 else => {
55955460 const ty = if (operand_ty.isSignedInt(zcu)) Type.i64 else Type.u64;
55965461 // leave those value on top of the stack for '.select'
5597 const lhs_lsb = try func.load(lhs, Type.u64, 0);
5598 const rhs_lsb = try func.load(rhs, Type.u64, 0);
5599 _ = try func.cmp(lhs_lsb, rhs_lsb, Type.u64, op);
5600 _ = try func.cmp(lhs_msb, rhs_msb, ty, op);
5601 _ = try func.cmp(lhs_msb, rhs_msb, ty, .eq);
5602 try func.addTag(.select);
5462 const lhs_lsb = try cg.load(lhs, Type.u64, 0);
5463 const rhs_lsb = try cg.load(rhs, Type.u64, 0);
5464 _ = try cg.cmp(lhs_lsb, rhs_lsb, Type.u64, op);
5465 _ = try cg.cmp(lhs_msb, rhs_msb, ty, op);
5466 _ = try cg.cmp(lhs_msb, rhs_msb, ty, .eq);
5467 try cg.addTag(.select);
56035468 },
56045469 }
56055470
56065471 return .stack;
56075472}
56085473
5609fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5610 const pt = func.pt;
5474fn airSetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5475 const pt = cg.pt;
56115476 const zcu = pt.zcu;
5612 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
5613 const un_ty = func.typeOf(bin_op.lhs).childType(zcu);
5614 const tag_ty = func.typeOf(bin_op.rhs);
5477 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
5478 const un_ty = cg.typeOf(bin_op.lhs).childType(zcu);
5479 const tag_ty = cg.typeOf(bin_op.rhs);
56155480 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);
5619 const new_tag = try func.resolveInst(bin_op.rhs);
5483 const union_ptr = try cg.resolveInst(bin_op.lhs);
5484 const new_tag = try cg.resolveInst(bin_op.rhs);
56205485 if (layout.payload_size == 0) {
5621 try func.store(union_ptr, new_tag, tag_ty, 0);
5622 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
5486 try cg.store(union_ptr, new_tag, tag_ty, 0);
5487 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
56235488 }
56245489
56255490 // 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 {
56275492 const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align)) blk: {
56285493 break :blk @intCast(layout.payload_size);
56295494 } else 0;
5630 try func.store(union_ptr, new_tag, tag_ty, offset);
5631 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
5495 try cg.store(union_ptr, new_tag, tag_ty, offset);
5496 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
56325497}
56335498
5634fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5635 const zcu = func.pt.zcu;
5636 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5499fn airGetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5500 const zcu = cg.pt.zcu;
5501 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
56375502
5638 const un_ty = func.typeOf(ty_op.operand);
5639 const tag_ty = func.typeOfIndex(inst);
5503 const un_ty = cg.typeOf(ty_op.operand);
5504 const tag_ty = cg.typeOfIndex(inst);
56405505 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);
56445509 // when the tag alignment is smaller than the payload, the field will be stored
56455510 // after the payload.
56465511 const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align))
56475512 @intCast(layout.payload_size)
56485513 else
56495514 0;
5650 const result = try func.load(operand, tag_ty, offset);
5651 return func.finishAir(inst, result, &.{ty_op.operand});
5515 const result = try cg.load(operand, tag_ty, offset);
5516 return cg.finishAir(inst, result, &.{ty_op.operand});
56525517}
56535518
5654fn airFpext(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5655 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5519fn airFpext(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5520 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
56565521
5657 const dest_ty = func.typeOfIndex(inst);
5658 const operand = try func.resolveInst(ty_op.operand);
5659 const result = try func.fpext(operand, func.typeOf(ty_op.operand), dest_ty);
5660 return func.finishAir(inst, result, &.{ty_op.operand});
5522 const dest_ty = cg.typeOfIndex(inst);
5523 const operand = try cg.resolveInst(ty_op.operand);
5524 const result = try cg.fpext(operand, cg.typeOf(ty_op.operand), dest_ty);
5525 return cg.finishAir(inst, result, &.{ty_op.operand});
56615526}
56625527
5663/// Extends a float from a given `Type` to a larger wanted `Type`
5664/// NOTE: Leaves the result on the stack
5665fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
5666 const given_bits = given.floatBits(func.target.*);
5667 const wanted_bits = wanted.floatBits(func.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;
5528/// Extends a float from a given `Type` to a larger wanted `Type`, leaving the
5529/// result on the stack.
5530fn fpext(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
5531 const given_bits = given.floatBits(cg.target.*);
5532 const wanted_bits = wanted.floatBits(cg.target.*);
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});
56965571}
56975572
5698fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5699 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5573fn airFptrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5574 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
57005575
5701 const dest_ty = func.typeOfIndex(inst);
5702 const operand = try func.resolveInst(ty_op.operand);
5703 const result = try func.fptrunc(operand, func.typeOf(ty_op.operand), dest_ty);
5704 return func.finishAir(inst, result, &.{ty_op.operand});
5576 const dest_ty = cg.typeOfIndex(inst);
5577 const operand = try cg.resolveInst(ty_op.operand);
5578 const result = try cg.fptrunc(operand, cg.typeOf(ty_op.operand), dest_ty);
5579 return cg.finishAir(inst, result, &.{ty_op.operand});
57055580}
57065581
5707/// Truncates a float from a given `Type` to its wanted `Type`
5708/// NOTE: The result value remains on the stack
5709fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
5710 const given_bits = given.floatBits(func.target.*);
5711 const wanted_bits = wanted.floatBits(func.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;
5582/// Truncates a float from a given `Type` to its wanted `Type`, leaving the
5583/// result on the stack.
5584fn fptrunc(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
5585 const given_bits = given.floatBits(cg.target.*);
5586 const wanted_bits = wanted.floatBits(cg.target.*);
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});
57355624}
57365625
5737fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5738 const pt = func.pt;
5739 const zcu = pt.zcu;
5740 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5626fn airErrUnionPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5627 const zcu = cg.pt.zcu;
5628 const ty_op = cg.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);
57435631 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
57465634 // set error-tag to '0' to annotate error union is non-error
5747 try func.store(
5635 try cg.store(
57485636 operand,
57495637 .{ .imm32 = 0 },
57505638 Type.anyerror,
......@@ -5753,63 +5641,60 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi
57535641
57545642 const result = result: {
57555643 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
5756 break :result func.reuseOperand(ty_op.operand, operand);
5644 break :result cg.reuseOperand(ty_op.operand, operand);
57575645 }
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);
57605648 };
5761 return func.finishAir(inst, result, &.{ty_op.operand});
5649 return cg.finishAir(inst, result, &.{ty_op.operand});
57625650}
57635651
5764fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5765 const pt = func.pt;
5766 const zcu = pt.zcu;
5767 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5768 const extra = func.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
5652fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5653 const zcu = cg.pt.zcu;
5654 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5655 const extra = cg.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);
57715658 const parent_ty = ty_pl.ty.toType().childType(zcu);
57725659 const field_offset = parent_ty.structFieldOffset(extra.field_index, zcu);
57735660
57745661 const result = if (field_offset != 0) result: {
5775 const base = try func.buildPointerOffset(field_ptr, 0, .new);
5776 try func.addLabel(.local_get, base.local.value);
5777 try func.addImm32(@intCast(field_offset));
5778 try func.addTag(.i32_sub);
5779 try func.addLabel(.local_set, base.local.value);
5662 const base = try cg.buildPointerOffset(field_ptr, 0, .new);
5663 try cg.addLocal(.local_get, base.local.value);
5664 try cg.addImm32(@intCast(field_offset));
5665 try cg.addTag(.i32_sub);
5666 try cg.addLocal(.local_set, base.local.value);
57805667 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});
57845671}
57855672
5786fn sliceOrArrayPtr(func: *CodeGen, ptr: WValue, ptr_ty: Type) InnerError!WValue {
5787 const pt = func.pt;
5788 const zcu = pt.zcu;
5673fn sliceOrArrayPtr(cg: *CodeGen, ptr: WValue, ptr_ty: Type) InnerError!WValue {
5674 const zcu = cg.pt.zcu;
57895675 if (ptr_ty.isSlice(zcu)) {
5790 return func.slicePtr(ptr);
5676 return cg.slicePtr(ptr);
57915677 } else {
57925678 return ptr;
57935679 }
57945680}
57955681
5796fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5797 const pt = func.pt;
5798 const zcu = pt.zcu;
5799 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
5800 const dst = try func.resolveInst(bin_op.lhs);
5801 const dst_ty = func.typeOf(bin_op.lhs);
5682fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5683 const zcu = cg.pt.zcu;
5684 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
5685 const dst = try cg.resolveInst(bin_op.lhs);
5686 const dst_ty = cg.typeOf(bin_op.lhs);
58025687 const ptr_elem_ty = dst_ty.childType(zcu);
5803 const src = try func.resolveInst(bin_op.rhs);
5804 const src_ty = func.typeOf(bin_op.rhs);
5688 const src = try cg.resolveInst(bin_op.rhs);
5689 const src_ty = cg.typeOf(bin_op.rhs);
58055690 const len = switch (dst_ty.ptrSize(zcu)) {
58065691 .Slice => blk: {
5807 const slice_len = try func.sliceLen(dst);
5692 const slice_len = try cg.sliceLen(dst);
58085693 if (ptr_elem_ty.abiSize(zcu) != 1) {
5809 try func.emitWValue(slice_len);
5810 try func.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });
5811 try func.addTag(.i32_mul);
5812 try func.addLabel(.local_set, slice_len.local.value);
5694 try cg.emitWValue(slice_len);
5695 try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });
5696 try cg.addTag(.i32_mul);
5697 try cg.addLocal(.local_set, slice_len.local.value);
58135698 }
58145699 break :blk slice_len;
58155700 },
......@@ -5818,96 +5703,94 @@ fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58185703 }),
58195704 .C, .Many => unreachable,
58205705 };
5821 const dst_ptr = try func.sliceOrArrayPtr(dst, dst_ty);
5822 const src_ptr = try func.sliceOrArrayPtr(src, src_ty);
5823 try func.memcpy(dst_ptr, src_ptr, len);
5706 const dst_ptr = try cg.sliceOrArrayPtr(dst, dst_ty);
5707 const src_ptr = try cg.sliceOrArrayPtr(src, src_ty);
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 });
58265711}
58275712
5828fn airRetAddr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5713fn airRetAddr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58295714 // 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) {
58315716 .wasm32 => .{ .imm32 = 0 },
58325717 .wasm64 => .{ .imm64 = 0 },
5833 else => unreachable,
58345718 }, &.{});
58355719}
58365720
5837fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5838 const pt = func.pt;
5721fn airPopcount(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5722 const pt = cg.pt;
58395723 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);
5843 const op_ty = func.typeOf(ty_op.operand);
5726 const operand = try cg.resolveInst(ty_op.operand);
5727 const op_ty = cg.typeOf(ty_op.operand);
58445728
58455729 if (op_ty.zigTypeTag(zcu) == .vector) {
5846 return func.fail("TODO: Implement @popCount for vectors", .{});
5730 return cg.fail("TODO: Implement @popCount for vectors", .{});
58475731 }
58485732
58495733 const int_info = op_ty.intInfo(zcu);
58505734 const bits = int_info.bits;
58515735 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});
58535737 };
58545738
58555739 switch (wasm_bits) {
58565740 32 => {
5857 try func.emitWValue(operand);
5741 try cg.emitWValue(operand);
58585742 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));
58605744 }
5861 try func.addTag(.i32_popcnt);
5745 try cg.addTag(.i32_popcnt);
58625746 },
58635747 64 => {
5864 try func.emitWValue(operand);
5748 try cg.emitWValue(operand);
58655749 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));
58675751 }
5868 try func.addTag(.i64_popcnt);
5869 try func.addTag(.i32_wrap_i64);
5870 try func.emitWValue(operand);
5752 try cg.addTag(.i64_popcnt);
5753 try cg.addTag(.i32_wrap_i64);
5754 try cg.emitWValue(operand);
58715755 },
58725756 128 => {
5873 _ = try func.load(operand, Type.u64, 0);
5874 try func.addTag(.i64_popcnt);
5875 _ = try func.load(operand, Type.u64, 8);
5757 _ = try cg.load(operand, Type.u64, 0);
5758 try cg.addTag(.i64_popcnt);
5759 _ = try cg.load(operand, Type.u64, 8);
58765760 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));
58785762 }
5879 try func.addTag(.i64_popcnt);
5880 try func.addTag(.i64_add);
5881 try func.addTag(.i32_wrap_i64);
5763 try cg.addTag(.i64_popcnt);
5764 try cg.addTag(.i64_add);
5765 try cg.addTag(.i32_wrap_i64);
58825766 },
58835767 else => unreachable,
58845768 }
58855769
5886 return func.finishAir(inst, .stack, &.{ty_op.operand});
5770 return cg.finishAir(inst, .stack, &.{ty_op.operand});
58875771}
58885772
5889fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5890 const pt = func.pt;
5891 const zcu = pt.zcu;
5892 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5773fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5774 const zcu = cg.pt.zcu;
5775 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
58935776
5894 const operand = try func.resolveInst(ty_op.operand);
5895 const ty = func.typeOf(ty_op.operand);
5777 const operand = try cg.resolveInst(ty_op.operand);
5778 const ty = cg.typeOf(ty_op.operand);
58965779
58975780 if (ty.zigTypeTag(zcu) == .vector) {
5898 return func.fail("TODO: Implement @bitReverse for vectors", .{});
5781 return cg.fail("TODO: Implement @bitReverse for vectors", .{});
58995782 }
59005783
59015784 const int_info = ty.intInfo(zcu);
59025785 const bits = int_info.bits;
59035786 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});
59055788 };
59065789
59075790 switch (wasm_bits) {
59085791 32 => {
5909 const intrin_ret = try func.callIntrinsic(
5910 "__bitreversesi2",
5792 const intrin_ret = try cg.callIntrinsic(
5793 .__bitreversesi2,
59115794 &.{.u32_type},
59125795 Type.u32,
59135796 &.{operand},
......@@ -5915,12 +5798,12 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
59155798 const result = if (bits == 32)
59165799 intrin_ret
59175800 else
5918 try func.binOp(intrin_ret, .{ .imm32 = 32 - bits }, ty, .shr);
5919 return func.finishAir(inst, result, &.{ty_op.operand});
5801 try cg.binOp(intrin_ret, .{ .imm32 = 32 - bits }, ty, .shr);
5802 return cg.finishAir(inst, result, &.{ty_op.operand});
59205803 },
59215804 64 => {
5922 const intrin_ret = try func.callIntrinsic(
5923 "__bitreversedi2",
5805 const intrin_ret = try cg.callIntrinsic(
5806 .__bitreversedi2,
59245807 &.{.u64_type},
59255808 Type.u64,
59265809 &.{operand},
......@@ -5928,68 +5811,63 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
59285811 const result = if (bits == 64)
59295812 intrin_ret
59305813 else
5931 try func.binOp(intrin_ret, .{ .imm64 = 64 - bits }, ty, .shr);
5932 return func.finishAir(inst, result, &.{ty_op.operand});
5814 try cg.binOp(intrin_ret, .{ .imm64 = 64 - bits }, ty, .shr);
5815 return cg.finishAir(inst, result, &.{ty_op.operand});
59335816 },
59345817 128 => {
5935 const result = try func.allocStack(ty);
5818 const result = try cg.allocStack(ty);
59365819
5937 try func.emitWValue(result);
5938 const first_half = try func.load(operand, Type.u64, 8);
5939 const intrin_ret_first = try func.callIntrinsic(
5940 "__bitreversedi2",
5820 try cg.emitWValue(result);
5821 const first_half = try cg.load(operand, Type.u64, 8);
5822 const intrin_ret_first = try cg.callIntrinsic(
5823 .__bitreversedi2,
59415824 &.{.u64_type},
59425825 Type.u64,
59435826 &.{first_half},
59445827 );
5945 try func.emitWValue(intrin_ret_first);
5828 try cg.emitWValue(intrin_ret_first);
59465829 if (bits < 128) {
5947 try func.emitWValue(.{ .imm64 = 128 - bits });
5948 try func.addTag(.i64_shr_u);
5830 try cg.emitWValue(.{ .imm64 = 128 - bits });
5831 try cg.addTag(.i64_shr_u);
59495832 }
5950 try func.emitWValue(result);
5951 const second_half = try func.load(operand, Type.u64, 0);
5952 const intrin_ret_second = try func.callIntrinsic(
5953 "__bitreversedi2",
5833 try cg.emitWValue(result);
5834 const second_half = try cg.load(operand, Type.u64, 0);
5835 const intrin_ret_second = try cg.callIntrinsic(
5836 .__bitreversedi2,
59545837 &.{.u64_type},
59555838 Type.u64,
59565839 &.{second_half},
59575840 );
5958 try func.emitWValue(intrin_ret_second);
5841 try cg.emitWValue(intrin_ret_second);
59595842 if (bits == 128) {
5960 try func.store(.stack, .stack, Type.u64, result.offset() + 8);
5961 try func.store(.stack, .stack, Type.u64, result.offset());
5843 try cg.store(.stack, .stack, Type.u64, result.offset() + 8);
5844 try cg.store(.stack, .stack, Type.u64, result.offset());
59625845 } else {
5963 var tmp = try func.allocLocal(Type.u64);
5964 defer tmp.free(func);
5965 try func.addLabel(.local_tee, tmp.local.value);
5966 try func.emitWValue(.{ .imm64 = 128 - bits });
5846 var tmp = try cg.allocLocal(Type.u64);
5847 defer tmp.free(cg);
5848 try cg.addLocal(.local_tee, tmp.local.value);
5849 try cg.emitWValue(.{ .imm64 = 128 - bits });
59675850 if (ty.isSignedInt(zcu)) {
5968 try func.addTag(.i64_shr_s);
5851 try cg.addTag(.i64_shr_s);
59695852 } else {
5970 try func.addTag(.i64_shr_u);
5853 try cg.addTag(.i64_shr_u);
59715854 }
5972 try func.store(.stack, .stack, Type.u64, result.offset() + 8);
5973 try func.addLabel(.local_get, tmp.local.value);
5974 try func.emitWValue(.{ .imm64 = bits - 64 });
5975 try func.addTag(.i64_shl);
5976 try func.addTag(.i64_or);
5977 try func.store(.stack, .stack, Type.u64, result.offset());
5855 try cg.store(.stack, .stack, Type.u64, result.offset() + 8);
5856 try cg.addLocal(.local_get, tmp.local.value);
5857 try cg.emitWValue(.{ .imm64 = bits - 64 });
5858 try cg.addTag(.i64_shl);
5859 try cg.addTag(.i64_or);
5860 try cg.store(.stack, .stack, Type.u64, result.offset());
59785861 }
5979 return func.finishAir(inst, result, &.{ty_op.operand});
5862 return cg.finishAir(inst, result, &.{ty_op.operand});
59805863 },
59815864 else => unreachable,
59825865 }
59835866}
59845867
5985fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5986 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5987
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 //
5868fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5869 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5870 const operand = try cg.resolveInst(un_op);
59935871 // Each entry to this table is a slice (ptr+len).
59945872 // The operand in this instruction represents the index within this table.
59955873 // 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 {
59975875 //
59985876 // As the names are global and the slice elements are constant, we do not have
59995877 // to make a copy of the ptr+value but can point towards them directly.
6000 const pt = func.pt;
6001 const error_table_symbol = try func.bin_file.getErrorTableSymbol(pt);
5878 const pt = cg.pt;
60025879 const name_ty = Type.slice_const_u8_sentinel_0;
60035880 const abi_size = name_ty.abiSize(pt.zcu);
60045881
6005 const error_name_value: WValue = .{ .memory = error_table_symbol }; // emitting this will create a relocation
6006 try func.emitWValue(error_name_value);
6007 try func.emitWValue(operand);
6008 switch (func.arch()) {
5882 cg.wasm.error_name_table_ref_count += 1;
5883
5884 // Lowers to a i32.const or i64.const with the error table memory address.
5885 try cg.addTag(.error_name_table_ref);
5886 try cg.emitWValue(operand);
5887 switch (cg.ptr_size) {
60095888 .wasm32 => {
6010 try func.addImm32(@intCast(abi_size));
6011 try func.addTag(.i32_mul);
6012 try func.addTag(.i32_add);
5889 try cg.addImm32(@intCast(abi_size));
5890 try cg.addTag(.i32_mul);
5891 try cg.addTag(.i32_add);
60135892 },
60145893 .wasm64 => {
6015 try func.addImm64(abi_size);
6016 try func.addTag(.i64_mul);
6017 try func.addTag(.i64_add);
5894 try cg.addImm64(abi_size);
5895 try cg.addTag(.i64_mul);
5896 try cg.addTag(.i64_add);
60185897 },
6019 else => unreachable,
60205898 }
60215899
6022 return func.finishAir(inst, .stack, &.{un_op});
5900 return cg.finishAir(inst, .stack, &.{un_op});
60235901}
60245902
6025fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void {
6026 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6027 const slice_ptr = try func.resolveInst(ty_op.operand);
6028 const result = try func.buildPointerOffset(slice_ptr, offset, .new);
6029 return func.finishAir(inst, result, &.{ty_op.operand});
5903fn airPtrSliceFieldPtr(cg: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void {
5904 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5905 const slice_ptr = try cg.resolveInst(ty_op.operand);
5906 const result = try cg.buildPointerOffset(slice_ptr, offset, .new);
5907 return cg.finishAir(inst, result, &.{ty_op.operand});
60305908}
60315909
60325910/// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits
6033fn intZeroValue(func: *CodeGen, ty: Type) InnerError!WValue {
6034 const zcu = func.bin_file.base.comp.zcu.?;
5911fn intZeroValue(cg: *CodeGen, ty: Type) InnerError!WValue {
5912 const zcu = cg.wasm.base.comp.zcu.?;
60355913 const int_info = ty.intInfo(zcu);
60365914 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});
60385916 };
60395917 switch (wasm_bits) {
60405918 32 => return .{ .imm32 = 0 },
60415919 64 => return .{ .imm64 = 0 },
60425920 128 => {
6043 const result = try func.allocStack(ty);
6044 try func.store(result, .{ .imm64 = 0 }, Type.u64, 0);
6045 try func.store(result, .{ .imm64 = 0 }, Type.u64, 8);
5921 const result = try cg.allocStack(ty);
5922 try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0);
5923 try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8);
60465924 return result;
60475925 },
60485926 else => unreachable,
60495927 }
60505928}
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 {
60535931 assert(op == .add or op == .sub);
6054 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6055 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
5932 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5933 const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;
60565934
6057 const lhs = try func.resolveInst(extra.lhs);
6058 const rhs = try func.resolveInst(extra.rhs);
6059 const ty = func.typeOf(extra.lhs);
6060 const pt = func.pt;
5935 const lhs = try cg.resolveInst(extra.lhs);
5936 const rhs = try cg.resolveInst(extra.rhs);
5937 const ty = cg.typeOf(extra.lhs);
5938 const pt = cg.pt;
60615939 const zcu = pt.zcu;
60625940
60635941 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", .{});
60655943 }
60665944
60675945 const int_info = ty.intInfo(zcu);
60685946 const is_signed = int_info.signedness == .signed;
60695947 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});
60715949 }
60725950
6073 const op_result = try func.wrapBinOp(lhs, rhs, ty, op);
6074 var op_tmp = try op_result.toLocal(func, ty);
6075 defer op_tmp.free(func);
5951 const op_result = try cg.wrapBinOp(lhs, rhs, ty, op);
5952 var op_tmp = try op_result.toLocal(cg, ty);
5953 defer op_tmp.free(cg);
60765954
60775955 const cmp_op: std.math.CompareOperator = switch (op) {
60785956 .add => .lt,
......@@ -6080,40 +5958,40 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
60805958 else => unreachable,
60815959 };
60825960 const overflow_bit = if (is_signed) blk: {
6083 const zero = try intZeroValue(func, ty);
6084 const rhs_is_neg = try func.cmp(rhs, zero, ty, .lt);
6085 const overflow_cmp = try func.cmp(op_tmp, lhs, ty, cmp_op);
6086 break :blk try func.cmp(rhs_is_neg, overflow_cmp, Type.u1, .neq);
6087 } else try func.cmp(op_tmp, lhs, ty, cmp_op);
6088 var bit_tmp = try overflow_bit.toLocal(func, Type.u1);
6089 defer bit_tmp.free(func);
6090
6091 const result = try func.allocStack(func.typeOfIndex(inst));
5961 const zero = try intZeroValue(cg, ty);
5962 const rhs_is_neg = try cg.cmp(rhs, zero, ty, .lt);
5963 const overflow_cmp = try cg.cmp(op_tmp, lhs, ty, cmp_op);
5964 break :blk try cg.cmp(rhs_is_neg, overflow_cmp, Type.u1, .neq);
5965 } else try cg.cmp(op_tmp, lhs, ty, cmp_op);
5966 var bit_tmp = try overflow_bit.toLocal(cg, Type.u1);
5967 defer bit_tmp.free(cg);
5968
5969 const result = try cg.allocStack(cg.typeOfIndex(inst));
60925970 const offset: u32 = @intCast(ty.abiSize(zcu));
6093 try func.store(result, op_tmp, ty, 0);
6094 try func.store(result, bit_tmp, Type.u1, offset);
5971 try cg.store(result, op_tmp, ty, 0);
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 });
60975975}
60985976
6099fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6100 const pt = func.pt;
5977fn airShlWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5978 const pt = cg.pt;
61015979 const zcu = pt.zcu;
6102 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6103 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
5980 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5981 const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;
61045982
6105 const lhs = try func.resolveInst(extra.lhs);
6106 const rhs = try func.resolveInst(extra.rhs);
6107 const ty = func.typeOf(extra.lhs);
6108 const rhs_ty = func.typeOf(extra.rhs);
5983 const lhs = try cg.resolveInst(extra.lhs);
5984 const rhs = try cg.resolveInst(extra.rhs);
5985 const ty = cg.typeOf(extra.lhs);
5986 const rhs_ty = cg.typeOf(extra.rhs);
61095987
61105988 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", .{});
61125990 }
61135991
61145992 const int_info = ty.intInfo(zcu);
61155993 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});
61175995 };
61185996
61195997 // 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 {
61215999 const rhs_wasm_bits = toWasmBits(rhs_ty.intInfo(zcu).bits).?;
61226000 // If wasm_bits == 128, compiler-rt expects i32 for shift
61236001 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);
6125 break :blk try rhs_casted.toLocal(func, ty);
6002 const rhs_casted = try cg.intcast(rhs, rhs_ty, ty);
6003 break :blk try rhs_casted.toLocal(cg, ty);
61266004 } else rhs;
61276005
6128 var shl = try (try func.wrapBinOp(lhs, rhs_final, ty, .shl)).toLocal(func, ty);
6129 defer shl.free(func);
6006 var shl = try (try cg.wrapBinOp(lhs, rhs_final, ty, .shl)).toLocal(cg, ty);
6007 defer shl.free(cg);
61306008
61316009 const overflow_bit = blk: {
6132 const shr = try func.binOp(shl, rhs_final, ty, .shr);
6133 break :blk try func.cmp(shr, lhs, ty, .neq);
6010 const shr = try cg.binOp(shl, rhs_final, ty, .shr);
6011 break :blk try cg.cmp(shr, lhs, ty, .neq);
61346012 };
6135 var overflow_local = try overflow_bit.toLocal(func, Type.u1);
6136 defer overflow_local.free(func);
6013 var overflow_local = try overflow_bit.toLocal(cg, Type.u1);
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));
61396017 const offset: u32 = @intCast(ty.abiSize(zcu));
6140 try func.store(result, shl, ty, 0);
6141 try func.store(result, overflow_local, Type.u1, offset);
6018 try cg.store(result, shl, ty, 0);
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 });
61446022}
61456023
6146fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6147 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6148 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
6024fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6025 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6026 const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;
61496027
6150 const lhs = try func.resolveInst(extra.lhs);
6151 const rhs = try func.resolveInst(extra.rhs);
6152 const ty = func.typeOf(extra.lhs);
6153 const pt = func.pt;
6028 const lhs = try cg.resolveInst(extra.lhs);
6029 const rhs = try cg.resolveInst(extra.rhs);
6030 const ty = cg.typeOf(extra.lhs);
6031 const pt = cg.pt;
61546032 const zcu = pt.zcu;
61556033
61566034 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", .{});
61586036 }
61596037
61606038 // We store the bit if it's overflowed or not in this. As it's zero-initialized
61616039 // we only need to update it if an overflow (or underflow) occurred.
6162 var overflow_bit = try func.ensureAllocLocal(Type.u1);
6163 defer overflow_bit.free(func);
6040 var overflow_bit = try cg.ensureAllocLocal(Type.u1);
6041 defer overflow_bit.free(cg);
61646042
61656043 const int_info = ty.intInfo(zcu);
61666044 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});
61686046 };
61696047
61706048 const zero: WValue = switch (wasm_bits) {
......@@ -6176,248 +6054,250 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61766054 // for 32 bit integers we upcast it to a 64bit integer
61776055 const mul = if (wasm_bits == 32) blk: {
61786056 const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64;
6179 const lhs_upcast = try func.intcast(lhs, ty, new_ty);
6180 const rhs_upcast = try func.intcast(rhs, ty, new_ty);
6181 const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty);
6182 const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty);
6183 const res_upcast = try func.intcast(res, ty, new_ty);
6184 _ = try func.cmp(res_upcast, bin_op, new_ty, .neq);
6185 try func.addLabel(.local_set, overflow_bit.local.value);
6057 const lhs_upcast = try cg.intcast(lhs, ty, new_ty);
6058 const rhs_upcast = try cg.intcast(rhs, ty, new_ty);
6059 const bin_op = try (try cg.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(cg, new_ty);
6060 const res = try (try cg.trunc(bin_op, ty, new_ty)).toLocal(cg, ty);
6061 const res_upcast = try cg.intcast(res, ty, new_ty);
6062 _ = try cg.cmp(res_upcast, bin_op, new_ty, .neq);
6063 try cg.addLocal(.local_set, overflow_bit.local.value);
61866064 break :blk res;
61876065 } else if (wasm_bits == 64) blk: {
61886066 const new_ty = if (int_info.signedness == .signed) Type.i128 else Type.u128;
6189 const lhs_upcast = try func.intcast(lhs, ty, new_ty);
6190 const rhs_upcast = try func.intcast(rhs, ty, new_ty);
6191 const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty);
6192 const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty);
6193 const res_upcast = try func.intcast(res, ty, new_ty);
6194 _ = try func.cmp(res_upcast, bin_op, new_ty, .neq);
6195 try func.addLabel(.local_set, overflow_bit.local.value);
6067 const lhs_upcast = try cg.intcast(lhs, ty, new_ty);
6068 const rhs_upcast = try cg.intcast(rhs, ty, new_ty);
6069 const bin_op = try (try cg.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(cg, new_ty);
6070 const res = try (try cg.trunc(bin_op, ty, new_ty)).toLocal(cg, ty);
6071 const res_upcast = try cg.intcast(res, ty, new_ty);
6072 _ = try cg.cmp(res_upcast, bin_op, new_ty, .neq);
6073 try cg.addLocal(.local_set, overflow_bit.local.value);
61966074 break :blk res;
61976075 } 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);
6199 defer lhs_lsb.free(func);
6200 var lhs_msb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
6201 defer lhs_msb.free(func);
6202 var rhs_lsb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
6203 defer rhs_lsb.free(func);
6204 var rhs_msb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);
6205 defer rhs_msb.free(func);
6206
6207 const cross_1 = try func.callIntrinsic(
6208 "__multi3",
6076 var lhs_lsb = try (try cg.load(lhs, Type.u64, 0)).toLocal(cg, Type.u64);
6077 defer lhs_lsb.free(cg);
6078 var lhs_msb = try (try cg.load(lhs, Type.u64, 8)).toLocal(cg, Type.u64);
6079 defer lhs_msb.free(cg);
6080 var rhs_lsb = try (try cg.load(rhs, Type.u64, 0)).toLocal(cg, Type.u64);
6081 defer rhs_lsb.free(cg);
6082 var rhs_msb = try (try cg.load(rhs, Type.u64, 8)).toLocal(cg, Type.u64);
6083 defer rhs_msb.free(cg);
6084
6085 const cross_1 = try cg.callIntrinsic(
6086 .__multi3,
62096087 &[_]InternPool.Index{.i64_type} ** 4,
62106088 Type.i128,
62116089 &.{ lhs_msb, zero, rhs_lsb, zero },
62126090 );
6213 const cross_2 = try func.callIntrinsic(
6214 "__multi3",
6091 const cross_2 = try cg.callIntrinsic(
6092 .__multi3,
62156093 &[_]InternPool.Index{.i64_type} ** 4,
62166094 Type.i128,
62176095 &.{ rhs_msb, zero, lhs_lsb, zero },
62186096 );
6219 const mul_lsb = try func.callIntrinsic(
6220 "__multi3",
6097 const mul_lsb = try cg.callIntrinsic(
6098 .__multi3,
62216099 &[_]InternPool.Index{.i64_type} ** 4,
62226100 Type.i128,
62236101 &.{ rhs_lsb, zero, lhs_lsb, zero },
62246102 );
62256103
6226 const rhs_msb_not_zero = try func.cmp(rhs_msb, zero, Type.u64, .neq);
6227 const lhs_msb_not_zero = try func.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");
6229 const cross_1_msb = try func.load(cross_1, Type.u64, 8);
6230 const cross_1_msb_not_zero = try func.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");
6232 const cross_2_msb = try func.load(cross_2, Type.u64, 8);
6233 const cross_2_msb_not_zero = try func.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");
6235
6236 const cross_1_lsb = try func.load(cross_1, Type.u64, 0);
6237 const cross_2_lsb = try func.load(cross_2, Type.u64, 0);
6238 const cross_add = try func.binOp(cross_1_lsb, cross_2_lsb, Type.u64, .add);
6239
6240 var mul_lsb_msb = try (try func.load(mul_lsb, Type.u64, 8)).toLocal(func, Type.u64);
6241 defer mul_lsb_msb.free(func);
6242 var all_add = try (try func.binOp(cross_add, mul_lsb_msb, Type.u64, .add)).toLocal(func, Type.u64);
6243 defer all_add.free(func);
6244 const add_overflow = try func.cmp(all_add, mul_lsb_msb, Type.u64, .lt);
6104 const rhs_msb_not_zero = try cg.cmp(rhs_msb, zero, Type.u64, .neq);
6105 const lhs_msb_not_zero = try cg.cmp(lhs_msb, zero, Type.u64, .neq);
6106 const both_msb_not_zero = try cg.binOp(rhs_msb_not_zero, lhs_msb_not_zero, Type.bool, .@"and");
6107 const cross_1_msb = try cg.load(cross_1, Type.u64, 8);
6108 const cross_1_msb_not_zero = try cg.cmp(cross_1_msb, zero, Type.u64, .neq);
6109 const cond_1 = try cg.binOp(both_msb_not_zero, cross_1_msb_not_zero, Type.bool, .@"or");
6110 const cross_2_msb = try cg.load(cross_2, Type.u64, 8);
6111 const cross_2_msb_not_zero = try cg.cmp(cross_2_msb, zero, Type.u64, .neq);
6112 const cond_2 = try cg.binOp(cond_1, cross_2_msb_not_zero, Type.bool, .@"or");
6113
6114 const cross_1_lsb = try cg.load(cross_1, Type.u64, 0);
6115 const cross_2_lsb = try cg.load(cross_2, Type.u64, 0);
6116 const cross_add = try cg.binOp(cross_1_lsb, cross_2_lsb, Type.u64, .add);
6117
6118 var mul_lsb_msb = try (try cg.load(mul_lsb, Type.u64, 8)).toLocal(cg, Type.u64);
6119 defer mul_lsb_msb.free(cg);
6120 var all_add = try (try cg.binOp(cross_add, mul_lsb_msb, Type.u64, .add)).toLocal(cg, Type.u64);
6121 defer all_add.free(cg);
6122 const add_overflow = try cg.cmp(all_add, mul_lsb_msb, Type.u64, .lt);
62456123
62466124 // result for overflow bit
6247 _ = try func.binOp(cond_2, add_overflow, Type.bool, .@"or");
6248 try func.addLabel(.local_set, overflow_bit.local.value);
6249
6250 const tmp_result = try func.allocStack(Type.u128);
6251 try func.emitWValue(tmp_result);
6252 const mul_lsb_lsb = try func.load(mul_lsb, Type.u64, 0);
6253 try func.store(.stack, mul_lsb_lsb, Type.u64, tmp_result.offset());
6254 try func.store(tmp_result, all_add, Type.u64, 8);
6125 _ = try cg.binOp(cond_2, add_overflow, Type.bool, .@"or");
6126 try cg.addLocal(.local_set, overflow_bit.local.value);
6127
6128 const tmp_result = try cg.allocStack(Type.u128);
6129 try cg.emitWValue(tmp_result);
6130 const mul_lsb_lsb = try cg.load(mul_lsb, Type.u64, 0);
6131 try cg.store(.stack, mul_lsb_lsb, Type.u64, tmp_result.offset());
6132 try cg.store(tmp_result, all_add, Type.u64, 8);
62556133 break :blk tmp_result;
62566134 } else if (int_info.bits == 128 and int_info.signedness == .signed) blk: {
6257 const overflow_ret = try func.allocStack(Type.i32);
6258 const res = try func.callIntrinsic(
6259 "__muloti4",
6135 const overflow_ret = try cg.allocStack(Type.i32);
6136 const res = try cg.callIntrinsic(
6137 .__muloti4,
62606138 &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type },
62616139 Type.i128,
62626140 &.{ lhs, rhs, overflow_ret },
62636141 );
6264 _ = try func.load(overflow_ret, Type.i32, 0);
6265 try func.addLabel(.local_set, overflow_bit.local.value);
6142 _ = try cg.load(overflow_ret, Type.i32, 0);
6143 try cg.addLocal(.local_set, overflow_bit.local.value);
62666144 break :blk res;
6267 } else return func.fail("TODO: @mulWithOverflow for {}", .{ty.fmt(pt)});
6268 var bin_op_local = try mul.toLocal(func, ty);
6269 defer bin_op_local.free(func);
6145 } else return cg.fail("TODO: @mulWithOverflow for {}", .{ty.fmt(pt)});
6146 var bin_op_local = try mul.toLocal(cg, ty);
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));
62726150 const offset: u32 = @intCast(ty.abiSize(zcu));
6273 try func.store(result, bin_op_local, ty, 0);
6274 try func.store(result, overflow_bit, Type.u1, offset);
6151 try cg.store(result, bin_op_local, ty, 0);
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 });
62776155}
62786156
6279fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
6280 assert(op == .max or op == .min);
6281 const pt = func.pt;
6282 const zcu = pt.zcu;
6283 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6157fn airMaxMin(
6158 cg: *CodeGen,
6159 inst: Air.Inst.Index,
6160 op: enum { fmax, fmin },
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);
62866167 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", .{});
62886169 }
62896170
62906171 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", .{});
62926173 }
62936174
6294 const lhs = try func.resolveInst(bin_op.lhs);
6295 const rhs = try func.resolveInst(bin_op.rhs);
6175 const lhs = try cg.resolveInst(bin_op.lhs);
6176 const rhs = try cg.resolveInst(bin_op.rhs);
62966177
62976178 if (ty.zigTypeTag(zcu) == .float) {
6298 var fn_name_buf: [64]u8 = undefined;
6299 const float_bits = ty.floatBits(func.target.*);
6300 const fn_name = std.fmt.bufPrint(&fn_name_buf, "{s}f{s}{s}", .{
6301 target_util.libcFloatPrefix(float_bits),
6302 @tagName(op),
6303 target_util.libcFloatSuffix(float_bits),
6304 }) catch unreachable;
6305 const result = try func.callIntrinsic(fn_name, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs });
6306 try func.lowerToStack(result);
6179 const intrinsic = switch (op) {
6180 inline .fmin, .fmax => |ct_op| switch (ty.floatBits(cg.target.*)) {
6181 inline 16, 32, 64, 80, 128 => |bits| @field(
6182 Mir.Intrinsic,
6183 libcFloatPrefix(bits) ++ @tagName(ct_op) ++ libcFloatSuffix(bits),
6184 ),
6185 else => unreachable,
6186 },
6187 };
6188 const result = try cg.callIntrinsic(intrinsic, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs });
6189 try cg.lowerToStack(result);
63076190 } else {
63086191 // operands to select from
6309 try func.lowerToStack(lhs);
6310 try func.lowerToStack(rhs);
6311 _ = try func.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt);
6192 try cg.lowerToStack(lhs);
6193 try cg.lowerToStack(rhs);
6194 _ = try cg.cmp(lhs, rhs, ty, cmp_op);
63126195
63136196 // based on the result from comparison, return operand 0 or 1.
6314 try func.addTag(.select);
6197 try cg.addTag(.select);
63156198 }
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 });
63186201}
63196202
6320fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6321 const pt = func.pt;
6322 const zcu = pt.zcu;
6323 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6324 const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data;
6203fn airMulAdd(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6204 const zcu = cg.pt.zcu;
6205 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6206 const bin_op = cg.air.extraData(Air.Bin, pl_op.payload).data;
63256207
6326 const ty = func.typeOfIndex(inst);
6208 const ty = cg.typeOfIndex(inst);
63276209 if (ty.zigTypeTag(zcu) == .vector) {
6328 return func.fail("TODO: `@mulAdd` for vectors", .{});
6210 return cg.fail("TODO: `@mulAdd` for vectors", .{});
63296211 }
63306212
6331 const addend = try func.resolveInst(pl_op.operand);
6332 const lhs = try func.resolveInst(bin_op.lhs);
6333 const rhs = try func.resolveInst(bin_op.rhs);
6213 const addend = try cg.resolveInst(pl_op.operand);
6214 const lhs = try cg.resolveInst(bin_op.lhs);
6215 const rhs = try cg.resolveInst(bin_op.rhs);
63346216
6335 const result = if (ty.floatBits(func.target.*) == 16) fl_result: {
6336 const rhs_ext = try func.fpext(rhs, ty, Type.f32);
6337 const lhs_ext = try func.fpext(lhs, ty, Type.f32);
6338 const addend_ext = try func.fpext(addend, ty, Type.f32);
6217 const result = if (ty.floatBits(cg.target.*) == 16) fl_result: {
6218 const rhs_ext = try cg.fpext(rhs, ty, Type.f32);
6219 const lhs_ext = try cg.fpext(lhs, ty, Type.f32);
6220 const addend_ext = try cg.fpext(addend, ty, Type.f32);
63396221 // call to compiler-rt `fn fmaf(f32, f32, f32) f32`
6340 const result = try func.callIntrinsic(
6341 "fmaf",
6222 const result = try cg.callIntrinsic(
6223 .fmaf,
63426224 &.{ .f32_type, .f32_type, .f32_type },
63436225 Type.f32,
63446226 &.{ rhs_ext, lhs_ext, addend_ext },
63456227 );
6346 break :fl_result try func.fptrunc(result, Type.f32, ty);
6228 break :fl_result try cg.fptrunc(result, Type.f32, ty);
63476229 } else result: {
6348 const mul_result = try func.binOp(lhs, rhs, ty, .mul);
6349 break :result try func.binOp(mul_result, addend, ty, .add);
6230 const mul_result = try cg.binOp(lhs, rhs, ty, .mul);
6231 break :result try cg.binOp(mul_result, addend, ty, .add);
63506232 };
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 });
63536235}
63546236
6355fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6356 const pt = func.pt;
6357 const zcu = pt.zcu;
6358 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6237fn airClz(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6238 const zcu = cg.pt.zcu;
6239 const ty_op = cg.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);
63616242 if (ty.zigTypeTag(zcu) == .vector) {
6362 return func.fail("TODO: `@clz` for vectors", .{});
6243 return cg.fail("TODO: `@clz` for vectors", .{});
63636244 }
63646245
6365 const operand = try func.resolveInst(ty_op.operand);
6246 const operand = try cg.resolveInst(ty_op.operand);
63666247 const int_info = ty.intInfo(zcu);
63676248 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});
63696250 };
63706251
63716252 switch (wasm_bits) {
63726253 32 => {
6373 try func.emitWValue(operand);
6374 try func.addTag(.i32_clz);
6254 try cg.emitWValue(operand);
6255 try cg.addTag(.i32_clz);
63756256 },
63766257 64 => {
6377 try func.emitWValue(operand);
6378 try func.addTag(.i64_clz);
6379 try func.addTag(.i32_wrap_i64);
6258 try cg.emitWValue(operand);
6259 try cg.addTag(.i64_clz);
6260 try cg.addTag(.i32_wrap_i64);
63806261 },
63816262 128 => {
6382 var msb = try (try func.load(operand, Type.u64, 8)).toLocal(func, Type.u64);
6383 defer msb.free(func);
6384
6385 try func.emitWValue(msb);
6386 try func.addTag(.i64_clz);
6387 _ = try func.load(operand, Type.u64, 0);
6388 try func.addTag(.i64_clz);
6389 try func.emitWValue(.{ .imm64 = 64 });
6390 try func.addTag(.i64_add);
6391 _ = try func.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq);
6392 try func.addTag(.select);
6393 try func.addTag(.i32_wrap_i64);
6263 var msb = try (try cg.load(operand, Type.u64, 8)).toLocal(cg, Type.u64);
6264 defer msb.free(cg);
6265
6266 try cg.emitWValue(msb);
6267 try cg.addTag(.i64_clz);
6268 _ = try cg.load(operand, Type.u64, 0);
6269 try cg.addTag(.i64_clz);
6270 try cg.emitWValue(.{ .imm64 = 64 });
6271 try cg.addTag(.i64_add);
6272 _ = try cg.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq);
6273 try cg.addTag(.select);
6274 try cg.addTag(.i32_wrap_i64);
63946275 },
63956276 else => unreachable,
63966277 }
63976278
63986279 if (wasm_bits != int_info.bits) {
6399 try func.emitWValue(.{ .imm32 = wasm_bits - int_info.bits });
6400 try func.addTag(.i32_sub);
6280 try cg.emitWValue(.{ .imm32 = wasm_bits - int_info.bits });
6281 try cg.addTag(.i32_sub);
64016282 }
64026283
6403 return func.finishAir(inst, .stack, &.{ty_op.operand});
6284 return cg.finishAir(inst, .stack, &.{ty_op.operand});
64046285}
64056286
6406fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6407 const pt = func.pt;
6408 const zcu = pt.zcu;
6409 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6287fn airCtz(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6288 const zcu = cg.pt.zcu;
6289 const ty_op = cg.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
64136293 if (ty.zigTypeTag(zcu) == .vector) {
6414 return func.fail("TODO: `@ctz` for vectors", .{});
6294 return cg.fail("TODO: `@ctz` for vectors", .{});
64156295 }
64166296
6417 const operand = try func.resolveInst(ty_op.operand);
6297 const operand = try cg.resolveInst(ty_op.operand);
64186298 const int_info = ty.intInfo(zcu);
64196299 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});
64216301 };
64226302
64236303 switch (wasm_bits) {
......@@ -6425,131 +6305,108 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
64256305 if (wasm_bits != int_info.bits) {
64266306 const val: u32 = @as(u32, 1) << @as(u5, @intCast(int_info.bits));
64276307 // leave value on the stack
6428 _ = try func.binOp(operand, .{ .imm32 = val }, ty, .@"or");
6429 } else try func.emitWValue(operand);
6430 try func.addTag(.i32_ctz);
6308 _ = try cg.binOp(operand, .{ .imm32 = val }, ty, .@"or");
6309 } else try cg.emitWValue(operand);
6310 try cg.addTag(.i32_ctz);
64316311 },
64326312 64 => {
64336313 if (wasm_bits != int_info.bits) {
64346314 const val: u64 = @as(u64, 1) << @as(u6, @intCast(int_info.bits));
64356315 // leave value on the stack
6436 _ = try func.binOp(operand, .{ .imm64 = val }, ty, .@"or");
6437 } else try func.emitWValue(operand);
6438 try func.addTag(.i64_ctz);
6439 try func.addTag(.i32_wrap_i64);
6316 _ = try cg.binOp(operand, .{ .imm64 = val }, ty, .@"or");
6317 } else try cg.emitWValue(operand);
6318 try cg.addTag(.i64_ctz);
6319 try cg.addTag(.i32_wrap_i64);
64406320 },
64416321 128 => {
6442 var lsb = try (try func.load(operand, Type.u64, 0)).toLocal(func, Type.u64);
6443 defer lsb.free(func);
6322 var lsb = try (try cg.load(operand, Type.u64, 0)).toLocal(cg, Type.u64);
6323 defer lsb.free(cg);
64446324
6445 try func.emitWValue(lsb);
6446 try func.addTag(.i64_ctz);
6447 _ = try func.load(operand, Type.u64, 8);
6325 try cg.emitWValue(lsb);
6326 try cg.addTag(.i64_ctz);
6327 _ = try cg.load(operand, Type.u64, 8);
64486328 if (wasm_bits != int_info.bits) {
6449 try func.addImm64(@as(u64, 1) << @as(u6, @intCast(int_info.bits - 64)));
6450 try func.addTag(.i64_or);
6329 try cg.addImm64(@as(u64, 1) << @as(u6, @intCast(int_info.bits - 64)));
6330 try cg.addTag(.i64_or);
64516331 }
6452 try func.addTag(.i64_ctz);
6453 try func.addImm64(64);
6332 try cg.addTag(.i64_ctz);
6333 try cg.addImm64(64);
64546334 if (wasm_bits != int_info.bits) {
6455 try func.addTag(.i64_or);
6335 try cg.addTag(.i64_or);
64566336 } else {
6457 try func.addTag(.i64_add);
6337 try cg.addTag(.i64_add);
64586338 }
6459 _ = try func.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq);
6460 try func.addTag(.select);
6461 try func.addTag(.i32_wrap_i64);
6339 _ = try cg.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq);
6340 try cg.addTag(.select);
6341 try cg.addTag(.i32_wrap_i64);
64626342 },
64636343 else => unreachable,
64646344 }
64656345
6466 return func.finishAir(inst, .stack, &.{ty_op.operand});
6346 return cg.finishAir(inst, .stack, &.{ty_op.operand});
64676347}
64686348
6469fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6470 if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});
6471
6472 const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
6473 try func.addInst(.{ .tag = .dbg_line, .data = .{
6474 .payload = try func.addExtra(Mir.DbgLineColumn{
6349fn airDbgStmt(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6350 const dbg_stmt = cg.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
6351 try cg.addInst(.{ .tag = .dbg_line, .data = .{
6352 .payload = try cg.addExtra(Mir.DbgLineColumn{
64756353 .line = dbg_stmt.line,
64766354 .column = dbg_stmt.column,
64776355 }),
64786356 } });
6479 return func.finishAir(inst, .none, &.{});
6357 return cg.finishAir(inst, .none, &.{});
64806358}
64816359
6482fn airDbgInlineBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6483 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6484 const extra = func.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
6360fn airDbgInlineBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6361 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6362 const extra = cg.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
64856363 // 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]));
64876365}
64886366
64896367fn airDbgVar(
6490 func: *CodeGen,
6368 cg: *CodeGen,
64916369 inst: Air.Inst.Index,
64926370 local_tag: link.File.Dwarf.WipNav.LocalTag,
64936371 is_ptr: bool,
64946372) InnerError!void {
64956373 _ = is_ptr;
6496 if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});
6497
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, &.{});
6374 _ = local_tag;
6375 return cg.finishAir(inst, .none, &.{});
65176376}
65186377
6519fn airTry(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6520 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6521 const err_union = try func.resolveInst(pl_op.operand);
6522 const extra = func.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]);
6524 const err_union_ty = func.typeOf(pl_op.operand);
6525 const result = try lowerTry(func, inst, err_union, body, err_union_ty, false);
6526 return func.finishAir(inst, result, &.{pl_op.operand});
6378fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6379 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6380 const err_union = try cg.resolveInst(pl_op.operand);
6381 const extra = cg.air.extraData(Air.Try, pl_op.payload);
6382 const body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len]);
6383 const err_union_ty = cg.typeOf(pl_op.operand);
6384 const result = try lowerTry(cg, inst, err_union, body, err_union_ty, false);
6385 return cg.finishAir(inst, result, &.{pl_op.operand});
65276386}
65286387
6529fn airTryPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6530 const pt = func.pt;
6531 const zcu = pt.zcu;
6532 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6533 const extra = func.air.extraData(Air.TryPtr, ty_pl.payload);
6534 const err_union_ptr = try func.resolveInst(extra.data.ptr);
6535 const body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]);
6536 const err_union_ty = func.typeOf(extra.data.ptr).childType(zcu);
6537 const result = try lowerTry(func, inst, err_union_ptr, body, err_union_ty, true);
6538 return func.finishAir(inst, result, &.{extra.data.ptr});
6388fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6389 const zcu = cg.pt.zcu;
6390 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6391 const extra = cg.air.extraData(Air.TryPtr, ty_pl.payload);
6392 const err_union_ptr = try cg.resolveInst(extra.data.ptr);
6393 const body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len]);
6394 const err_union_ty = cg.typeOf(extra.data.ptr).childType(zcu);
6395 const result = try lowerTry(cg, inst, err_union_ptr, body, err_union_ty, true);
6396 return cg.finishAir(inst, result, &.{extra.data.ptr});
65396397}
65406398
65416399fn lowerTry(
6542 func: *CodeGen,
6400 cg: *CodeGen,
65436401 inst: Air.Inst.Index,
65446402 err_union: WValue,
65456403 body: []const Air.Inst.Index,
65466404 err_union_ty: Type,
65476405 operand_is_ptr: bool,
65486406) InnerError!WValue {
6549 const pt = func.pt;
6550 const zcu = pt.zcu;
6407 const zcu = cg.pt.zcu;
65516408 if (operand_is_ptr) {
6552 return func.fail("TODO: lowerTry for pointers", .{});
6409 return cg.fail("TODO: lowerTry for pointers", .{});
65536410 }
65546411
65556412 const pl_ty = err_union_ty.errorUnionPayload(zcu);
......@@ -6557,29 +6414,29 @@ fn lowerTry(
65576414
65586415 if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
65596416 // 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
65626419 // check if the error tag is set for the error union.
6563 try func.emitWValue(err_union);
6420 try cg.emitWValue(err_union);
65646421 if (pl_has_bits) {
65656422 const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
6566 try func.addMemArg(.i32_load16_u, .{
6423 try cg.addMemArg(.i32_load16_u, .{
65676424 .offset = err_union.offset() + err_offset,
65686425 .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),
65696426 });
65706427 }
6571 try func.addTag(.i32_eqz);
6572 try func.addLabel(.br_if, 0); // jump out of block when error is '0'
6428 try cg.addTag(.i32_eqz);
6429 try cg.addLabel(.br_if, 0); // jump out of block when error is '0'
65736430
6574 const liveness = func.liveness.getCondBr(inst);
6575 try func.branches.append(func.gpa, .{});
6576 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.else_deaths.len + liveness.then_deaths.len);
6431 const liveness = cg.liveness.getCondBr(inst);
6432 try cg.branches.append(cg.gpa, .{});
6433 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, liveness.else_deaths.len + liveness.then_deaths.len);
65776434 defer {
6578 var branch = func.branches.pop();
6579 branch.deinit(func.gpa);
6435 var branch = cg.branches.pop();
6436 branch.deinit(cg.gpa);
65806437 }
6581 try func.genBody(body);
6582 try func.endBlock();
6438 try cg.genBody(body);
6439 try cg.endBlock();
65836440 }
65846441
65856442 // if we reach here it means error was not set, and we want the payload
......@@ -6588,39 +6445,38 @@ fn lowerTry(
65886445 }
65896446
65906447 const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));
6591 if (isByRef(pl_ty, pt, func.target.*)) {
6592 return buildPointerOffset(func, err_union, pl_offset, .new);
6448 if (isByRef(pl_ty, zcu, cg.target)) {
6449 return buildPointerOffset(cg, err_union, pl_offset, .new);
65936450 }
6594 const payload = try func.load(err_union, pl_ty, pl_offset);
6595 return payload.toLocal(func, pl_ty);
6451 const payload = try cg.load(err_union, pl_ty, pl_offset);
6452 return payload.toLocal(cg, pl_ty);
65966453}
65976454
6598fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6599 const pt = func.pt;
6600 const zcu = pt.zcu;
6601 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6455fn airByteSwap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6456 const zcu = cg.pt.zcu;
6457 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
66026458
6603 const ty = func.typeOfIndex(inst);
6604 const operand = try func.resolveInst(ty_op.operand);
6459 const ty = cg.typeOfIndex(inst);
6460 const operand = try cg.resolveInst(ty_op.operand);
66056461
66066462 if (ty.zigTypeTag(zcu) == .vector) {
6607 return func.fail("TODO: @byteSwap for vectors", .{});
6463 return cg.fail("TODO: @byteSwap for vectors", .{});
66086464 }
66096465 const int_info = ty.intInfo(zcu);
66106466 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});
66126468 };
66136469
66146470 // bytes are no-op
66156471 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});
66176473 }
66186474
66196475 const result = result: {
66206476 switch (wasm_bits) {
66216477 32 => {
6622 const intrin_ret = try func.callIntrinsic(
6623 "__bswapsi2",
6478 const intrin_ret = try cg.callIntrinsic(
6479 .__bswapsi2,
66246480 &.{.u32_type},
66256481 Type.u32,
66266482 &.{operand},
......@@ -6628,11 +6484,11 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
66286484 break :result if (int_info.bits == 32)
66296485 intrin_ret
66306486 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);
66326488 },
66336489 64 => {
6634 const intrin_ret = try func.callIntrinsic(
6635 "__bswapdi2",
6490 const intrin_ret = try cg.callIntrinsic(
6491 .__bswapdi2,
66366492 &.{.u64_type},
66376493 Type.u64,
66386494 &.{operand},
......@@ -6640,61 +6496,60 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
66406496 break :result if (int_info.bits == 64)
66416497 intrin_ret
66426498 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);
66446500 },
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}),
66466502 }
66476503 };
6648 return func.finishAir(inst, result, &.{ty_op.operand});
6504 return cg.finishAir(inst, result, &.{ty_op.operand});
66496505}
66506506
6651fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6652 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6507fn airDiv(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6508 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
66536509
6654 const ty = func.typeOfIndex(inst);
6655 const lhs = try func.resolveInst(bin_op.lhs);
6656 const rhs = try func.resolveInst(bin_op.rhs);
6510 const ty = cg.typeOfIndex(inst);
6511 const lhs = try cg.resolveInst(bin_op.lhs);
6512 const rhs = try cg.resolveInst(bin_op.rhs);
66576513
6658 const result = try func.binOp(lhs, rhs, ty, .div);
6659 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
6514 const result = try cg.binOp(lhs, rhs, ty, .div);
6515 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
66606516}
66616517
6662fn airDivTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6663 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6518fn airDivTrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6519 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
66646520
6665 const ty = func.typeOfIndex(inst);
6666 const lhs = try func.resolveInst(bin_op.lhs);
6667 const rhs = try func.resolveInst(bin_op.rhs);
6521 const ty = cg.typeOfIndex(inst);
6522 const lhs = try cg.resolveInst(bin_op.lhs);
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
66716527 if (ty.isAnyFloat()) {
6672 const trunc_result = try func.floatOp(.trunc, ty, &.{div_result});
6673 return func.finishAir(inst, trunc_result, &.{ bin_op.lhs, bin_op.rhs });
6528 const trunc_result = try cg.floatOp(.trunc, ty, &.{div_result});
6529 return cg.finishAir(inst, trunc_result, &.{ bin_op.lhs, bin_op.rhs });
66746530 }
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 });
66776533}
66786534
6679fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6680 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6535fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6536 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
66816537
6682 const pt = func.pt;
6683 const zcu = pt.zcu;
6684 const ty = func.typeOfIndex(inst);
6685 const lhs = try func.resolveInst(bin_op.lhs);
6686 const rhs = try func.resolveInst(bin_op.rhs);
6538 const zcu = cg.pt.zcu;
6539 const ty = cg.typeOfIndex(inst);
6540 const lhs = try cg.resolveInst(bin_op.lhs);
6541 const rhs = try cg.resolveInst(bin_op.rhs);
66876542
66886543 if (ty.isUnsignedInt(zcu)) {
6689 _ = try func.binOp(lhs, rhs, ty, .div);
6544 _ = try cg.binOp(lhs, rhs, ty, .div);
66906545 } else if (ty.isSignedInt(zcu)) {
66916546 const int_bits = ty.intInfo(zcu).bits;
66926547 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});
66946549 };
66956550
66966551 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});
66986553 }
66996554
67006555 const zero: WValue = switch (wasm_bits) {
......@@ -6704,108 +6559,108 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
67046559 };
67056560
67066561 // tee leaves the value on the stack and stores it in a local.
6707 const quotient = try func.allocLocal(ty);
6708 _ = try func.binOp(lhs, rhs, ty, .div);
6709 try func.addLabel(.local_tee, quotient.local.value);
6562 const quotient = try cg.allocLocal(ty);
6563 _ = try cg.binOp(lhs, rhs, ty, .div);
6564 try cg.addLocal(.local_tee, quotient.local.value);
67106565
67116566 // select takes a 32 bit value as the condition, so in the 64 bit case we use eqz to narrow
67126567 // the 64 bit value we want to use as the condition to 32 bits.
67136568 // This also inverts the condition (non 0 => 0, 0 => 1), so we put the adjusted and
67146569 // non-adjusted quotients on the stack in the opposite order for 32 vs 64 bits.
67156570 if (wasm_bits == 64) {
6716 try func.emitWValue(quotient);
6571 try cg.emitWValue(quotient);
67176572 }
67186573
67196574 // 0 if the signs of rhs_wasm and lhs_wasm are the same, 1 otherwise.
6720 _ = try func.binOp(lhs, rhs, ty, .xor);
6721 _ = try func.cmp(.stack, zero, ty, .lt);
6575 _ = try cg.binOp(lhs, rhs, ty, .xor);
6576 _ = try cg.cmp(.stack, zero, ty, .lt);
67226577
67236578 switch (wasm_bits) {
67246579 32 => {
6725 try func.addTag(.i32_sub);
6726 try func.emitWValue(quotient);
6580 try cg.addTag(.i32_sub);
6581 try cg.emitWValue(quotient);
67276582 },
67286583 64 => {
6729 try func.addTag(.i64_extend_i32_u);
6730 try func.addTag(.i64_sub);
6584 try cg.addTag(.i64_extend_i32_u);
6585 try cg.addTag(.i64_sub);
67316586 },
67326587 else => unreachable,
67336588 }
67346589
6735 _ = try func.binOp(lhs, rhs, ty, .rem);
6590 _ = try cg.binOp(lhs, rhs, ty, .rem);
67366591
67376592 if (wasm_bits == 64) {
6738 try func.addTag(.i64_eqz);
6593 try cg.addTag(.i64_eqz);
67396594 }
67406595
6741 try func.addTag(.select);
6596 try cg.addTag(.select);
67426597
67436598 // We need to zero the high bits because N bit comparisons consider all 32 or 64 bits, and
67446599 // expect all but the lowest N bits to be 0.
67456600 // TODO: Should we be zeroing the high bits here or should we be ignoring the high bits
67466601 // when performing comparisons?
67476602 if (int_bits != wasm_bits) {
6748 _ = try func.wrapOperand(.stack, ty);
6603 _ = try cg.wrapOperand(.stack, ty);
67496604 }
67506605 } else {
6751 const float_bits = ty.floatBits(func.target.*);
6606 const float_bits = ty.floatBits(cg.target.*);
67526607 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});
67546609 }
67556610 const is_f16 = float_bits == 16;
67566611
6757 const lhs_wasm = if (is_f16) try func.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;
6612 const lhs_wasm = if (is_f16) try cg.fpext(lhs, Type.f16, Type.f32) else lhs;
6613 const rhs_wasm = if (is_f16) try cg.fpext(rhs, Type.f16, Type.f32) else rhs;
67596614
6760 try func.emitWValue(lhs_wasm);
6761 try func.emitWValue(rhs_wasm);
6615 try cg.emitWValue(lhs_wasm);
6616 try cg.emitWValue(rhs_wasm);
67626617
67636618 switch (float_bits) {
67646619 16, 32 => {
6765 try func.addTag(.f32_div);
6766 try func.addTag(.f32_floor);
6620 try cg.addTag(.f32_div);
6621 try cg.addTag(.f32_floor);
67676622 },
67686623 64 => {
6769 try func.addTag(.f64_div);
6770 try func.addTag(.f64_floor);
6624 try cg.addTag(.f64_div);
6625 try cg.addTag(.f64_floor);
67716626 },
67726627 else => unreachable,
67736628 }
67746629
67756630 if (is_f16) {
6776 _ = try func.fptrunc(.stack, Type.f32, Type.f16);
6631 _ = try cg.fptrunc(.stack, Type.f32, Type.f16);
67776632 }
67786633 }
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 });
67816636}
67826637
6783fn airRem(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6784 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6638fn airRem(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6639 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
67856640
6786 const ty = func.typeOfIndex(inst);
6787 const lhs = try func.resolveInst(bin_op.lhs);
6788 const rhs = try func.resolveInst(bin_op.rhs);
6641 const ty = cg.typeOfIndex(inst);
6642 const lhs = try cg.resolveInst(bin_op.lhs);
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 });
67936648}
67946649
67956650/// Remainder after floor division, defined by:
67966651/// @divFloor(a, b) * b + @mod(a, b) = a
6797fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6798 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6652fn airMod(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6653 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
67996654
6800 const pt = func.pt;
6655 const pt = cg.pt;
68016656 const zcu = pt.zcu;
6802 const ty = func.typeOfIndex(inst);
6803 const lhs = try func.resolveInst(bin_op.lhs);
6804 const rhs = try func.resolveInst(bin_op.rhs);
6657 const ty = cg.typeOfIndex(inst);
6658 const lhs = try cg.resolveInst(bin_op.lhs);
6659 const rhs = try cg.resolveInst(bin_op.rhs);
68056660
68066661 const result = result: {
68076662 if (ty.isUnsignedInt(zcu)) {
6808 break :result try func.binOp(lhs, rhs, ty, .rem);
6663 break :result try cg.binOp(lhs, rhs, ty, .rem);
68096664 }
68106665 if (ty.isSignedInt(zcu)) {
68116666 // 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 {
68146669 // @mod(a, b) = @rem(@rem(a, b) + b, b)
68156670 const int_bits = ty.intInfo(zcu).bits;
68166671 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});
68186673 };
68196674
68206675 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});
68226677 }
68236678
6824 _ = try func.binOp(lhs, rhs, ty, .rem);
6825 _ = try func.binOp(.stack, rhs, ty, .add);
6826 break :result try func.binOp(.stack, rhs, ty, .rem);
6679 _ = try cg.binOp(lhs, rhs, ty, .rem);
6680 _ = try cg.binOp(.stack, rhs, ty, .add);
6681 break :result try cg.binOp(.stack, rhs, ty, .rem);
68276682 }
68286683 if (ty.isAnyFloat()) {
6829 const rem = try func.binOp(lhs, rhs, ty, .rem);
6830 const add = try func.binOp(rem, rhs, ty, .add);
6831 break :result try func.binOp(add, rhs, ty, .rem);
6684 const rem = try cg.binOp(lhs, rhs, ty, .rem);
6685 const add = try cg.binOp(rem, rhs, ty, .add);
6686 break :result try cg.binOp(add, rhs, ty, .rem);
68326687 }
6833 return func.fail("TODO: @mod for {}", .{ty.fmt(pt)});
6688 return cg.fail("TODO: @mod for {}", .{ty.fmt(pt)});
68346689 };
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 });
68376692}
68386693
6839fn airSatMul(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6840 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6694fn airSatMul(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6695 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
68416696
6842 const pt = func.pt;
6697 const pt = cg.pt;
68436698 const zcu = pt.zcu;
6844 const ty = func.typeOfIndex(inst);
6699 const ty = cg.typeOfIndex(inst);
68456700 const int_info = ty.intInfo(zcu);
68466701 const is_signed = int_info.signedness == .signed;
68476702
6848 const lhs = try func.resolveInst(bin_op.lhs);
6849 const rhs = try func.resolveInst(bin_op.rhs);
6703 const lhs = try cg.resolveInst(bin_op.lhs);
6704 const rhs = try cg.resolveInst(bin_op.rhs);
68506705 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)});
68526707 };
68536708
68546709 switch (wasm_bits) {
68556710 32 => {
68566711 const upcast_ty: Type = if (is_signed) Type.i64 else Type.u64;
6857 const lhs_up = try func.intcast(lhs, ty, upcast_ty);
6858 const rhs_up = try func.intcast(rhs, ty, upcast_ty);
6859 var mul_res = try (try func.binOp(lhs_up, rhs_up, upcast_ty, .mul)).toLocal(func, upcast_ty);
6860 defer mul_res.free(func);
6712 const lhs_up = try cg.intcast(lhs, ty, upcast_ty);
6713 const rhs_up = try cg.intcast(rhs, ty, upcast_ty);
6714 var mul_res = try (try cg.binOp(lhs_up, rhs_up, upcast_ty, .mul)).toLocal(cg, upcast_ty);
6715 defer mul_res.free(cg);
68616716 if (is_signed) {
68626717 const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - (int_info.bits - 1)) };
6863 try func.emitWValue(mul_res);
6864 try func.emitWValue(imm_max);
6865 _ = try func.cmp(mul_res, imm_max, upcast_ty, .lt);
6866 try func.addTag(.select);
6718 try cg.emitWValue(mul_res);
6719 try cg.emitWValue(imm_max);
6720 _ = try cg.cmp(mul_res, imm_max, upcast_ty, .lt);
6721 try cg.addTag(.select);
68676722
6868 var tmp = try func.allocLocal(upcast_ty);
6869 defer tmp.free(func);
6870 try func.addLabel(.local_set, tmp.local.value);
6723 var tmp = try cg.allocLocal(upcast_ty);
6724 defer tmp.free(cg);
6725 try cg.addLocal(.local_set, tmp.local.value);
68716726
68726727 const imm_min: WValue = .{ .imm64 = ~@as(u64, 0) << @intCast(int_info.bits - 1) };
6873 try func.emitWValue(tmp);
6874 try func.emitWValue(imm_min);
6875 _ = try func.cmp(tmp, imm_min, upcast_ty, .gt);
6876 try func.addTag(.select);
6728 try cg.emitWValue(tmp);
6729 try cg.emitWValue(imm_min);
6730 _ = try cg.cmp(tmp, imm_min, upcast_ty, .gt);
6731 try cg.addTag(.select);
68776732 } else {
68786733 const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_info.bits) };
6879 try func.emitWValue(mul_res);
6880 try func.emitWValue(imm_max);
6881 _ = try func.cmp(mul_res, imm_max, upcast_ty, .lt);
6882 try func.addTag(.select);
6734 try cg.emitWValue(mul_res);
6735 try cg.emitWValue(imm_max);
6736 _ = try cg.cmp(mul_res, imm_max, upcast_ty, .lt);
6737 try cg.addTag(.select);
68836738 }
6884 try func.addTag(.i32_wrap_i64);
6739 try cg.addTag(.i32_wrap_i64);
68856740 },
68866741 64 => {
68876742 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)});
68896744 }
6890 const overflow_ret = try func.allocStack(Type.i32);
6891 _ = try func.callIntrinsic(
6892 "__mulodi4",
6745 const overflow_ret = try cg.allocStack(Type.i32);
6746 _ = try cg.callIntrinsic(
6747 .__mulodi4,
68936748 &[_]InternPool.Index{ .i64_type, .i64_type, .usize_type },
68946749 Type.i64,
68956750 &.{ lhs, rhs, overflow_ret },
68966751 );
6897 const xor = try func.binOp(lhs, rhs, Type.i64, .xor);
6898 const sign_v = try func.binOp(xor, .{ .imm64 = 63 }, Type.i64, .shr);
6899 _ = try func.binOp(sign_v, .{ .imm64 = ~@as(u63, 0) }, Type.i64, .xor);
6900 _ = try func.load(overflow_ret, Type.i32, 0);
6901 try func.addTag(.i32_eqz);
6902 try func.addTag(.select);
6752 const xor = try cg.binOp(lhs, rhs, Type.i64, .xor);
6753 const sign_v = try cg.binOp(xor, .{ .imm64 = 63 }, Type.i64, .shr);
6754 _ = try cg.binOp(sign_v, .{ .imm64 = ~@as(u63, 0) }, Type.i64, .xor);
6755 _ = try cg.load(overflow_ret, Type.i32, 0);
6756 try cg.addTag(.i32_eqz);
6757 try cg.addTag(.select);
69036758 },
69046759 128 => {
69056760 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)});
69076762 }
6908 const overflow_ret = try func.allocStack(Type.i32);
6909 const ret = try func.callIntrinsic(
6910 "__muloti4",
6763 const overflow_ret = try cg.allocStack(Type.i32);
6764 const ret = try cg.callIntrinsic(
6765 .__muloti4,
69116766 &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type },
69126767 Type.i128,
69136768 &.{ lhs, rhs, overflow_ret },
69146769 );
6915 try func.lowerToStack(ret);
6916 const xor = try func.binOp(lhs, rhs, Type.i128, .xor);
6917 const sign_v = try func.binOp(xor, .{ .imm32 = 127 }, Type.i128, .shr);
6770 try cg.lowerToStack(ret);
6771 const xor = try cg.binOp(lhs, rhs, Type.i128, .xor);
6772 const sign_v = try cg.binOp(xor, .{ .imm32 = 127 }, Type.i128, .shr);
69186773
69196774 // xor ~@as(u127, 0)
6920 try func.emitWValue(sign_v);
6921 const lsb = try func.load(sign_v, Type.u64, 0);
6922 _ = try func.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
6923 try func.store(.stack, .stack, Type.u64, sign_v.offset());
6924 try func.emitWValue(sign_v);
6925 const msb = try func.load(sign_v, Type.u64, 8);
6926 _ = try func.binOp(msb, .{ .imm64 = ~@as(u63, 0) }, Type.u64, .xor);
6927 try func.store(.stack, .stack, Type.u64, sign_v.offset() + 8);
6928
6929 try func.lowerToStack(sign_v);
6930 _ = try func.load(overflow_ret, Type.i32, 0);
6931 try func.addTag(.i32_eqz);
6932 try func.addTag(.select);
6775 try cg.emitWValue(sign_v);
6776 const lsb = try cg.load(sign_v, Type.u64, 0);
6777 _ = try cg.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
6778 try cg.store(.stack, .stack, Type.u64, sign_v.offset());
6779 try cg.emitWValue(sign_v);
6780 const msb = try cg.load(sign_v, Type.u64, 8);
6781 _ = try cg.binOp(msb, .{ .imm64 = ~@as(u63, 0) }, Type.u64, .xor);
6782 try cg.store(.stack, .stack, Type.u64, sign_v.offset() + 8);
6783
6784 try cg.lowerToStack(sign_v);
6785 _ = try cg.load(overflow_ret, Type.i32, 0);
6786 try cg.addTag(.i32_eqz);
6787 try cg.addTag(.select);
69336788 },
69346789 else => unreachable,
69356790 }
6936 return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
6791 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
69376792}
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 {
69406795 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;
6944 const zcu = pt.zcu;
6945 const ty = func.typeOfIndex(inst);
6946 const lhs = try func.resolveInst(bin_op.lhs);
6947 const rhs = try func.resolveInst(bin_op.rhs);
6798 const zcu = cg.pt.zcu;
6799 const ty = cg.typeOfIndex(inst);
6800 const lhs = try cg.resolveInst(bin_op.lhs);
6801 const rhs = try cg.resolveInst(bin_op.rhs);
69486802
69496803 const int_info = ty.intInfo(zcu);
69506804 const is_signed = int_info.signedness == .signed;
69516805
69526806 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});
69546808 }
69556809
69566810 if (is_signed) {
6957 const result = try signedSat(func, lhs, rhs, ty, op);
6958 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
6811 const result = try signedSat(cg, lhs, rhs, ty, op);
6812 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
69596813 }
69606814
69616815 const wasm_bits = toWasmBits(int_info.bits).?;
6962 var bin_result = try (try func.binOp(lhs, rhs, ty, op)).toLocal(func, ty);
6963 defer bin_result.free(func);
6816 var bin_result = try (try cg.binOp(lhs, rhs, ty, op)).toLocal(cg, ty);
6817 defer bin_result.free(cg);
69646818 if (wasm_bits != int_info.bits and op == .add) {
69656819 const val: u64 = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(int_info.bits))) - 1));
69666820 const imm_val: WValue = switch (wasm_bits) {
......@@ -6969,25 +6823,25 @@ fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
69696823 else => unreachable,
69706824 };
69716825
6972 try func.emitWValue(bin_result);
6973 try func.emitWValue(imm_val);
6974 _ = try func.cmp(bin_result, imm_val, ty, .lt);
6826 try cg.emitWValue(bin_result);
6827 try cg.emitWValue(imm_val);
6828 _ = try cg.cmp(bin_result, imm_val, ty, .lt);
69756829 } else {
69766830 switch (wasm_bits) {
6977 32 => try func.addImm32(if (op == .add) std.math.maxInt(u32) else 0),
6978 64 => try func.addImm64(if (op == .add) std.math.maxInt(u64) else 0),
6831 32 => try cg.addImm32(if (op == .add) std.math.maxInt(u32) else 0),
6832 64 => try cg.addImm64(if (op == .add) std.math.maxInt(u64) else 0),
69796833 else => unreachable,
69806834 }
6981 try func.emitWValue(bin_result);
6982 _ = try func.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt);
6835 try cg.emitWValue(bin_result);
6836 _ = try cg.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt);
69836837 }
69846838
6985 try func.addTag(.select);
6986 return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
6839 try cg.addTag(.select);
6840 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
69876841}
69886842
6989fn signedSat(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
6990 const pt = func.pt;
6843fn signedSat(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
6844 const pt = cg.pt;
69916845 const zcu = pt.zcu;
69926846 const int_info = ty.intInfo(zcu);
69936847 const wasm_bits = toWasmBits(int_info.bits).?;
......@@ -7007,92 +6861,92 @@ fn signedSat(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr
70076861 else => unreachable,
70086862 };
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);
70116865 if (!is_wasm_bits) {
7012 defer bin_result.free(func); // not returned in this branch
7013 try func.emitWValue(bin_result);
7014 try func.emitWValue(max_wvalue);
7015 _ = try func.cmp(bin_result, max_wvalue, ext_ty, .lt);
7016 try func.addTag(.select);
7017 try func.addLabel(.local_set, bin_result.local.value); // re-use local
7018
7019 try func.emitWValue(bin_result);
7020 try func.emitWValue(min_wvalue);
7021 _ = try func.cmp(bin_result, min_wvalue, ext_ty, .gt);
7022 try func.addTag(.select);
7023 try func.addLabel(.local_set, bin_result.local.value); // re-use local
7024 return (try func.wrapOperand(bin_result, ty)).toLocal(func, ty);
6866 defer bin_result.free(cg); // not returned in this branch
6867 try cg.emitWValue(bin_result);
6868 try cg.emitWValue(max_wvalue);
6869 _ = try cg.cmp(bin_result, max_wvalue, ext_ty, .lt);
6870 try cg.addTag(.select);
6871 try cg.addLocal(.local_set, bin_result.local.value); // re-use local
6872
6873 try cg.emitWValue(bin_result);
6874 try cg.emitWValue(min_wvalue);
6875 _ = try cg.cmp(bin_result, min_wvalue, ext_ty, .gt);
6876 try cg.addTag(.select);
6877 try cg.addLocal(.local_set, bin_result.local.value); // re-use local
6878 return (try cg.wrapOperand(bin_result, ty)).toLocal(cg, ty);
70256879 } else {
70266880 const zero: WValue = switch (wasm_bits) {
70276881 32 => .{ .imm32 = 0 },
70286882 64 => .{ .imm64 = 0 },
70296883 else => unreachable,
70306884 };
7031 try func.emitWValue(max_wvalue);
7032 try func.emitWValue(min_wvalue);
7033 _ = try func.cmp(bin_result, zero, ty, .lt);
7034 try func.addTag(.select);
7035 try func.emitWValue(bin_result);
6885 try cg.emitWValue(max_wvalue);
6886 try cg.emitWValue(min_wvalue);
6887 _ = try cg.cmp(bin_result, zero, ty, .lt);
6888 try cg.addTag(.select);
6889 try cg.emitWValue(bin_result);
70366890 // leave on stack
7037 const cmp_zero_result = try func.cmp(rhs, zero, ty, if (op == .add) .lt else .gt);
7038 const cmp_bin_result = try func.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.
7040 try func.addTag(.select);
7041 try func.addLabel(.local_set, bin_result.local.value); // re-use local
6891 const cmp_zero_result = try cg.cmp(rhs, zero, ty, if (op == .add) .lt else .gt);
6892 const cmp_bin_result = try cg.cmp(bin_result, lhs, ty, .lt);
6893 _ = try cg.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor.
6894 try cg.addTag(.select);
6895 try cg.addLocal(.local_set, bin_result.local.value); // re-use local
70426896 return bin_result;
70436897 }
70446898}
70456899
7046fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7047 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6900fn airShlSat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6901 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
70486902
7049 const pt = func.pt;
6903 const pt = cg.pt;
70506904 const zcu = pt.zcu;
7051 const ty = func.typeOfIndex(inst);
6905 const ty = cg.typeOfIndex(inst);
70526906 const int_info = ty.intInfo(zcu);
70536907 const is_signed = int_info.signedness == .signed;
70546908 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});
70566910 }
70576911
7058 const lhs = try func.resolveInst(bin_op.lhs);
7059 const rhs = try func.resolveInst(bin_op.rhs);
6912 const lhs = try cg.resolveInst(bin_op.lhs);
6913 const rhs = try cg.resolveInst(bin_op.rhs);
70606914 const wasm_bits = toWasmBits(int_info.bits).?;
7061 const result = try func.allocLocal(ty);
6915 const result = try cg.allocLocal(ty);
70626916
70636917 if (wasm_bits == int_info.bits) {
7064 var shl = try (try func.binOp(lhs, rhs, ty, .shl)).toLocal(func, ty);
7065 defer shl.free(func);
7066 var shr = try (try func.binOp(shl, rhs, ty, .shr)).toLocal(func, ty);
7067 defer shr.free(func);
6918 var shl = try (try cg.binOp(lhs, rhs, ty, .shl)).toLocal(cg, ty);
6919 defer shl.free(cg);
6920 var shr = try (try cg.binOp(shl, rhs, ty, .shr)).toLocal(cg, ty);
6921 defer shr.free(cg);
70686922
70696923 switch (wasm_bits) {
70706924 32 => blk: {
70716925 if (!is_signed) {
7072 try func.addImm32(std.math.maxInt(u32));
6926 try cg.addImm32(std.math.maxInt(u32));
70736927 break :blk;
70746928 }
7075 try func.addImm32(@bitCast(@as(i32, std.math.minInt(i32))));
7076 try func.addImm32(@bitCast(@as(i32, std.math.maxInt(i32))));
7077 _ = try func.cmp(lhs, .{ .imm32 = 0 }, ty, .lt);
7078 try func.addTag(.select);
6929 try cg.addImm32(@bitCast(@as(i32, std.math.minInt(i32))));
6930 try cg.addImm32(@bitCast(@as(i32, std.math.maxInt(i32))));
6931 _ = try cg.cmp(lhs, .{ .imm32 = 0 }, ty, .lt);
6932 try cg.addTag(.select);
70796933 },
70806934 64 => blk: {
70816935 if (!is_signed) {
7082 try func.addImm64(std.math.maxInt(u64));
6936 try cg.addImm64(std.math.maxInt(u64));
70836937 break :blk;
70846938 }
7085 try func.addImm64(@bitCast(@as(i64, std.math.minInt(i64))));
7086 try func.addImm64(@bitCast(@as(i64, std.math.maxInt(i64))));
7087 _ = try func.cmp(lhs, .{ .imm64 = 0 }, ty, .lt);
7088 try func.addTag(.select);
6939 try cg.addImm64(@bitCast(@as(i64, std.math.minInt(i64))));
6940 try cg.addImm64(@bitCast(@as(i64, std.math.maxInt(i64))));
6941 _ = try cg.cmp(lhs, .{ .imm64 = 0 }, ty, .lt);
6942 try cg.addTag(.select);
70896943 },
70906944 else => unreachable,
70916945 }
7092 try func.emitWValue(shl);
7093 _ = try func.cmp(lhs, shr, ty, .neq);
7094 try func.addTag(.select);
7095 try func.addLabel(.local_set, result.local.value);
6946 try cg.emitWValue(shl);
6947 _ = try cg.cmp(lhs, shr, ty, .neq);
6948 try cg.addTag(.select);
6949 try cg.addLocal(.local_set, result.local.value);
70966950 } else {
70976951 const shift_size = wasm_bits - int_info.bits;
70986952 const shift_value: WValue = switch (wasm_bits) {
......@@ -7102,50 +6956,50 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71026956 };
71036957 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);
7106 defer shl_res.free(func);
7107 var shl = try (try func.binOp(shl_res, rhs, ext_ty, .shl)).toLocal(func, ext_ty);
7108 defer shl.free(func);
7109 var shr = try (try func.binOp(shl, rhs, ext_ty, .shr)).toLocal(func, ext_ty);
7110 defer shr.free(func);
6959 var shl_res = try (try cg.binOp(lhs, shift_value, ext_ty, .shl)).toLocal(cg, ext_ty);
6960 defer shl_res.free(cg);
6961 var shl = try (try cg.binOp(shl_res, rhs, ext_ty, .shl)).toLocal(cg, ext_ty);
6962 defer shl.free(cg);
6963 var shr = try (try cg.binOp(shl, rhs, ext_ty, .shr)).toLocal(cg, ext_ty);
6964 defer shr.free(cg);
71116965
71126966 switch (wasm_bits) {
71136967 32 => blk: {
71146968 if (!is_signed) {
7115 try func.addImm32(std.math.maxInt(u32));
6969 try cg.addImm32(std.math.maxInt(u32));
71166970 break :blk;
71176971 }
71186972
7119 try func.addImm32(@bitCast(@as(i32, std.math.minInt(i32))));
7120 try func.addImm32(@bitCast(@as(i32, std.math.maxInt(i32))));
7121 _ = try func.cmp(shl_res, .{ .imm32 = 0 }, ext_ty, .lt);
7122 try func.addTag(.select);
6973 try cg.addImm32(@bitCast(@as(i32, std.math.minInt(i32))));
6974 try cg.addImm32(@bitCast(@as(i32, std.math.maxInt(i32))));
6975 _ = try cg.cmp(shl_res, .{ .imm32 = 0 }, ext_ty, .lt);
6976 try cg.addTag(.select);
71236977 },
71246978 64 => blk: {
71256979 if (!is_signed) {
7126 try func.addImm64(std.math.maxInt(u64));
6980 try cg.addImm64(std.math.maxInt(u64));
71276981 break :blk;
71286982 }
71296983
7130 try func.addImm64(@bitCast(@as(i64, std.math.minInt(i64))));
7131 try func.addImm64(@bitCast(@as(i64, std.math.maxInt(i64))));
7132 _ = try func.cmp(shl_res, .{ .imm64 = 0 }, ext_ty, .lt);
7133 try func.addTag(.select);
6984 try cg.addImm64(@bitCast(@as(i64, std.math.minInt(i64))));
6985 try cg.addImm64(@bitCast(@as(i64, std.math.maxInt(i64))));
6986 _ = try cg.cmp(shl_res, .{ .imm64 = 0 }, ext_ty, .lt);
6987 try cg.addTag(.select);
71346988 },
71356989 else => unreachable,
71366990 }
7137 try func.emitWValue(shl);
7138 _ = try func.cmp(shl_res, shr, ext_ty, .neq);
7139 try func.addTag(.select);
7140 try func.addLabel(.local_set, result.local.value);
7141 var shift_result = try func.binOp(result, shift_value, ext_ty, .shr);
6991 try cg.emitWValue(shl);
6992 _ = try cg.cmp(shl_res, shr, ext_ty, .neq);
6993 try cg.addTag(.select);
6994 try cg.addLocal(.local_set, result.local.value);
6995 var shift_result = try cg.binOp(result, shift_value, ext_ty, .shr);
71426996 if (is_signed) {
7143 shift_result = try func.wrapOperand(shift_result, ty);
6997 shift_result = try cg.wrapOperand(shift_result, ty);
71446998 }
7145 try func.addLabel(.local_set, result.local.value);
6999 try cg.addLocal(.local_set, result.local.value);
71467000 }
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 });
71497003}
71507004
71517005/// Calls a compiler-rt intrinsic by creating an undefined symbol,
......@@ -7155,31 +7009,23 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71557009/// passed as the first parameter.
71567010/// May leave the return value on the stack.
71577011fn callIntrinsic(
7158 func: *CodeGen,
7159 name: []const u8,
7012 cg: *CodeGen,
7013 intrinsic: Mir.Intrinsic,
71607014 param_types: []const InternPool.Index,
71617015 return_type: Type,
71627016 args: []const WValue,
71637017) InnerError!WValue {
71647018 assert(param_types.len == args.len);
7165 const symbol_index = func.bin_file.getGlobalSymbol(name, null) catch |err| {
7166 return func.fail("Could not find or create global symbol '{s}'", .{@errorName(err)});
7167 };
7019 const zcu = cg.pt.zcu;
71687020
71697021 // 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);
71787024 // if we want return as first param, we allocate a pointer to stack,
71797025 // and emit it as our first argument
71807026 const sret = if (want_sret_param) blk: {
7181 const sret_local = try func.allocStack(return_type);
7182 try func.lowerToStack(sret_local);
7027 const sret_local = try cg.allocStack(return_type);
7028 try cg.lowerToStack(sret_local);
71837029 break :blk sret_local;
71847030 } else .none;
71857031
......@@ -7187,16 +7033,15 @@ fn callIntrinsic(
71877033 for (args, 0..) |arg, arg_i| {
71887034 assert(!(want_sret_param and arg == .stack));
71897035 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);
71917037 }
71927038
7193 // Actually call our intrinsic
7194 try func.addLabel(.call, @intFromEnum(symbol_index));
7039 try cg.addInst(.{ .tag = .call_intrinsic, .data = .{ .intrinsic = intrinsic } });
71957040
71967041 if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) {
71977042 return .none;
71987043 } else if (return_type.isNoReturn(zcu)) {
7199 try func.addTag(.@"unreachable");
7044 try cg.addTag(.@"unreachable");
72007045 return .none;
72017046 } else if (want_sret_param) {
72027047 return sret;
......@@ -7205,194 +7050,30 @@ fn callIntrinsic(
72057050 }
72067051}
72077052
7208fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7209 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
7210 const operand = try func.resolveInst(un_op);
7211 const enum_ty = func.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 }
7053fn airTagName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7054 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
7055 const operand = try cg.resolveInst(un_op);
7056 const enum_ty = cg.typeOf(un_op);
73717057
7372 try writer.writeByte(std.wasm.opcode(.@"unreachable")); // tag value does not have a name
7373 // finish outer block
7374 try writer.writeByte(std.wasm.opcode(.end));
7375 // finish function body
7376 try writer.writeByte(std.wasm.opcode(.end));
7058 const result_ptr = try cg.allocStack(cg.typeOfIndex(inst));
7059 try cg.lowerToStack(result_ptr);
7060 try cg.emitWValue(operand);
7061 try cg.addInst(.{ .tag = .call_tag_name, .data = .{ .ip_index = enum_ty.toIntern() } });
73777062
7378 const slice_ty = Type.slice_const_u8_sentinel_0;
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);
7063 return cg.finishAir(inst, result_ptr, &.{un_op});
73827064}
73837065
7384fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7385 const pt = func.pt;
7386 const zcu = pt.zcu;
7066fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7067 const zcu = cg.pt.zcu;
73877068 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);
73917072 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
73947075 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);
73967077 defer values.deinit();
73977078
73987079 var lowest: ?u32 = null;
......@@ -7418,23 +7099,23 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
74187099 }
74197100
74207101 // start block for 'true' branch
7421 try func.startBlock(.block, wasm.block_empty);
7102 try cg.startBlock(.block, .empty);
74227103 // start block for 'false' branch
7423 try func.startBlock(.block, wasm.block_empty);
7104 try cg.startBlock(.block, .empty);
74247105 // block for the jump table itself
7425 try func.startBlock(.block, wasm.block_empty);
7106 try cg.startBlock(.block, .empty);
74267107
74277108 // lower operand to determine jump table target
7428 try func.emitWValue(operand);
7429 try func.addImm32(lowest.?);
7430 try func.addTag(.i32_sub);
7109 try cg.emitWValue(operand);
7110 try cg.addImm32(lowest.?);
7111 try cg.addTag(.i32_sub);
74317112
74327113 // Account for default branch so always add '1'
74337114 const depth = @as(u32, @intCast(highest.? - lowest.? + 1));
74347115 const jump_table: Mir.JumpTable = .{ .length = depth };
7435 const table_extra_index = try func.addExtra(jump_table);
7436 try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
7437 try func.mir_extra.ensureUnusedCapacity(func.gpa, depth);
7116 const table_extra_index = try cg.addExtra(jump_table);
7117 try cg.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
7118 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, depth);
74387119
74397120 var value: u32 = lowest.?;
74407121 while (value <= highest.?) : (value += 1) {
......@@ -7444,202 +7125,200 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
74447125 }
74457126 break :blk 0;
74467127 };
7447 func.mir_extra.appendAssumeCapacity(idx);
7128 cg.mir_extra.appendAssumeCapacity(idx);
74487129 }
7449 try func.endBlock();
7130 try cg.endBlock();
74507131
74517132 // 'false' branch (i.e. error set does not have value
74527133 // ensure we set local to 0 in case the local was re-used.
7453 try func.addImm32(0);
7454 try func.addLabel(.local_set, result.local.value);
7455 try func.addLabel(.br, 1);
7456 try func.endBlock();
7134 try cg.addImm32(0);
7135 try cg.addLocal(.local_set, result.local.value);
7136 try cg.addLabel(.br, 1);
7137 try cg.endBlock();
74577138
74587139 // 'true' branch
7459 try func.addImm32(1);
7460 try func.addLabel(.local_set, result.local.value);
7461 try func.addLabel(.br, 0);
7462 try func.endBlock();
7140 try cg.addImm32(1);
7141 try cg.addLocal(.local_set, result.local.value);
7142 try cg.addLabel(.br, 0);
7143 try cg.endBlock();
74637144
7464 return func.finishAir(inst, result, &.{ty_op.operand});
7145 return cg.finishAir(inst, result, &.{ty_op.operand});
74657146}
74667147
7467inline fn useAtomicFeature(func: *const CodeGen) bool {
7468 return std.Target.wasm.featureSetHas(func.target.cpu.features, .atomics);
7148inline fn useAtomicFeature(cg: *const CodeGen) bool {
7149 return std.Target.wasm.featureSetHas(cg.target.cpu.features, .atomics);
74697150}
74707151
7471fn airCmpxchg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7472 const pt = func.pt;
7473 const zcu = pt.zcu;
7474 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
7475 const extra = func.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
7152fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7153 const zcu = cg.pt.zcu;
7154 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
7155 const extra = cg.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);
74787158 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);
7482 const expected_val = try func.resolveInst(extra.expected_value);
7483 const new_val = try func.resolveInst(extra.new_value);
7161 const ptr_operand = try cg.resolveInst(extra.ptr);
7162 const expected_val = try cg.resolveInst(extra.expected_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: {
7488 const val_local = try func.allocLocal(ty);
7489 try func.emitWValue(ptr_operand);
7490 try func.lowerToStack(expected_val);
7491 try func.lowerToStack(new_val);
7492 try func.addAtomicMemArg(switch (ty.abiSize(zcu)) {
7167 const ptr_val = if (cg.useAtomicFeature()) val: {
7168 const val_local = try cg.allocLocal(ty);
7169 try cg.emitWValue(ptr_operand);
7170 try cg.lowerToStack(expected_val);
7171 try cg.lowerToStack(new_val);
7172 try cg.addAtomicMemArg(switch (ty.abiSize(zcu)) {
74937173 1 => .i32_atomic_rmw8_cmpxchg_u,
74947174 2 => .i32_atomic_rmw16_cmpxchg_u,
74957175 4 => .i32_atomic_rmw_cmpxchg,
74967176 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}),
74987178 }, .{
74997179 .offset = ptr_operand.offset(),
75007180 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
75017181 });
7502 try func.addLabel(.local_tee, val_local.local.value);
7503 _ = try func.cmp(.stack, expected_val, ty, .eq);
7504 try func.addLabel(.local_set, cmp_result.local.value);
7182 try cg.addLocal(.local_tee, val_local.local.value);
7183 _ = try cg.cmp(.stack, expected_val, ty, .eq);
7184 try cg.addLocal(.local_set, cmp_result.local.value);
75057185 break :val val_local;
75067186 } else val: {
75077187 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", .{});
75097189 }
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);
7513 try func.lowerToStack(new_val);
7514 try func.emitWValue(ptr_val);
7515 _ = try func.cmp(ptr_val, expected_val, ty, .eq);
7516 try func.addLabel(.local_tee, cmp_result.local.value);
7517 try func.addTag(.select);
7518 try func.store(.stack, .stack, ty, 0);
7192 try cg.lowerToStack(ptr_operand);
7193 try cg.lowerToStack(new_val);
7194 try cg.emitWValue(ptr_val);
7195 _ = try cg.cmp(ptr_val, expected_val, ty, .eq);
7196 try cg.addLocal(.local_tee, cmp_result.local.value);
7197 try cg.addTag(.select);
7198 try cg.store(.stack, .stack, ty, 0);
75197199
75207200 break :val ptr_val;
75217201 };
75227202
7523 const result = if (isByRef(result_ty, pt, func.target.*)) val: {
7524 try func.emitWValue(cmp_result);
7525 try func.addImm32(~@as(u32, 0));
7526 try func.addTag(.i32_xor);
7527 try func.addImm32(1);
7528 try func.addTag(.i32_and);
7529 const and_result = try WValue.toLocal(.stack, func, Type.bool);
7530 const result_ptr = try func.allocStack(result_ty);
7531 try func.store(result_ptr, and_result, Type.bool, @as(u32, @intCast(ty.abiSize(zcu))));
7532 try func.store(result_ptr, ptr_val, ty, 0);
7203 const result = if (isByRef(result_ty, zcu, cg.target)) val: {
7204 try cg.emitWValue(cmp_result);
7205 try cg.addImm32(~@as(u32, 0));
7206 try cg.addTag(.i32_xor);
7207 try cg.addImm32(1);
7208 try cg.addTag(.i32_and);
7209 const and_result = try WValue.toLocal(.stack, cg, Type.bool);
7210 const result_ptr = try cg.allocStack(result_ty);
7211 try cg.store(result_ptr, and_result, Type.bool, @as(u32, @intCast(ty.abiSize(zcu))));
7212 try cg.store(result_ptr, ptr_val, ty, 0);
75337213 break :val result_ptr;
75347214 } else val: {
7535 try func.addImm32(0);
7536 try func.emitWValue(ptr_val);
7537 try func.emitWValue(cmp_result);
7538 try func.addTag(.select);
7215 try cg.addImm32(0);
7216 try cg.emitWValue(ptr_val);
7217 try cg.emitWValue(cmp_result);
7218 try cg.addTag(.select);
75397219 break :val .stack;
75407220 };
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 });
75437223}
75447224
7545fn airAtomicLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7546 const pt = func.pt;
7547 const atomic_load = func.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;
7548 const ptr = try func.resolveInst(atomic_load.ptr);
7549 const ty = func.typeOfIndex(inst);
7225fn airAtomicLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7226 const zcu = cg.pt.zcu;
7227 const atomic_load = cg.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;
7228 const ptr = try cg.resolveInst(atomic_load.ptr);
7229 const ty = cg.typeOfIndex(inst);
75507230
7551 if (func.useAtomicFeature()) {
7552 const tag: wasm.AtomicsOpcode = switch (ty.abiSize(pt.zcu)) {
7231 if (cg.useAtomicFeature()) {
7232 const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {
75537233 1 => .i32_atomic_load8_u,
75547234 2 => .i32_atomic_load16_u,
75557235 4 => .i32_atomic_load,
75567236 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}),
75587238 };
7559 try func.emitWValue(ptr);
7560 try func.addAtomicMemArg(tag, .{
7239 try cg.emitWValue(ptr);
7240 try cg.addAtomicMemArg(tag, .{
75617241 .offset = ptr.offset(),
7562 .alignment = @intCast(ty.abiAlignment(pt.zcu).toByteUnits().?),
7242 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
75637243 });
75647244 } else {
7565 _ = try func.load(ptr, ty, 0);
7245 _ = try cg.load(ptr, ty, 0);
75667246 }
75677247
7568 return func.finishAir(inst, .stack, &.{atomic_load.ptr});
7248 return cg.finishAir(inst, .stack, &.{atomic_load.ptr});
75697249}
75707250
7571fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7572 const pt = func.pt;
7573 const zcu = pt.zcu;
7574 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
7575 const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data;
7251fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7252 const zcu = cg.pt.zcu;
7253 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
7254 const extra = cg.air.extraData(Air.AtomicRmw, pl_op.payload).data;
75767255
7577 const ptr = try func.resolveInst(pl_op.operand);
7578 const operand = try func.resolveInst(extra.operand);
7579 const ty = func.typeOfIndex(inst);
7256 const ptr = try cg.resolveInst(pl_op.operand);
7257 const operand = try cg.resolveInst(extra.operand);
7258 const ty = cg.typeOfIndex(inst);
75807259 const op: std.builtin.AtomicRmwOp = extra.op();
75817260
7582 if (func.useAtomicFeature()) {
7261 if (cg.useAtomicFeature()) {
75837262 switch (op) {
75847263 .Max,
75857264 .Min,
75867265 .Nand,
75877266 => {
7588 const tmp = try func.load(ptr, ty, 0);
7589 const value = try tmp.toLocal(func, ty);
7267 const tmp = try cg.load(ptr, ty, 0);
7268 const value = try tmp.toLocal(cg, ty);
75907269
75917270 // 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);
7595 try func.emitWValue(value);
7273 try cg.emitWValue(ptr);
7274 try cg.emitWValue(value);
75967275 if (op == .Nand) {
75977276 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");
76007279 if (wasm_bits == 32)
7601 try func.addImm32(~@as(u32, 0))
7280 try cg.addImm32(~@as(u32, 0))
76027281 else if (wasm_bits == 64)
7603 try func.addImm64(~@as(u64, 0))
7282 try cg.addImm64(~@as(u64, 0))
76047283 else
7605 return func.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{});
7606 _ = try func.binOp(and_res, .stack, ty, .xor);
7284 return cg.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{});
7285 _ = try cg.binOp(and_res, .stack, ty, .xor);
76077286 } else {
7608 try func.emitWValue(value);
7609 try func.emitWValue(operand);
7610 _ = try func.cmp(value, operand, ty, if (op == .Max) .gt else .lt);
7611 try func.addTag(.select);
7287 try cg.emitWValue(value);
7288 try cg.emitWValue(operand);
7289 _ = try cg.cmp(value, operand, ty, if (op == .Max) .gt else .lt);
7290 try cg.addTag(.select);
76127291 }
7613 try func.addAtomicMemArg(
7292 try cg.addAtomicMemArg(
76147293 switch (ty.abiSize(zcu)) {
76157294 1 => .i32_atomic_rmw8_cmpxchg_u,
76167295 2 => .i32_atomic_rmw16_cmpxchg_u,
76177296 4 => .i32_atomic_rmw_cmpxchg,
76187297 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)}),
76207299 },
76217300 .{
76227301 .offset = ptr.offset(),
76237302 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
76247303 },
76257304 );
7626 const select_res = try func.allocLocal(ty);
7627 try func.addLabel(.local_tee, select_res.local.value);
7628 _ = try func.cmp(.stack, value, ty, .neq); // leave on stack so we can use it for br_if
7305 const select_res = try cg.allocLocal(ty);
7306 try cg.addLocal(.local_tee, select_res.local.value);
7307 _ = 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);
7631 try func.addLabel(.local_set, value.local.value);
7309 try cg.emitWValue(select_res);
7310 try cg.addLocal(.local_set, value.local.value);
76327311
7633 try func.addLabel(.br_if, 0);
7634 try func.endBlock();
7635 return func.finishAir(inst, value, &.{ pl_op.operand, extra.operand });
7312 try cg.addLabel(.br_if, 0);
7313 try cg.endBlock();
7314 return cg.finishAir(inst, value, &.{ pl_op.operand, extra.operand });
76367315 },
76377316
76387317 // the other operations have their own instructions for Wasm.
76397318 else => {
7640 try func.emitWValue(ptr);
7641 try func.emitWValue(operand);
7642 const tag: wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {
7319 try cg.emitWValue(ptr);
7320 try cg.emitWValue(operand);
7321 const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {
76437322 1 => switch (op) {
76447323 .Xchg => .i32_atomic_rmw8_xchg_u,
76457324 .Add => .i32_atomic_rmw8_add_u,
......@@ -7676,22 +7355,22 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
76767355 .Xor => .i64_atomic_rmw_xor,
76777356 else => unreachable,
76787357 },
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}),
76807359 };
7681 try func.addAtomicMemArg(tag, .{
7360 try cg.addAtomicMemArg(tag, .{
76827361 .offset = ptr.offset(),
76837362 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
76847363 });
7685 return func.finishAir(inst, .stack, &.{ pl_op.operand, extra.operand });
7364 return cg.finishAir(inst, .stack, &.{ pl_op.operand, extra.operand });
76867365 },
76877366 }
76887367 } else {
7689 const loaded = try func.load(ptr, ty, 0);
7690 const result = try loaded.toLocal(func, ty);
7368 const loaded = try cg.load(ptr, ty, 0);
7369 const result = try loaded.toLocal(cg, ty);
76917370
76927371 switch (op) {
76937372 .Xchg => {
7694 try func.store(ptr, operand, ty, 0);
7373 try cg.store(ptr, operand, ty, 0);
76957374 },
76967375 .Add,
76977376 .Sub,
......@@ -7699,8 +7378,8 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
76997378 .Or,
77007379 .Xor,
77017380 => {
7702 try func.emitWValue(ptr);
7703 _ = try func.binOp(result, operand, ty, switch (op) {
7381 try cg.emitWValue(ptr);
7382 _ = try cg.binOp(result, operand, ty, switch (op) {
77047383 .Add => .add,
77057384 .Sub => .sub,
77067385 .And => .@"and",
......@@ -7709,87 +7388,123 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
77097388 else => unreachable,
77107389 });
77117390 if (ty.isInt(zcu) and (op == .Add or op == .Sub)) {
7712 _ = try func.wrapOperand(.stack, ty);
7391 _ = try cg.wrapOperand(.stack, ty);
77137392 }
7714 try func.store(.stack, .stack, ty, ptr.offset());
7393 try cg.store(.stack, .stack, ty, ptr.offset());
77157394 },
77167395 .Max,
77177396 .Min,
77187397 => {
7719 try func.emitWValue(ptr);
7720 try func.emitWValue(result);
7721 try func.emitWValue(operand);
7722 _ = try func.cmp(result, operand, ty, if (op == .Max) .gt else .lt);
7723 try func.addTag(.select);
7724 try func.store(.stack, .stack, ty, ptr.offset());
7398 try cg.emitWValue(ptr);
7399 try cg.emitWValue(result);
7400 try cg.emitWValue(operand);
7401 _ = try cg.cmp(result, operand, ty, if (op == .Max) .gt else .lt);
7402 try cg.addTag(.select);
7403 try cg.store(.stack, .stack, ty, ptr.offset());
77257404 },
77267405 .Nand => {
77277406 const wasm_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?;
77287407
7729 try func.emitWValue(ptr);
7730 const and_res = try func.binOp(result, operand, ty, .@"and");
7408 try cg.emitWValue(ptr);
7409 const and_res = try cg.binOp(result, operand, ty, .@"and");
77317410 if (wasm_bits == 32)
7732 try func.addImm32(~@as(u32, 0))
7411 try cg.addImm32(~@as(u32, 0))
77337412 else if (wasm_bits == 64)
7734 try func.addImm64(~@as(u64, 0))
7413 try cg.addImm64(~@as(u64, 0))
77357414 else
7736 return func.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{});
7737 _ = try func.binOp(and_res, .stack, ty, .xor);
7738 try func.store(.stack, .stack, ty, ptr.offset());
7415 return cg.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{});
7416 _ = try cg.binOp(and_res, .stack, ty, .xor);
7417 try cg.store(.stack, .stack, ty, ptr.offset());
77397418 },
77407419 }
77417420
7742 return func.finishAir(inst, result, &.{ pl_op.operand, extra.operand });
7421 return cg.finishAir(inst, result, &.{ pl_op.operand, extra.operand });
77437422 }
77447423}
77457424
7746fn airAtomicStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7747 const pt = func.pt;
7748 const zcu = pt.zcu;
7749 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
7425fn airAtomicStore(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7426 const zcu = cg.pt.zcu;
7427 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
77507428
7751 const ptr = try func.resolveInst(bin_op.lhs);
7752 const operand = try func.resolveInst(bin_op.rhs);
7753 const ptr_ty = func.typeOf(bin_op.lhs);
7429 const ptr = try cg.resolveInst(bin_op.lhs);
7430 const operand = try cg.resolveInst(bin_op.rhs);
7431 const ptr_ty = cg.typeOf(bin_op.lhs);
77547432 const ty = ptr_ty.childType(zcu);
77557433
7756 if (func.useAtomicFeature()) {
7757 const tag: wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {
7434 if (cg.useAtomicFeature()) {
7435 const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {
77587436 1 => .i32_atomic_store8,
77597437 2 => .i32_atomic_store16,
77607438 4 => .i32_atomic_store,
77617439 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}),
77637441 };
7764 try func.emitWValue(ptr);
7765 try func.lowerToStack(operand);
7766 try func.addAtomicMemArg(tag, .{
7442 try cg.emitWValue(ptr);
7443 try cg.lowerToStack(operand);
7444 try cg.addAtomicMemArg(tag, .{
77677445 .offset = ptr.offset(),
77687446 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
77697447 });
77707448 } else {
7771 try func.store(ptr, operand, ty, 0);
7449 try cg.store(ptr, operand, ty, 0);
77727450 }
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 });
77757453}
77767454
7777fn airFrameAddress(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7778 if (func.initial_stack_value == .none) {
7779 try func.initializeStack();
7455fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7456 if (cg.initial_stack_value == .none) {
7457 try cg.initializeStack();
77807458 }
7781 try func.emitWValue(func.bottom_stack_value);
7782 return func.finishAir(inst, .stack, &.{});
7459 try cg.emitWValue(cg.bottom_stack_value);
7460 return cg.finishAir(inst, .stack, &.{});
77837461}
77847462
7785fn typeOf(func: *CodeGen, inst: Air.Inst.Ref) Type {
7786 const pt = func.pt;
7787 const zcu = pt.zcu;
7788 return func.air.typeOf(inst, &zcu.intern_pool);
7463fn typeOf(cg: *CodeGen, inst: Air.Inst.Ref) Type {
7464 const zcu = cg.pt.zcu;
7465 return cg.air.typeOf(inst, &zcu.intern_pool);
77897466}
77907467
7791fn typeOfIndex(func: *CodeGen, inst: Air.Inst.Index) Type {
7792 const pt = func.pt;
7793 const zcu = pt.zcu;
7794 return func.air.typeOfIndex(inst, &zcu.intern_pool);
7468fn typeOfIndex(cg: *CodeGen, inst: Air.Inst.Index) Type {
7469 const zcu = cg.pt.zcu;
7470 return cg.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);
77957510}
src/arch/wasm/Emit.zig+920-620
......@@ -1,673 +1,973 @@
1//! Contains all logic to lower wasm MIR into its binary
2//! or textual representation.
3
41const Emit = @This();
2
53const std = @import("std");
4const assert = std.debug.assert;
5const Allocator = std.mem.Allocator;
6const leb = std.leb;
7
8const Wasm = link.File.Wasm;
69const Mir = @import("Mir.zig");
710const link = @import("../../link.zig");
811const Zcu = @import("../../Zcu.zig");
912const InternPool = @import("../../InternPool.zig");
1013const codegen = @import("../../codegen.zig");
11const leb128 = std.leb;
1214
13/// Contains our list of instructions
1415mir: Mir,
15/// Reference to the Wasm module linker
16bin_file: *link.File.Wasm,
17/// Possible error message. When set, the value is allocated and
18/// must be freed manually.
19error_msg: ?*Zcu.ErrorMsg = null,
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{
16wasm: *Wasm,
17/// The binary representation that will be emitted by this module.
18code: *std.ArrayListUnmanaged(u8),
19
20pub const Error = error{
3821 OutOfMemory,
39 EmitFail,
4022};
4123
42pub fn emitMir(emit: *Emit) InnerError!void {
43 const mir_tags = emit.mir.instructions.items(.tag);
44 // write the locals in the prologue of the function body
45 // before we emit the function body when lowering MIR
46 try emit.emitLocals();
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.?;
24pub fn lowerToCode(emit: *Emit) Error!void {
25 const mir = &emit.mir;
26 const code = emit.code;
27 const wasm = emit.wasm;
28 const comp = wasm.base.comp;
25929 const gpa = comp.gpa;
260 emit.error_msg = try Zcu.ErrorMsg.create(gpa, zcu.navSrcLoc(emit.owner_nav), format, args);
261 return error.EmitFail;
262}
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();
30 const is_obj = comp.config.output_mode == .Obj;
31 const target = &comp.root_mod.resolved_target.result;
32 const is_wasm32 = target.cpu.arch == .wasm32;
28933
290 try emit.code.append(std.wasm.opcode(.br_table));
291 try leb128.writeUleb128(writer, extra.data.length - 1); // Default label is not part of length/depth
292 for (labels) |label| {
293 try leb128.writeUleb128(writer, label);
294 }
295}
34 const tags = mir.instruction_tags;
35 const datas = mir.instruction_datas;
36 var inst: u32 = 0;
29637
297fn emitLabel(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
298 const label = emit.mir.instructions.items(.data)[inst].label;
299 try emit.code.append(@intFromEnum(tag));
300 try leb128.writeUleb128(emit.code.writer(), label);
301}
38 loop: switch (tags[inst]) {
39 .dbg_epilogue_begin => {
40 return;
41 },
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 {
304 const comp = emit.bin_file.base.comp;
305 const gpa = comp.gpa;
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}
131 inst += 1;
132 continue :loop tags[inst];
133 },
321134
322fn emitImm32(emit: *Emit, inst: Mir.Inst.Index) !void {
323 const value: i32 = emit.mir.instructions.items(.data)[inst].imm32;
324 try emit.code.append(std.wasm.opcode(.i32_const));
325 try leb128.writeIleb128(emit.code.writer(), value);
326}
135 .local_get, .local_set, .local_tee => {
136 try code.ensureUnusedCapacity(gpa, 11);
137 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
138 leb.writeUleb128(code.fixedWriter(), datas[inst].local) catch unreachable;
327139
328fn emitImm64(emit: *Emit, inst: Mir.Inst.Index) !void {
329 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
330 const value = emit.mir.extraData(Mir.Imm64, extra_index);
331 try emit.code.append(std.wasm.opcode(.i64_const));
332 try leb128.writeIleb128(emit.code.writer(), @as(i64, @bitCast(value.data.toU64())));
333}
140 inst += 1;
141 continue :loop tags[inst];
142 },
334143
335fn emitFloat32(emit: *Emit, inst: Mir.Inst.Index) !void {
336 const value: f32 = emit.mir.instructions.items(.data)[inst].float32;
337 try emit.code.append(std.wasm.opcode(.f32_const));
338 try emit.code.writer().writeInt(u32, @bitCast(value), .little);
339}
144 .br_table => {
145 const extra_index = datas[inst].payload;
146 const extra = mir.extraData(Mir.JumpTable, extra_index);
147 const labels = mir.extra[extra.end..][0..extra.data.length];
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 {
342 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
343 const value = emit.mir.extraData(Mir.Float64, extra_index);
344 try emit.code.append(std.wasm.opcode(.f64_const));
345 try emit.code.writer().writeInt(u64, value.data.toU64(), .little);
346}
158 .call_nav => {
159 try code.ensureUnusedCapacity(gpa, 6);
160 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
161 if (is_obj) {
162 try wasm.out_relocs.append(gpa, .{
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 {
349 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
350 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;
351 try emit.code.append(@intFromEnum(tag));
352 try encodeMemArg(mem_arg, emit.code.writer());
353}
177 .call_indirect => {
178 try code.ensureUnusedCapacity(gpa, 11);
179 const func_ty_index = datas[inst].func_ty;
180 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect));
181 if (is_obj) {
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 {
356 // wasm encodes alignment as power of 2, rather than natural alignment
357 const encoded_alignment = @ctz(mem_arg.alignment);
358 try leb128.writeUleb128(writer, encoded_alignment);
359 try leb128.writeUleb128(writer, mem_arg.offset);
360}
199 .call_tag_name => {
200 try code.ensureUnusedCapacity(gpa, 6);
201 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
202 if (is_obj) {
203 try wasm.out_relocs.append(gpa, .{
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 {
363 const comp = emit.bin_file.base.comp;
364 const gpa = comp.gpa;
365 const label = emit.mir.instructions.items(.data)[inst].label;
366 try emit.code.append(std.wasm.opcode(.call));
367 const call_offset = emit.offset();
368 var buf: [5]u8 = undefined;
369 leb128.writeUnsignedFixed(5, &buf, label);
370 try emit.code.appendSlice(&buf);
371
372 if (label != 0) {
373 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;
374 const atom = emit.bin_file.getAtomPtr(atom_index);
375 try atom.relocs.append(gpa, .{
376 .offset = call_offset,
377 .index = label,
378 .relocation_type = .R_WASM_FUNCTION_INDEX_LEB,
379 });
380 }
381}
218 .call_intrinsic => {
219 // Although this currently uses `wasm.internString`, note that it
220 // *could* be changed to directly index into a preloaded strings
221 // table initialized based on the `Mir.Intrinsic` enum.
222 const symbol_name = try wasm.internString(@tagName(datas[inst].intrinsic));
223
224 try code.ensureUnusedCapacity(gpa, 6);
225 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
226 if (is_obj) {
227 try wasm.out_relocs.append(gpa, .{
228 .offset = @intCast(code.items.len),
229 .pointee = .{ .symbol_index = try wasm.symbolNameIndex(symbol_name) },
230 .tag = .function_index_leb,
231 .addend = 0,
232 });
233 code.appendNTimesAssumeCapacity(0, 5);
234 } else {
235 appendOutputFunctionIndex(code, .fromSymbolName(wasm, symbol_name));
236 }
237
238 inst += 1;
239 continue :loop tags[inst];
240 },
382241
383fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
384 const type_index = emit.mir.instructions.items(.data)[inst].label;
385 try emit.code.append(std.wasm.opcode(.call_indirect));
386 // NOTE: If we remove unused function types in the future for incremental
387 // linking, we must also emit a relocation for this `type_index`
388 const call_offset = emit.offset();
389 var buf: [5]u8 = undefined;
390 leb128.writeUnsignedFixed(5, &buf, type_index);
391 try emit.code.appendSlice(&buf);
392 if (type_index != 0) {
393 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;
394 const atom = emit.bin_file.getAtomPtr(atom_index);
395 try atom.relocs.append(emit.bin_file.base.comp.gpa, .{
396 .offset = call_offset,
397 .index = type_index,
398 .relocation_type = .R_WASM_TYPE_INDEX_LEB,
399 });
400 }
401 try leb128.writeUleb128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index
402}
242 .global_set_sp => {
243 try code.ensureUnusedCapacity(gpa, 6);
244 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
245 if (is_obj) {
246 try wasm.out_relocs.append(gpa, .{
247 .offset = @intCast(code.items.len),
248 .pointee = .{ .symbol_index = try wasm.stackPointerSymbolIndex() },
249 .tag = .global_index_leb,
250 .addend = 0,
251 });
252 code.appendNTimesAssumeCapacity(0, 5);
253 } else {
254 const sp_global: Wasm.GlobalIndex = .stack_pointer;
255 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
256 }
257
258 inst += 1;
259 continue :loop tags[inst];
260 },
403261
404fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
405 const comp = emit.bin_file.base.comp;
406 const gpa = comp.gpa;
407 const symbol_index = emit.mir.instructions.items(.data)[inst].label;
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}
262 .f32_const => {
263 try code.ensureUnusedCapacity(gpa, 5);
264 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f32_const));
265 std.mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @bitCast(datas[inst].float32), .little);
424266
425fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
426 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
427 const mem = emit.mir.extraData(Mir.Memory, extra_index).data;
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 }
267 inst += 1;
268 continue :loop tags[inst];
269 },
444270
445 if (mem.pointer != 0) {
446 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;
447 const atom = emit.bin_file.getAtomPtr(atom_index);
448 try atom.relocs.append(gpa, .{
449 .offset = mem_offset,
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}
271 .f64_const => {
272 try code.ensureUnusedCapacity(gpa, 9);
273 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f64_const));
274 const float64 = mir.extraData(Mir.Float64, datas[inst].payload).data;
275 std.mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), float64.toInt(), .little);
456276
457fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
458 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
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
277 inst += 1;
278 continue :loop tags[inst];
473279 },
474 .memory_fill => {
475 try leb128.writeUleb128(writer, @as(u32, 0)); // memory index
280 .i32_const => {
281 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];
476287 },
477 .memory_copy => {
478 try leb128.writeUleb128(writer, @as(u32, 0)); // dst memory index
479 try leb128.writeUleb128(writer, @as(u32, 0)); // src memory index
288 .i64_const => {
289 try code.ensureUnusedCapacity(gpa, 11);
290 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];
480296 },
481297
482 // nontrapping-float-to-int-conversion opcodes
483 .i32_trunc_sat_f32_s,
484 .i32_trunc_sat_f32_u,
485 .i32_trunc_sat_f64_s,
486 .i32_trunc_sat_f64_u,
487 .i64_trunc_sat_f32_s,
488 .i64_trunc_sat_f32_u,
489 .i64_trunc_sat_f64_s,
490 .i64_trunc_sat_f64_u,
491 => {}, // opcode already written
492 else => |tag| return emit.fail("TODO: Implement extension instruction: {s}\n", .{@tagName(tag)}),
493 }
494}
495
496fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
497 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
498 const opcode = emit.mir.extra[extra_index];
499 const writer = emit.code.writer();
500 try emit.code.append(std.wasm.opcode(.simd_prefix));
501 try leb128.writeUleb128(writer, opcode);
502 switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) {
503 .v128_store,
504 .v128_load,
505 .v128_load8_splat,
506 .v128_load16_splat,
507 .v128_load32_splat,
508 .v128_load64_splat,
298 .i32_load,
299 .i64_load,
300 .f32_load,
301 .f64_load,
302 .i32_load8_s,
303 .i32_load8_u,
304 .i32_load16_s,
305 .i32_load16_u,
306 .i64_load8_s,
307 .i64_load8_u,
308 .i64_load16_s,
309 .i64_load16_u,
310 .i64_load32_s,
311 .i64_load32_u,
312 .i32_store,
313 .i64_store,
314 .f32_store,
315 .f64_store,
316 .i32_store8,
317 .i32_store16,
318 .i64_store8,
319 .i64_store16,
320 .i64_store32,
509321 => {
510 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data;
511 try encodeMemArg(mem_arg, writer);
322 try code.ensureUnusedCapacity(gpa, 1 + 20);
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];
512327 },
513 .v128_const,
514 .i8x16_shuffle,
515 => {
516 const simd_value = emit.mir.extra[extra_index + 1 ..][0..4];
517 try writer.writeAll(std.mem.asBytes(simd_value));
518 },
519 .i8x16_extract_lane_s,
520 .i8x16_extract_lane_u,
521 .i8x16_replace_lane,
522 .i16x8_extract_lane_s,
523 .i16x8_extract_lane_u,
524 .i16x8_replace_lane,
525 .i32x4_extract_lane,
526 .i32x4_replace_lane,
527 .i64x2_extract_lane,
528 .i64x2_replace_lane,
529 .f32x4_extract_lane,
530 .f32x4_replace_lane,
531 .f64x2_extract_lane,
532 .f64x2_replace_lane,
328
329 .end,
330 .@"return",
331 .@"unreachable",
332 .select,
333 .i32_eqz,
334 .i32_eq,
335 .i32_ne,
336 .i32_lt_s,
337 .i32_lt_u,
338 .i32_gt_s,
339 .i32_gt_u,
340 .i32_le_s,
341 .i32_le_u,
342 .i32_ge_s,
343 .i32_ge_u,
344 .i64_eqz,
345 .i64_eq,
346 .i64_ne,
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,
533457 => {
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];
535461 },
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 {
548 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
549 const opcode = emit.mir.extra[extra_index];
550 const writer = emit.code.writer();
551 try emit.code.append(std.wasm.opcode(.atomics_prefix));
552 try leb128.writeUleb128(writer, opcode);
553 switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) {
554 .i32_atomic_load,
555 .i64_atomic_load,
556 .i32_atomic_load8_u,
557 .i32_atomic_load16_u,
558 .i64_atomic_load8_u,
559 .i64_atomic_load16_u,
560 .i64_atomic_load32_u,
561 .i32_atomic_store,
562 .i64_atomic_store,
563 .i32_atomic_store8,
564 .i32_atomic_store16,
565 .i64_atomic_store8,
566 .i64_atomic_store16,
567 .i64_atomic_store32,
568 .i32_atomic_rmw_add,
569 .i64_atomic_rmw_add,
570 .i32_atomic_rmw8_add_u,
571 .i32_atomic_rmw16_add_u,
572 .i64_atomic_rmw8_add_u,
573 .i64_atomic_rmw16_add_u,
574 .i64_atomic_rmw32_add_u,
575 .i32_atomic_rmw_sub,
576 .i64_atomic_rmw_sub,
577 .i32_atomic_rmw8_sub_u,
578 .i32_atomic_rmw16_sub_u,
579 .i64_atomic_rmw8_sub_u,
580 .i64_atomic_rmw16_sub_u,
581 .i64_atomic_rmw32_sub_u,
582 .i32_atomic_rmw_and,
583 .i64_atomic_rmw_and,
584 .i32_atomic_rmw8_and_u,
585 .i32_atomic_rmw16_and_u,
586 .i64_atomic_rmw8_and_u,
587 .i64_atomic_rmw16_and_u,
588 .i64_atomic_rmw32_and_u,
589 .i32_atomic_rmw_or,
590 .i64_atomic_rmw_or,
591 .i32_atomic_rmw8_or_u,
592 .i32_atomic_rmw16_or_u,
593 .i64_atomic_rmw8_or_u,
594 .i64_atomic_rmw16_or_u,
595 .i64_atomic_rmw32_or_u,
596 .i32_atomic_rmw_xor,
597 .i64_atomic_rmw_xor,
598 .i32_atomic_rmw8_xor_u,
599 .i32_atomic_rmw16_xor_u,
600 .i64_atomic_rmw8_xor_u,
601 .i64_atomic_rmw16_xor_u,
602 .i64_atomic_rmw32_xor_u,
603 .i32_atomic_rmw_xchg,
604 .i64_atomic_rmw_xchg,
605 .i32_atomic_rmw8_xchg_u,
606 .i32_atomic_rmw16_xchg_u,
607 .i64_atomic_rmw8_xchg_u,
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);
463 .misc_prefix => {
464 try code.ensureUnusedCapacity(gpa, 6 + 6);
465 const extra_index = datas[inst].payload;
466 const opcode = mir.extra[extra_index];
467 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix));
468 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;
469 switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) {
470 // bulk-memory opcodes
471 .data_drop => {
472 const segment = mir.extra[extra_index + 1];
473 leb.writeUleb128(code.fixedWriter(), segment) catch unreachable;
474
475 inst += 1;
476 continue :loop tags[inst];
477 },
478 .memory_init => {
479 const segment = mir.extra[extra_index + 1];
480 leb.writeUleb128(code.fixedWriter(), segment) catch unreachable;
481 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index
482
483 inst += 1;
484 continue :loop tags[inst];
485 },
486 .memory_fill => {
487 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index
488
489 inst += 1;
490 continue :loop tags[inst];
491 },
492 .memory_copy => {
493 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // dst memory index
494 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // src memory index
495
496 inst += 1;
497 continue :loop tags[inst];
498 },
499
500 // nontrapping-float-to-int-conversion opcodes
501 .i32_trunc_sat_f32_s,
502 .i32_trunc_sat_f32_u,
503 .i32_trunc_sat_f64_s,
504 .i32_trunc_sat_f64_u,
505 .i64_trunc_sat_f32_s,
506 .i64_trunc_sat_f32_u,
507 .i64_trunc_sat_f64_s,
508 .i64_trunc_sat_f64_u,
509 => {
510 inst += 1;
511 continue :loop tags[inst];
512 },
513
514 .table_init => @panic("TODO"),
515 .elem_drop => @panic("TODO"),
516 .table_copy => @panic("TODO"),
517 .table_grow => @panic("TODO"),
518 .table_size => @panic("TODO"),
519 .table_fill => @panic("TODO"),
520
521 _ => unreachable,
522 }
523 comptime unreachable;
621524 },
622 .atomic_fence => {
623 // TODO: When multi-memory proposal is accepted and implemented in the compiler,
624 // change this to (user-)specified index, rather than hardcode it to memory index 0.
625 const memory_index: u32 = 0;
626 try leb128.writeUleb128(writer, memory_index);
525 .simd_prefix => {
526 try code.ensureUnusedCapacity(gpa, 6 + 20);
527 const extra_index = datas[inst].payload;
528 const opcode = mir.extra[extra_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;
627901 },
628 else => |tag| return emit.fail("TODO: Implement atomic instruction: {s}", .{@tagName(tag)}),
629902 }
903 comptime unreachable;
630904}
631905
632fn emitMemFill(emit: *Emit) !void {
633 try emit.code.append(0xFC);
634 try emit.code.append(0x0B);
635 // When multi-memory proposal reaches phase 4, we
636 // can emit a different memory index here.
637 // For now we will always emit index 0.
638 try leb128.writeUleb128(emit.code.writer(), @as(u32, 0));
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);
906/// Asserts 20 unused capacity.
907fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {
908 assert(code.unusedCapacitySlice().len >= 20);
909 // Wasm encodes alignment as power of 2, rather than natural alignment.
910 const encoded_alignment = @ctz(mem_arg.alignment);
911 leb.writeUleb128(code.fixedWriter(), encoded_alignment) catch unreachable;
912 leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable;
645913}
646914
647fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
648 if (emit.dbg_output != .dwarf) return;
915fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffObj, is_wasm32: bool) !void {
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));
651 const delta_pc = emit.offset() - emit.prev_di_offset;
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);
920 try code.ensureUnusedCapacity(gpa, 11);
921 code.appendAssumeCapacity(@intFromEnum(opcode));
655922
656 emit.prev_di_line = line;
657 emit.prev_di_column = column;
658 emit.prev_di_offset = emit.offset();
923 try wasm.out_relocs.append(gpa, .{
924 .offset = @intCast(code.items.len),
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);
659930}
660931
661fn emitDbgPrologueEnd(emit: *Emit) !void {
662 if (emit.dbg_output != .dwarf) return;
932fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffExe, is_wasm32: bool) !void {
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();
665 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
940 const addr = wasm.uavAddr(data.uav_exe);
941 leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable;
666942}
667943
668fn emitDbgEpilogueBegin(emit: *Emit) !void {
669 if (emit.dbg_output != .dwarf) return;
944fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff, is_wasm32: bool) !void {
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();
672 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
971fn appendOutputFunctionIndex(code: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) void {
972 leb.writeUleb128(code.fixedWriter(), @intFromEnum(i)) catch unreachable;
673973}
src/arch/wasm/Mir.zig+338-102
......@@ -7,11 +7,15 @@
77//! and known jump labels for blocks.
88
99const Mir = @This();
10const InternPool = @import("../../InternPool.zig");
11const Wasm = @import("../../link/Wasm.zig");
1012
13const builtin = @import("builtin");
1114const std = @import("std");
15const assert = std.debug.assert;
1216
13/// A struct of array that represents each individual wasm
14instructions: std.MultiArrayList(Inst).Slice,
17instruction_tags: []const Inst.Tag,
18instruction_datas: []const Inst.Data,
1519/// A slice of indexes where the meaning of the data is determined by the
1620/// `Inst.Tag` value.
1721extra: []const u32,
......@@ -26,16 +30,14 @@ pub const Inst = struct {
2630 /// The position of a given MIR isntruction with the instruction list.
2731 pub const Index = u32;
2832
29 /// Contains all possible wasm opcodes the Zig compiler may emit
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.
33 /// Some tags match wasm opcode values to facilitate trivial lowering.
3634 pub const Tag = enum(u8) {
37 /// Uses `nop`
35 /// Uses `tag`.
3836 @"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,
3941 /// Creates a new block that can be jump from.
4042 ///
4143 /// Type of the block is given in data `block_type`
......@@ -44,56 +46,92 @@ pub const Inst = struct {
4446 ///
4547 /// Type of the loop is given in data `block_type`
4648 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,
4785 /// Inserts debug information about the current line and column
4886 /// of the source code
4987 ///
5088 /// Uses `payload` of which the payload type is `DbgLineColumn`
51 dbg_line = 0x06,
52 /// Emits epilogue begin debug information
53 ///
54 /// Uses `nop`
55 dbg_epilogue_begin = 0x07,
56 /// Emits prologue end debug information
57 ///
58 /// Uses `nop`
59 dbg_prologue_end = 0x08,
89 dbg_line,
90 /// Lowers to an i32_const containing the number of unique Zig error
91 /// names.
92 /// Uses `tag`.
93 errors_len,
6094 /// Represents the end of a function body or an initialization expression
6195 ///
62 /// Payload is `nop`
96 /// Uses `tag` (no additional data).
6397 end = 0x0B,
6498 /// Breaks from the current block to a label
6599 ///
66 /// Data is `label` where index represents the label to jump to
100 /// Uses `label` where index represents the label to jump to
67101 br = 0x0C,
68102 /// Breaks from the current block if the stack value is non-zero
69103 ///
70 /// Data is `label` where index represents the label to jump to
104 /// Uses `label` where index represents the label to jump to
71105 br_if = 0x0D,
72106 /// Jump table that takes the stack value as an index where each value
73107 /// represents the label to jump to.
74108 ///
75109 /// Data is extra of which the Payload's type is `JumpTable`
76 br_table = 0x0E,
110 br_table,
77111 /// Returns from the function
78112 ///
79 /// Uses `nop`
113 /// Uses `tag`.
80114 @"return" = 0x0F,
81 /// Calls a function by its index
82 ///
83 /// Uses `label`
84 call = 0x10,
115 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) containing
116 /// the base address of the table of error code names, with each
117 /// element being a null-terminated slice.
118 ///
119 /// Uses `tag`.
120 error_name_table_ref,
121 /// Calls a function using `nav_index`.
122 call_nav,
85123 /// Calls a function pointer by its function signature
86124 /// and index into the function table.
87125 ///
88 /// Uses `label`
89 call_indirect = 0x11,
90 /// Contains a symbol to a function pointer
91 /// uses `label`
92 ///
93 /// Note: This uses `0x16` as value which is reserved by the WebAssembly
94 /// specification but unused, meaning we must update this if the specification were to
95 /// use this value.
96 function_index = 0x16,
126 /// Uses `func_ty`
127 call_indirect,
128 /// Calls a function by its index.
129 ///
130 /// The function is the auto-generated tag name function for the type
131 /// provided in `ip_index`.
132 call_tag_name,
133 /// Lowers to a `call` instruction, using `intrinsic`.
134 call_intrinsic,
97135 /// Pops three values from the stack and pushes
98136 /// the first or second value dependent on the third value.
99137 /// Uses `tag`
......@@ -112,15 +150,11 @@ pub const Inst = struct {
112150 ///
113151 /// Uses `label`
114152 local_tee = 0x22,
115 /// Loads a (mutable) global at given index onto the stack
116 ///
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.
153 /// Pops a value from the stack and sets the stack pointer global.
154 /// The value must be the same type as the stack pointer global.
121155 ///
122 /// Uses `label`.
123 global_set = 0x24,
156 /// Uses `tag` (no additional data).
157 global_set_sp,
124158 /// Loads a 32-bit integer from memory (data section) onto the stack
125159 /// Pops the value from the stack which represents the offset into memory.
126160 ///
......@@ -256,19 +290,19 @@ pub const Inst = struct {
256290 /// Loads a 32-bit signed immediate value onto the stack
257291 ///
258292 /// Uses `imm32`
259 i32_const = 0x41,
293 i32_const,
260294 /// Loads a i64-bit signed immediate value onto the stack
261295 ///
262296 /// uses `payload` of type `Imm64`
263 i64_const = 0x42,
297 i64_const,
264298 /// Loads a 32-bit float value onto the stack.
265299 ///
266300 /// Uses `float32`
267 f32_const = 0x43,
301 f32_const,
268302 /// Loads a 64-bit float value onto the stack.
269303 ///
270304 /// Uses `payload` of type `Float64`
271 f64_const = 0x44,
305 f64_const,
272306 /// Uses `tag`
273307 i32_eqz = 0x45,
274308 /// Uses `tag`
......@@ -522,25 +556,19 @@ pub const Inst = struct {
522556 ///
523557 /// The `data` field depends on the extension instruction and
524558 /// may contain additional data.
525 misc_prefix = 0xFC,
559 misc_prefix,
526560 /// The instruction consists of a simd opcode.
527561 /// The actual simd-opcode is found at payload's index.
528562 ///
529563 /// The `data` field depends on the simd instruction and
530564 /// may contain additional data.
531 simd_prefix = 0xFD,
565 simd_prefix,
532566 /// The instruction consists of an atomics opcode.
533567 /// The actual atomics-opcode is found at payload's index.
534568 ///
535569 /// The `data` field depends on the atomics instruction and
536570 /// may contain additional data.
537571 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
545573 /// From a given wasm opcode, returns a MIR tag.
546574 pub fn fromOpcode(opcode: std.wasm.Opcode) Tag {
......@@ -560,26 +588,41 @@ pub const Inst = struct {
560588 /// Uses no additional data
561589 tag: void,
562590 /// Contains the result type of a block
563 ///
564 /// Used by `block` and `loop`
565 block_type: u8,
566 /// Contains an u32 index into a wasm section entry, such as a local.
567 /// Note: This is not an index to another instruction.
568 ///
569 /// Used by e.g. `local_get`, `local_set`, etc.
591 block_type: std.wasm.BlockType,
592 /// Label: Each structured control instruction introduces an implicit label.
593 /// Labels are targets for branch instructions that reference them with
594 /// label indices. Unlike with other index spaces, indexing of labels
595 /// is relative by nesting depth, that is, label 0 refers to the
596 /// innermost structured control instruction enclosing the referring
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.
570600 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,
571605 /// A 32-bit immediate value.
572 ///
573 /// Used by `i32_const`
574606 imm32: i32,
575607 /// A 32-bit float value
576 ///
577 /// Used by `f32_float`
578608 float32: f32,
579609 /// Index into `extra`. Meaning of what can be found there is context-dependent.
580 ///
581 /// Used by e.g. `br_table`
582610 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 }
583626 };
584627};
585628
......@@ -596,6 +639,11 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data
596639 inline for (fields) |field| {
597640 @field(result, field.name) = switch (field.type) {
598641 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]),
599647 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
600648 };
601649 i += 1;
......@@ -609,28 +657,19 @@ pub const JumpTable = struct {
609657 length: u32,
610658};
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`
618660pub const Imm64 = struct {
619661 msb: u32,
620662 lsb: u32,
621663
622 pub fn fromU64(imm: u64) Imm64 {
664 pub fn init(full: u64) Imm64 {
623665 return .{
624 .msb = @as(u32, @truncate(imm >> 32)),
625 .lsb = @as(u32, @truncate(imm)),
666 .msb = @truncate(full >> 32),
667 .lsb = @truncate(full),
626668 };
627669 }
628670
629 pub fn toU64(self: Imm64) u64 {
630 var result: u64 = 0;
631 result |= @as(u64, self.msb) << 32;
632 result |= @as(u64, self.lsb);
633 return result;
671 pub fn toInt(i: Imm64) u64 {
672 return (@as(u64, i.msb) << 32) | @as(u64, i.lsb);
634673 }
635674};
636675
......@@ -638,23 +677,16 @@ pub const Float64 = struct {
638677 msb: u32,
639678 lsb: u32,
640679
641 pub fn fromFloat64(float: f64) Float64 {
642 const tmp = @as(u64, @bitCast(float));
680 pub fn init(f: f64) Float64 {
681 const int: u64 = @bitCast(f);
643682 return .{
644 .msb = @as(u32, @truncate(tmp >> 32)),
645 .lsb = @as(u32, @truncate(tmp)),
683 .msb = @truncate(int >> 32),
684 .lsb = @truncate(int),
646685 };
647686 }
648687
649 pub fn toF64(self: Float64) f64 {
650 @as(f64, @bitCast(self.toU64()));
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;
688 pub fn toInt(f: Float64) u64 {
689 return (@as(u64, f.msb) << 32) | @as(u64, f.lsb);
658690 }
659691};
660692
......@@ -663,11 +695,19 @@ pub const MemArg = struct {
663695 alignment: u32,
664696};
665697
666/// Represents a memory address, which holds both the pointer
667/// or the parent pointer and the offset to it.
668pub const Memory = struct {
669 pointer: u32,
670 offset: u32,
698pub const UavRefOffObj = struct {
699 uav_obj: Wasm.UavsObjIndex,
700 offset: i32,
701};
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,
671711};
672712
673713/// Maps a source line with wasm bytecode
......@@ -675,3 +715,199 @@ pub const DbgLineColumn = struct {
675715 line: u32,
676716 column: u32,
677717};
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 };
2222/// Classifies a given Zig type to determine how they must be passed
2323/// or returned as value within a wasm function.
2424/// 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 {
2626 const ip = &zcu.intern_pool;
2727 const target = zcu.getTarget();
2828 if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return none;
src/arch/x86_64/CodeGen.zig+36-85
......@@ -19,7 +19,6 @@ const Allocator = mem.Allocator;
1919const CodeGenError = codegen.CodeGenError;
2020const Compilation = @import("../../Compilation.zig");
2121const ErrorMsg = Zcu.ErrorMsg;
22const Result = codegen.Result;
2322const Emit = @import("Emit.zig");
2423const Liveness = @import("../../Liveness.zig");
2524const Lower = @import("Lower.zig");
......@@ -59,7 +58,6 @@ target: *const std.Target,
5958owner: Owner,
6059inline_func: InternPool.Index,
6160mod: *Package.Module,
62err_msg: ?*ErrorMsg,
6361arg_index: u32,
6462args: []MCValue,
6563va_info: union {
......@@ -819,9 +817,9 @@ pub fn generate(
819817 func_index: InternPool.Index,
820818 air: Air,
821819 liveness: Liveness,
822 code: *std.ArrayList(u8),
820 code: *std.ArrayListUnmanaged(u8),
823821 debug_output: link.File.DebugInfoOutput,
824) CodeGenError!Result {
822) CodeGenError!void {
825823 const zcu = pt.zcu;
826824 const comp = zcu.comp;
827825 const gpa = zcu.gpa;
......@@ -841,7 +839,6 @@ pub fn generate(
841839 .debug_output = debug_output,
842840 .owner = .{ .nav_index = func.owner_nav },
843841 .inline_func = func_index,
844 .err_msg = null,
845842 .arg_index = undefined,
846843 .args = undefined, // populated after `resolveCallingConventionValues`
847844 .va_info = undefined, // populated after `resolveCallingConventionValues`
......@@ -881,15 +878,7 @@ pub fn generate(
881878 const fn_info = zcu.typeToFunc(fn_type).?;
882879 const cc = abi.resolveCallingConvention(fn_info.cc, function.target.*);
883880 var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) {
884 error.CodegenFail => return Result{ .fail = function.err_msg.? },
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 },
881 error.CodegenFail => return error.CodegenFail,
893882 else => |e| return e,
894883 };
895884 defer call_info.deinit(&function);
......@@ -926,10 +915,8 @@ pub fn generate(
926915 };
927916
928917 function.gen() catch |err| switch (err) {
929 error.CodegenFail => return Result{ .fail = function.err_msg.? },
930 error.OutOfRegisters => return Result{
931 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
932 },
918 error.CodegenFail => return error.CodegenFail,
919 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
933920 else => |e| return e,
934921 };
935922
......@@ -953,10 +940,7 @@ pub fn generate(
953940 .pic = mod.pic,
954941 },
955942 .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) {
956 error.CodegenFail => return Result{ .fail = function.err_msg.? },
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 },
943 error.CodegenFail => return error.CodegenFail,
960944 else => |e| return e,
961945 },
962946 .debug_output = debug_output,
......@@ -974,29 +958,11 @@ pub fn generate(
974958 };
975959 defer emit.deinit();
976960 emit.emitMir() catch |err| switch (err) {
977 error.LowerFail, error.EmitFail => return Result{ .fail = 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 };
961 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?),
994962
995 if (function.err_msg) |em| {
996 return Result{ .fail = em };
997 } else {
998 return Result.ok;
999 }
963 error.InvalidInstruction, error.CannotEncode => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}),
964 else => |e| return function.fail("emit MIR failed: {s}", .{@errorName(e)}),
965 };
1000966}
1001967
1002968pub fn generateLazy(
......@@ -1004,9 +970,9 @@ pub fn generateLazy(
1004970 pt: Zcu.PerThread,
1005971 src_loc: Zcu.LazySrcLoc,
1006972 lazy_sym: link.File.LazySymbol,
1007 code: *std.ArrayList(u8),
973 code: *std.ArrayListUnmanaged(u8),
1008974 debug_output: link.File.DebugInfoOutput,
1009) CodeGenError!Result {
975) CodeGenError!void {
1010976 const comp = bin_file.comp;
1011977 const gpa = comp.gpa;
1012978 // This function is for generating global code, so we use the root module.
......@@ -1022,7 +988,6 @@ pub fn generateLazy(
1022988 .debug_output = debug_output,
1023989 .owner = .{ .lazy_sym = lazy_sym },
1024990 .inline_func = undefined,
1025 .err_msg = null,
1026991 .arg_index = undefined,
1027992 .args = undefined,
1028993 .va_info = undefined,
......@@ -1038,10 +1003,8 @@ pub fn generateLazy(
10381003 }
10391004
10401005 function.genLazy(lazy_sym) catch |err| switch (err) {
1041 error.CodegenFail => return Result{ .fail = function.err_msg.? },
1042 error.OutOfRegisters => return Result{
1043 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
1044 },
1006 error.CodegenFail => return error.CodegenFail,
1007 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
10451008 else => |e| return e,
10461009 };
10471010
......@@ -1065,10 +1028,7 @@ pub fn generateLazy(
10651028 .pic = mod.pic,
10661029 },
10671030 .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) {
1068 error.CodegenFail => return Result{ .fail = function.err_msg.? },
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 },
1031 error.CodegenFail => return error.CodegenFail,
10721032 else => |e| return e,
10731033 },
10741034 .debug_output = debug_output,
......@@ -1078,29 +1038,11 @@ pub fn generateLazy(
10781038 };
10791039 defer emit.deinit();
10801040 emit.emitMir() catch |err| switch (err) {
1081 error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? },
1082 error.InvalidInstruction, error.CannotEncode => |e| {
1083 const msg = switch (e) {
1084 error.InvalidInstruction => "CodeGen failed to find a viable instruction.",
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,
1041 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?),
1042 error.InvalidInstruction => return function.fail("failed to find a viable x86 instruction (Zig compiler bug)", .{}),
1043 error.CannotEncode => return function.fail("failed to encode x86 instruction (Zig compiler bug)", .{}),
1044 else => |e| return function.fail("failed to emit MIR: {s}", .{@errorName(e)}),
10971045 };
1098
1099 if (function.err_msg) |em| {
1100 return Result{ .fail = em };
1101 } else {
1102 return Result.ok;
1103 }
11041046}
11051047
11061048const FormatNavData = struct {
......@@ -19276,10 +19218,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
1927619218 .load_got => |sym_index| .{ .lea_got = sym_index },
1927719219 .load_tlv => |sym_index| .{ .lea_tlv = sym_index },
1927819220 },
19279 .fail => |msg| {
19280 self.err_msg = msg;
19281 return error.CodegenFail;
19282 },
19221 .fail => |msg| return self.failMsg(msg),
1928319222 };
1928419223}
1928519224
......@@ -19592,11 +19531,23 @@ fn resolveCallingConventionValues(
1959219531 return result;
1959319532}
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 } {
1959619535 @branchHint(.cold);
19597 assert(self.err_msg == null);
19598 const gpa = self.gpa;
19599 self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args);
19536 const zcu = self.pt.zcu;
19537 switch (self.owner) {
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 }
1960019551 return error.CodegenFail;
1960119552}
1960219553
src/arch/x86_64/Emit.zig+8-7
......@@ -4,7 +4,7 @@ air: Air,
44lower: Lower,
55atom_index: u32,
66debug_output: link.File.DebugInfoOutput,
7code: *std.ArrayList(u8),
7code: *std.ArrayListUnmanaged(u8),
88
99prev_di_loc: Loc,
1010/// Relative to the beginning of `code`.
......@@ -18,6 +18,7 @@ pub const Error = Lower.Error || error{
1818} || link.File.UpdateDebugInfoError;
1919
2020pub fn emitMir(emit: *Emit) Error!void {
21 const gpa = emit.lower.bin_file.comp.gpa;
2122 for (0..emit.lower.mir.instructions.len) |mir_i| {
2223 const mir_index: Mir.Inst.Index = @intCast(mir_i);
2324 try emit.code_offset_mapping.putNoClobber(
......@@ -82,7 +83,7 @@ pub fn emitMir(emit: *Emit) Error!void {
8283 }
8384 continue;
8485 }
85 try lowered_inst.encode(emit.code.writer(), .{});
86 try lowered_inst.encode(emit.code.writer(gpa), .{});
8687 const end_offset: u32 = @intCast(emit.code.items.len);
8788 while (lowered_relocs.len > 0 and
8889 lowered_relocs[0].lowered_inst_index == lowered_index) : ({
......@@ -100,7 +101,7 @@ pub fn emitMir(emit: *Emit) Error!void {
100101 const zo = elf_file.zigObjectPtr().?;
101102 const atom_ptr = zo.symbol(emit.atom_index).atom(elf_file).?;
102103 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, .{
104105 .r_offset = end_offset - 4,
105106 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
106107 .r_addend = lowered_relocs[0].off - 4,
......@@ -147,7 +148,7 @@ pub fn emitMir(emit: *Emit) Error!void {
147148 const zo = elf_file.zigObjectPtr().?;
148149 const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
149150 const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD);
150 try atom.addReloc(elf_file.base.comp.gpa, .{
151 try atom.addReloc(gpa, .{
151152 .r_offset = end_offset - 4,
152153 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
153154 .r_addend = lowered_relocs[0].off - 4,
......@@ -158,7 +159,7 @@ pub fn emitMir(emit: *Emit) Error!void {
158159 const zo = elf_file.zigObjectPtr().?;
159160 const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
160161 const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32);
161 try atom.addReloc(elf_file.base.comp.gpa, .{
162 try atom.addReloc(gpa, .{
162163 .r_offset = end_offset - 4,
163164 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
164165 .r_addend = lowered_relocs[0].off,
......@@ -173,7 +174,7 @@ pub fn emitMir(emit: *Emit) Error!void {
173174 @intFromEnum(std.elf.R_X86_64.GOTPCREL)
174175 else
175176 @intFromEnum(std.elf.R_X86_64.PC32);
176 try atom.addReloc(elf_file.base.comp.gpa, .{
177 try atom.addReloc(gpa, .{
177178 .r_offset = end_offset - 4,
178179 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
179180 .r_addend = lowered_relocs[0].off - 4,
......@@ -183,7 +184,7 @@ pub fn emitMir(emit: *Emit) Error!void {
183184 @intFromEnum(std.elf.R_X86_64.TPOFF32)
184185 else
185186 @intFromEnum(std.elf.R_X86_64.@"32");
186 try atom.addReloc(elf_file.base.comp.gpa, .{
187 try atom.addReloc(gpa, .{
187188 .r_offset = end_offset - 4,
188189 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
189190 .r_addend = lowered_relocs[0].off,
src/codegen.zig+199-181
......@@ -2,7 +2,6 @@ const std = @import("std");
22const build_options = @import("build_options");
33const builtin = @import("builtin");
44const assert = std.debug.assert;
5const leb128 = std.leb;
65const link = @import("link.zig");
76const log = std.log.scoped(.codegen);
87const mem = std.mem;
......@@ -24,19 +23,13 @@ const Zir = std.zig.Zir;
2423const Alignment = InternPool.Alignment;
2524const 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
3526pub const CodeGenError = error{
3627 OutOfMemory,
28 /// Compiler was asked to operate on a number larger than supported.
3729 Overflow,
30 /// Indicates the error is already stored in Zcu `failed_codegen`.
3831 CodegenFail,
39} || link.File.UpdateDebugInfoError;
32};
4033
4134fn devFeatureForBackend(comptime backend: std.builtin.CompilerBackend) dev.Feature {
4235 comptime assert(mem.startsWith(u8, @tagName(backend), "stage2_"));
......@@ -49,7 +42,6 @@ fn importBackend(comptime backend: std.builtin.CompilerBackend) type {
4942 .stage2_arm => @import("arch/arm/CodeGen.zig"),
5043 .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"),
5144 .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"),
52 .stage2_wasm => @import("arch/wasm/CodeGen.zig"),
5345 .stage2_x86_64 => @import("arch/x86_64/CodeGen.zig"),
5446 else => unreachable,
5547 };
......@@ -62,9 +54,9 @@ pub fn generateFunction(
6254 func_index: InternPool.Index,
6355 air: Air,
6456 liveness: Liveness,
65 code: *std.ArrayList(u8),
57 code: *std.ArrayListUnmanaged(u8),
6658 debug_output: link.File.DebugInfoOutput,
67) CodeGenError!Result {
59) CodeGenError!void {
6860 const zcu = pt.zcu;
6961 const func = zcu.funcInfo(func_index);
7062 const target = zcu.navFileScope(func.owner_nav).mod.resolved_target.result;
......@@ -74,7 +66,6 @@ pub fn generateFunction(
7466 .stage2_arm,
7567 .stage2_riscv64,
7668 .stage2_sparc64,
77 .stage2_wasm,
7869 .stage2_x86_64,
7970 => |backend| {
8071 dev.check(devFeatureForBackend(backend));
......@@ -88,17 +79,15 @@ pub fn generateLazyFunction(
8879 pt: Zcu.PerThread,
8980 src_loc: Zcu.LazySrcLoc,
9081 lazy_sym: link.File.LazySymbol,
91 code: *std.ArrayList(u8),
82 code: *std.ArrayListUnmanaged(u8),
9283 debug_output: link.File.DebugInfoOutput,
93) CodeGenError!Result {
84) CodeGenError!void {
9485 const zcu = pt.zcu;
9586 const file = Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(&zcu.intern_pool);
9687 const target = zcu.fileByIndex(file).mod.resolved_target.result;
9788 switch (target_util.zigBackend(target, false)) {
9889 else => unreachable,
99 inline .stage2_x86_64,
100 .stage2_riscv64,
101 => |backend| {
90 inline .stage2_x86_64, .stage2_riscv64 => |backend| {
10291 dev.check(devFeatureForBackend(backend));
10392 return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output);
10493 },
......@@ -120,20 +109,21 @@ pub fn generateLazySymbol(
120109 lazy_sym: link.File.LazySymbol,
121110 // TODO don't use an "out" parameter like this; put it in the result instead
122111 alignment: *Alignment,
123 code: *std.ArrayList(u8),
112 code: *std.ArrayListUnmanaged(u8),
124113 debug_output: link.File.DebugInfoOutput,
125114 reloc_parent: link.File.RelocInfo.Parent,
126) CodeGenError!Result {
115) CodeGenError!void {
127116 _ = reloc_parent;
128117
129118 const tracy = trace(@src());
130119 defer tracy.end();
131120
132121 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;
134125 const target = comp.root_mod.resolved_target.result;
135126 const endian = target.cpu.arch.endian();
136 const gpa = comp.gpa;
137127
138128 log.debug("generateLazySymbol: kind = {s}, ty = {}", .{
139129 @tagName(lazy_sym.kind),
......@@ -150,52 +140,56 @@ pub fn generateLazySymbol(
150140 const err_names = ip.global_error_set.getNamesFromMainThread();
151141 var offset_index: u32 = @intCast(code.items.len);
152142 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);
154144 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;
156146 offset_index += 4;
157147 for (err_names) |err_name_nts| {
158148 const err_name = err_name_nts.toSlice(ip);
159149 mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian);
160150 offset_index += 4;
161 try code.ensureUnusedCapacity(err_name.len + 1);
151 try code.ensureUnusedCapacity(gpa, err_name.len + 1);
162152 code.appendSliceAssumeCapacity(err_name);
163153 code.appendAssumeCapacity(0);
164154 string_index += @intCast(err_name.len + 1);
165155 }
166156 mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian);
167 return .ok;
168 } else if (Type.fromInterned(lazy_sym.ty).zigTypeTag(pt.zcu) == .@"enum") {
157 } else if (Type.fromInterned(lazy_sym.ty).zigTypeTag(zcu) == .@"enum") {
169158 alignment.* = .@"1";
170159 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);
172161 for (0..tag_names.len) |tag_index| {
173162 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);
175164 code.appendSliceAssumeCapacity(tag_name);
176165 code.appendAssumeCapacity(0);
177166 }
178 return .ok;
179 } else return .{ .fail = try .create(
180 gpa,
181 src_loc,
182 "TODO implement generateLazySymbol for {s} {}",
183 .{ @tagName(lazy_sym.kind), Type.fromInterned(lazy_sym.ty).fmt(pt) },
184 ) };
167 } else {
168 return zcu.codegenFailType(lazy_sym.ty, "TODO implement generateLazySymbol for {s} {}", .{
169 @tagName(lazy_sym.kind), Type.fromInterned(lazy_sym.ty).fmt(pt),
170 });
171 }
185172}
186173
174pub const GenerateSymbolError = error{
175 OutOfMemory,
176 /// Compiler was asked to operate on a number larger than supported.
177 Overflow,
178};
179
187180pub fn generateSymbol(
188181 bin_file: *link.File,
189182 pt: Zcu.PerThread,
190183 src_loc: Zcu.LazySrcLoc,
191184 val: Value,
192 code: *std.ArrayList(u8),
185 code: *std.ArrayListUnmanaged(u8),
193186 reloc_parent: link.File.RelocInfo.Parent,
194) CodeGenError!Result {
187) GenerateSymbolError!void {
195188 const tracy = trace(@src());
196189 defer tracy.end();
197190
198191 const zcu = pt.zcu;
192 const gpa = zcu.gpa;
199193 const ip = &zcu.intern_pool;
200194 const ty = val.typeOf(zcu);
201195
......@@ -206,8 +200,8 @@ pub fn generateSymbol(
206200
207201 if (val.isUndefDeep(zcu)) {
208202 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
209 try code.appendNTimes(0xaa, abi_size);
210 return .ok;
203 try code.appendNTimes(gpa, 0xaa, abi_size);
204 return;
211205 }
212206
213207 switch (ip.indexToKey(val.toIntern())) {
......@@ -231,14 +225,13 @@ pub fn generateSymbol(
231225
232226 .undef => unreachable, // handled above
233227 .simple_value => |simple_value| switch (simple_value) {
234 .undefined,
235 .void,
236 .null,
237 .empty_tuple,
238 .@"unreachable",
239 .generic_poison,
240 => unreachable, // non-runtime values
241 .false, .true => try code.append(switch (simple_value) {
228 .undefined => unreachable, // non-runtime value
229 .void => unreachable, // non-runtime value
230 .null => unreachable, // non-runtime value
231 .@"unreachable" => unreachable, // non-runtime value
232 .generic_poison => unreachable, // non-runtime value
233 .empty_tuple => return,
234 .false, .true => try code.append(gpa, switch (simple_value) {
242235 .false => 0,
243236 .true => 1,
244237 else => unreachable,
......@@ -254,11 +247,11 @@ pub fn generateSymbol(
254247 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
255248 var space: Value.BigIntSpace = undefined;
256249 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);
258251 },
259252 .err => |err| {
260253 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);
262255 },
263256 .error_union => |error_union| {
264257 const payload_ty = ty.errorUnionPayload(zcu);
......@@ -268,8 +261,8 @@ pub fn generateSymbol(
268261 };
269262
270263 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
271 try code.writer().writeInt(u16, err_val, endian);
272 return .ok;
264 try code.writer(gpa).writeInt(u16, err_val, endian);
265 return;
273266 }
274267
275268 const payload_align = payload_ty.abiAlignment(zcu);
......@@ -278,72 +271,57 @@ pub fn generateSymbol(
278271
279272 // error value first when its type is larger than the error union's payload
280273 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);
282275 }
283276
284277 // emit payload part of the error union
285278 {
286279 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) {
288281 .err_name => try pt.intern(.{ .undef = payload_ty.toIntern() }),
289282 .payload => |payload| payload,
290 }), code, reloc_parent)) {
291 .ok => {},
292 .fail => |em| return .{ .fail = em },
293 }
283 }), code, reloc_parent);
294284 const unpadded_end = code.items.len - begin;
295285 const padded_end = abi_align.forward(unpadded_end);
296286 const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow;
297287
298288 if (padding > 0) {
299 try code.appendNTimes(0, padding);
289 try code.appendNTimes(gpa, 0, padding);
300290 }
301291 }
302292
303293 // Payload size is larger than error set, so emit our error set last
304294 if (error_align.compare(.lte, payload_align)) {
305295 const begin = code.items.len;
306 try code.writer().writeInt(u16, err_val, endian);
296 try code.writer(gpa).writeInt(u16, err_val, endian);
307297 const unpadded_end = code.items.len - begin;
308298 const padded_end = abi_align.forward(unpadded_end);
309299 const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow;
310300
311301 if (padding > 0) {
312 try code.appendNTimes(0, padding);
302 try code.appendNTimes(gpa, 0, padding);
313303 }
314304 }
315305 },
316306 .enum_tag => |enum_tag| {
317307 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)) {
319 .ok => {},
320 .fail => |em| return .{ .fail = em },
321 }
308 try generateSymbol(bin_file, pt, src_loc, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), code, reloc_parent);
322309 },
323310 .float => |float| switch (float.storage) {
324 .f16 => |f16_val| writeFloat(f16, f16_val, target, endian, try code.addManyAsArray(2)),
325 .f32 => |f32_val| writeFloat(f32, f32_val, target, endian, try code.addManyAsArray(4)),
326 .f64 => |f64_val| writeFloat(f64, f64_val, target, endian, try code.addManyAsArray(8)),
311 .f16 => |f16_val| writeFloat(f16, f16_val, target, endian, try code.addManyAsArray(gpa, 2)),
312 .f32 => |f32_val| writeFloat(f32, f32_val, target, endian, try code.addManyAsArray(gpa, 4)),
313 .f64 => |f64_val| writeFloat(f64, f64_val, target, endian, try code.addManyAsArray(gpa, 8)),
327314 .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));
329316 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);
331318 },
332 .f128 => |f128_val| writeFloat(f128, f128_val, target, endian, try code.addManyAsArray(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 },
319 .f128 => |f128_val| writeFloat(f128, f128_val, target, endian, try code.addManyAsArray(gpa, 16)),
337320 },
321 .ptr => try lowerPtr(bin_file, pt, src_loc, val.toIntern(), code, reloc_parent, 0),
338322 .slice => |slice| {
339 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), code, reloc_parent)) {
340 .ok => {},
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 }
323 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), code, reloc_parent);
324 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.len), code, reloc_parent);
347325 },
348326 .opt => {
349327 const payload_type = ty.optionalChild(zcu);
......@@ -352,12 +330,9 @@ pub fn generateSymbol(
352330
353331 if (ty.optionalReprIsPayload(zcu)) {
354332 if (payload_val) |value| {
355 switch (try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent)) {
356 .ok => {},
357 .fail => |em| return Result{ .fail = em },
358 }
333 try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent);
359334 } else {
360 try code.appendNTimes(0, abi_size);
335 try code.appendNTimes(gpa, 0, abi_size);
361336 }
362337 } else {
363338 const padding = abi_size - (math.cast(usize, payload_type.abiSize(zcu)) orelse return error.Overflow) - 1;
......@@ -365,39 +340,33 @@ pub fn generateSymbol(
365340 const value = payload_val orelse Value.fromInterned(try pt.intern(.{
366341 .undef = payload_type.toIntern(),
367342 }));
368 switch (try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent)) {
369 .ok => {},
370 .fail => |em| return Result{ .fail = em },
371 }
343 try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent);
372344 }
373 try code.writer().writeByte(@intFromBool(payload_val != null));
374 try code.appendNTimes(0, padding);
345 try code.writer(gpa).writeByte(@intFromBool(payload_val != null));
346 try code.appendNTimes(gpa, 0, padding);
375347 }
376348 },
377349 .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) {
378350 .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)),
380352 .elems, .repeated_elem => {
381353 var index: u64 = 0;
382354 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) {
384356 .bytes => unreachable,
385357 .elems => |elems| elems[@intCast(index)],
386358 .repeated_elem => |elem| if (index < array_type.len)
387359 elem
388360 else
389361 array_type.sentinel,
390 }), code, reloc_parent)) {
391 .ok => {},
392 .fail => |em| return .{ .fail = em },
393 }
362 }), code, reloc_parent);
394363 }
395364 },
396365 },
397366 .vector_type => |vector_type| {
398367 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
399368 if (vector_type.child == .bool_type) {
400 const bytes = try code.addManyAsSlice(abi_size);
369 const bytes = try code.addManyAsSlice(gpa, abi_size);
401370 @memset(bytes, 0xaa);
402371 var index: usize = 0;
403372 const len = math.cast(usize, vector_type.len) orelse return error.Overflow;
......@@ -436,20 +405,17 @@ pub fn generateSymbol(
436405 }
437406 } else {
438407 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)),
440409 .elems, .repeated_elem => {
441410 var index: u64 = 0;
442411 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) {
444413 .bytes => unreachable,
445414 .elems => |elems| elems[
446415 math.cast(usize, index) orelse return error.Overflow
447416 ],
448417 .repeated_elem => |elem| elem,
449 }), code, reloc_parent)) {
450 .ok => {},
451 .fail => |em| return .{ .fail = em },
452 }
418 }), code, reloc_parent);
453419 }
454420 },
455421 }
......@@ -457,7 +423,7 @@ pub fn generateSymbol(
457423 const padding = abi_size -
458424 (math.cast(usize, Type.fromInterned(vector_type.child).abiSize(zcu) * vector_type.len) orelse
459425 return error.Overflow);
460 if (padding > 0) try code.appendNTimes(0, padding);
426 if (padding > 0) try code.appendNTimes(gpa, 0, padding);
461427 }
462428 },
463429 .tuple_type => |tuple| {
......@@ -479,10 +445,7 @@ pub fn generateSymbol(
479445 .repeated_elem => |elem| elem,
480446 };
481447
482 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent)) {
483 .ok => {},
484 .fail => |em| return Result{ .fail = em },
485 }
448 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent);
486449 const unpadded_field_end = code.items.len - struct_begin;
487450
488451 // Pad struct members if required
......@@ -491,7 +454,7 @@ pub fn generateSymbol(
491454 return error.Overflow;
492455
493456 if (padding > 0) {
494 try code.appendNTimes(0, padding);
457 try code.appendNTimes(gpa, 0, padding);
495458 }
496459 }
497460 },
......@@ -501,7 +464,7 @@ pub fn generateSymbol(
501464 .@"packed" => {
502465 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
503466 const current_pos = code.items.len;
504 try code.appendNTimes(0, abi_size);
467 try code.appendNTimes(gpa, 0, abi_size);
505468 var bits: u16 = 0;
506469
507470 for (struct_type.field_types.get(ip), 0..) |field_ty, index| {
......@@ -519,12 +482,10 @@ pub fn generateSymbol(
519482 if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .pointer) {
520483 const field_size = math.cast(usize, Type.fromInterned(field_ty).abiSize(zcu)) orelse
521484 return error.Overflow;
522 var tmp_list = try std.ArrayList(u8).initCapacity(code.allocator, field_size);
523 defer tmp_list.deinit();
524 switch (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),
526 .fail => |em| return Result{ .fail = em },
527 }
485 var tmp_list = try std.ArrayListUnmanaged(u8).initCapacity(gpa, field_size);
486 defer tmp_list.deinit(gpa);
487 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), &tmp_list, reloc_parent);
488 @memcpy(code.items[current_pos..][0..tmp_list.items.len], tmp_list.items);
528489 } else {
529490 Value.fromInterned(field_val).writeToPackedMemory(Type.fromInterned(field_ty), pt, code.items[current_pos..], bits) catch unreachable;
530491 }
......@@ -554,12 +515,9 @@ pub fn generateSymbol(
554515 usize,
555516 offsets[field_index] - (code.items.len - struct_begin),
556517 ) 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)) {
560 .ok => {},
561 .fail => |em| return Result{ .fail = em },
562 }
520 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent);
563521 }
564522
565523 const size = struct_type.sizeUnordered(ip);
......@@ -570,7 +528,7 @@ pub fn generateSymbol(
570528 std.mem.alignForward(u64, size, @max(alignment, 1)) -
571529 (code.items.len - struct_begin),
572530 ) orelse return error.Overflow;
573 if (padding > 0) try code.appendNTimes(0, padding);
531 if (padding > 0) try code.appendNTimes(gpa, 0, padding);
574532 },
575533 }
576534 },
......@@ -585,10 +543,7 @@ pub fn generateSymbol(
585543
586544 // Check if we should store the tag first.
587545 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)) {
589 .ok => {},
590 .fail => |em| return Result{ .fail = em },
591 }
546 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent);
592547 }
593548
594549 const union_obj = zcu.typeToUnion(ty).?;
......@@ -596,39 +551,29 @@ pub fn generateSymbol(
596551 const field_index = ty.unionTagFieldIndex(Value.fromInterned(un.tag), zcu).?;
597552 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
598553 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);
600555 } else {
601 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent)) {
602 .ok => {},
603 .fail => |em| return Result{ .fail = em },
604 }
556 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent);
605557
606558 const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(zcu)) orelse return error.Overflow;
607559 if (padding > 0) {
608 try code.appendNTimes(0, padding);
560 try code.appendNTimes(gpa, 0, padding);
609561 }
610562 }
611563 } else {
612 switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent)) {
613 .ok => {},
614 .fail => |em| return Result{ .fail = em },
615 }
564 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent);
616565 }
617566
618567 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)) {
620 .ok => {},
621 .fail => |em| return Result{ .fail = em },
622 }
568 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent);
623569
624570 if (layout.padding > 0) {
625 try code.appendNTimes(0, layout.padding);
571 try code.appendNTimes(gpa, 0, layout.padding);
626572 }
627573 }
628574 },
629575 .memoized_call => unreachable,
630576 }
631 return .ok;
632577}
633578
634579fn lowerPtr(
......@@ -636,15 +581,15 @@ fn lowerPtr(
636581 pt: Zcu.PerThread,
637582 src_loc: Zcu.LazySrcLoc,
638583 ptr_val: InternPool.Index,
639 code: *std.ArrayList(u8),
584 code: *std.ArrayListUnmanaged(u8),
640585 reloc_parent: link.File.RelocInfo.Parent,
641586 prev_offset: u64,
642) CodeGenError!Result {
587) GenerateSymbolError!void {
643588 const zcu = pt.zcu;
644589 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;
645590 const offset: u64 = prev_offset + ptr.byte_offset;
646591 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),
648593 .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, code, reloc_parent, offset),
649594 .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), code, reloc_parent),
650595 .eu_payload => |eu_ptr| try lowerPtr(
......@@ -689,29 +634,62 @@ fn lowerUavRef(
689634 pt: Zcu.PerThread,
690635 src_loc: Zcu.LazySrcLoc,
691636 uav: InternPool.Key.Ptr.BaseAddr.Uav,
692 code: *std.ArrayList(u8),
637 code: *std.ArrayListUnmanaged(u8),
693638 reloc_parent: link.File.RelocInfo.Parent,
694639 offset: u64,
695) CodeGenError!Result {
640) GenerateSymbolError!void {
696641 const zcu = pt.zcu;
642 const gpa = zcu.gpa;
697643 const ip = &zcu.intern_pool;
698 const target = lf.comp.root_mod.resolved_target.result;
699
644 const comp = lf.comp;
645 const target = &comp.root_mod.resolved_target.result;
700646 const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8);
647 const is_obj = comp.config.output_mode == .Obj;
701648 const uav_val = uav.val;
702649 const uav_ty = Type.fromInterned(ip.typeOf(uav_val));
703 log.debug("lowerUavRef: ty = {}", .{uav_ty.fmt(pt)});
704650 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
705655 if (!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) {
706 try code.appendNTimes(0xaa, ptr_width_bytes);
707 return Result.ok;
656 code.appendNTimesAssumeCapacity(0xaa, ptr_width_bytes);
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 => {},
708687 }
709688
710689 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);
712 switch (res) {
690 switch (try lf.lowerUav(pt, uav_val, uav_align, src_loc)) {
713691 .mcv => {},
714 .fail => |em| return .{ .fail = em },
692 .fail => |em| std.debug.panic("TODO rework lowerUav. internal error: {s}", .{em.msg}),
715693 }
716694
717695 const vaddr = try lf.getUavVAddr(uav_val, .{
......@@ -721,51 +699,91 @@ fn lowerUavRef(
721699 });
722700 const endian = target.cpu.arch.endian();
723701 switch (ptr_width_bytes) {
724 2 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian),
725 4 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian),
726 8 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian),
702 2 => mem.writeInt(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(vaddr), endian),
703 4 => mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(vaddr), endian),
704 8 => mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), vaddr, endian),
727705 else => unreachable,
728706 }
729
730 return Result.ok;
731707}
732708
733709fn lowerNavRef(
734710 lf: *link.File,
735711 pt: Zcu.PerThread,
736 src_loc: Zcu.LazySrcLoc,
737712 nav_index: InternPool.Nav.Index,
738 code: *std.ArrayList(u8),
713 code: *std.ArrayListUnmanaged(u8),
739714 reloc_parent: link.File.RelocInfo.Parent,
740715 offset: u64,
741) CodeGenError!Result {
742 _ = src_loc;
716) GenerateSymbolError!void {
743717 const zcu = pt.zcu;
718 const gpa = zcu.gpa;
744719 const ip = &zcu.intern_pool;
745720 const target = zcu.navFileScope(nav_index).mod.resolved_target.result;
746
747 const ptr_width = target.ptrBitWidth();
721 const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8);
722 const is_obj = lf.comp.config.output_mode == .Obj;
748723 const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip));
749724 const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn";
725
726 try code.ensureUnusedCapacity(gpa, ptr_width_bytes);
727
750728 if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) {
751 try code.appendNTimes(0xaa, @divExact(ptr_width, 8));
752 return Result.ok;
729 code.appendNTimesAssumeCapacity(0xaa, ptr_width_bytes);
730 return;
753731 }
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, .{
756776 .parent = reloc_parent,
757777 .offset = code.items.len,
758778 .addend = @intCast(offset),
759 });
779 }) catch @panic("TODO rework getNavVAddr");
760780 const endian = target.cpu.arch.endian();
761 switch (ptr_width) {
762 16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian),
763 32 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian),
764 64 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian),
781 switch (ptr_width_bytes) {
782 2 => mem.writeInt(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(vaddr), endian),
783 4 => mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(vaddr), endian),
784 8 => mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), vaddr, endian),
765785 else => unreachable,
766786 }
767
768 return Result.ok;
769787}
770788
771789/// 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(
30523052 try w.writeAll(";\n");
30533053}
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 {
30563056 const zcu = dg.pt.zcu;
30573057 const ip = &zcu.intern_pool;
30583058 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;
30613061 try fwd.writeAll("#define ");
30623062 switch (exported) {
30633063 .nav => |nav| try dg.renderNavName(fwd, nav),
......@@ -3069,7 +3069,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
30693069
30703070 const exported_val = exported.getValue(zcu);
30713071 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);
30733073 try fwd.writeAll("zig_extern ");
30743074 if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn ");
30753075 try dg.renderFunctionSignature(
......@@ -3091,7 +3091,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
30913091 else => true,
30923092 };
30933093 for (export_indices) |export_index| {
3094 const @"export" = &zcu.all_exports.items[export_index];
3094 const @"export" = export_index.ptr(zcu);
30953095 try fwd.writeAll("zig_extern ");
30963096 if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage ");
30973097 const extern_name = @"export".opts.name.toSlice(ip);
src/codegen/llvm.zig+22-32
......@@ -1059,9 +1059,10 @@ pub const Object = struct {
10591059 lto: Compilation.Config.LtoMode,
10601060 };
10611061
1062 pub fn emit(o: *Object, options: EmitOptions) !void {
1062 pub fn emit(o: *Object, options: EmitOptions) error{ LinkFailure, OutOfMemory }!void {
10631063 const zcu = o.pt.zcu;
10641064 const comp = zcu.comp;
1065 const diags = &comp.link_diags;
10651066
10661067 {
10671068 try o.genErrorNameTable();
......@@ -1223,27 +1224,30 @@ pub const Object = struct {
12231224 o.builder.clearAndFree();
12241225
12251226 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) });
12271229 defer file.close();
12281230
12291231 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) });
12311234 }
12321235
12331236 if (options.asm_path == null and options.bin_path == null and
12341237 options.post_ir_path == null and options.post_bc_path == null) return;
12351238
12361239 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) });
12381242 defer file.close();
12391243
12401244 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) });
12421247 }
12431248
12441249 if (!build_options.have_llvm or !comp.config.use_lib_llvm) {
1245 log.err("emitting without libllvm not implemented", .{});
1246 return error.FailedToEmit;
1250 return diags.fail("emitting without libllvm not implemented", .{});
12471251 }
12481252
12491253 initializeLLVMTarget(comp.root_mod.resolved_target.result.cpu.arch);
......@@ -1263,8 +1267,7 @@ pub const Object = struct {
12631267
12641268 var module: *llvm.Module = undefined;
12651269 if (context.parseBitcodeInContext2(bitcode_memory_buffer, &module).toBool() or context.getBrokenDebugInfo()) {
1266 log.err("Failed to parse bitcode", .{});
1267 return error.FailedToEmit;
1270 return diags.fail("Failed to parse bitcode", .{});
12681271 }
12691272 break :emit .{ context, module };
12701273 };
......@@ -1274,12 +1277,7 @@ pub const Object = struct {
12741277 var error_message: [*:0]const u8 = undefined;
12751278 if (llvm.Target.getFromTriple(target_triple_sentinel, &target, &error_message).toBool()) {
12761279 defer llvm.disposeMessage(error_message);
1277
1278 log.err("LLVM failed to parse '{s}': {s}", .{
1279 target_triple_sentinel,
1280 error_message,
1281 });
1282 @panic("Invalid LLVM triple");
1280 return diags.fail("LLVM failed to parse '{s}': {s}", .{ target_triple_sentinel, error_message });
12831281 }
12841282
12851283 const optimize_mode = comp.root_mod.optimize_mode;
......@@ -1374,10 +1372,9 @@ pub const Object = struct {
13741372 if (options.asm_path != null and options.bin_path != null) {
13751373 if (target_machine.emitToFile(module, &error_message, &lowered_options)) {
13761374 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}", .{
13781376 emit_bin_msg, post_llvm_ir_msg, error_message,
13791377 });
1380 return error.FailedToEmit;
13811378 }
13821379 lowered_options.bin_filename = null;
13831380 lowered_options.llvm_ir_filename = null;
......@@ -1386,11 +1383,9 @@ pub const Object = struct {
13861383 lowered_options.asm_filename = options.asm_path;
13871384 if (target_machine.emitToFile(module, &error_message, &lowered_options)) {
13881385 defer llvm.disposeMessage(error_message);
1389 log.err("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,
1391 error_message,
1386 return diags.fail("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{
1387 emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, error_message,
13921388 });
1393 return error.FailedToEmit;
13941389 }
13951390 }
13961391
......@@ -1815,7 +1810,7 @@ pub const Object = struct {
18151810 self: *Object,
18161811 pt: Zcu.PerThread,
18171812 exported: Zcu.Exported,
1818 export_indices: []const u32,
1813 export_indices: []const Zcu.Export.Index,
18191814 ) link.File.UpdateExportsError!void {
18201815 assert(std.meta.eql(pt, self.pt));
18211816 const zcu = pt.zcu;
......@@ -1843,11 +1838,11 @@ pub const Object = struct {
18431838 o: *Object,
18441839 zcu: *Zcu,
18451840 exported_value: InternPool.Index,
1846 export_indices: []const u32,
1841 export_indices: []const Zcu.Export.Index,
18471842 ) link.File.UpdateExportsError!void {
18481843 const gpa = zcu.gpa;
18491844 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));
18511846 const global_index = i: {
18521847 const gop = try o.uav_map.getOrPut(gpa, exported_value);
18531848 if (gop.found_existing) {
......@@ -1878,11 +1873,11 @@ pub const Object = struct {
18781873 o: *Object,
18791874 zcu: *Zcu,
18801875 global_index: Builder.Global.Index,
1881 export_indices: []const u32,
1876 export_indices: []const Zcu.Export.Index,
18821877 ) link.File.UpdateExportsError!void {
18831878 const comp = zcu.comp;
18841879 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
18871882 // We will rename this global to have a name matching `first_export`.
18881883 // Successive exports become aliases.
......@@ -1939,7 +1934,7 @@ pub const Object = struct {
19391934 // Until then we iterate over existing aliases and make them point
19401935 // to the correct decl, or otherwise add a new alias. Old aliases are leaked.
19411936 for (export_indices[1..]) |export_idx| {
1942 const exp = zcu.all_exports.items[export_idx];
1937 const exp = export_idx.ptr(zcu);
19431938 const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip));
19441939 if (o.builder.getGlobal(exp_name)) |global| {
19451940 switch (global.ptrConst(&o.builder).kind) {
......@@ -1967,11 +1962,6 @@ pub const Object = struct {
19671962 }
19681963 }
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
19751965 fn getDebugFile(o: *Object, file_index: Zcu.File.Index) Allocator.Error!Builder.Metadata {
19761966 const gpa = o.gpa;
19771967 const gop = try o.debug_file_map.getOrPut(gpa, file_index);
src/dev.zig+12
......@@ -30,6 +30,10 @@ pub const Env = enum {
3030 /// - `zig build-* -fno-llvm -fno-lld -target riscv64-linux`
3131 @"riscv64-linux",
3232
33 /// - sema
34 /// - `zig build-* -fno-llvm -fno-lld -target wasm32-* --listen=-`
35 wasm,
36
3337 pub inline fn supports(comptime dev_env: Env, comptime feature: Feature) bool {
3438 return switch (dev_env) {
3539 .full => true,
......@@ -144,6 +148,14 @@ pub const Env = enum {
144148 => true,
145149 else => Env.sema.supports(feature),
146150 },
151 .wasm => switch (feature) {
152 .stdio_listen,
153 .incremental,
154 .wasm_backend,
155 .wasm_linker,
156 => true,
157 else => Env.sema.supports(feature),
158 },
147159 };
148160 }
149161
src/glibc.zig+12
......@@ -1217,6 +1217,18 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
12171217 });
12181218}
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
12201232fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
12211233 const target_version = comp.getTarget().os.versionRange().gnuLibCVersion().?;
12221234
src/link.zig+181-156
......@@ -38,6 +38,11 @@ pub const Diags = struct {
3838 flags: Flags,
3939 lld: std.ArrayListUnmanaged(Lld),
4040
41 pub const SourceLocation = union(enum) {
42 none,
43 wasm: File.Wasm.SourceLocation,
44 };
45
4146 pub const Flags = packed struct {
4247 no_entry_point_found: bool = false,
4348 missing_libc: bool = false,
......@@ -70,9 +75,25 @@ pub const Diags = struct {
7075 };
7176
7277 pub const Msg = struct {
78 source_location: SourceLocation = .none,
7379 msg: []const u8,
7480 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
7697 pub fn deinit(self: *Msg, gpa: Allocator) void {
7798 for (self.notes) |*note| note.deinit(gpa);
7899 gpa.free(self.notes);
......@@ -97,15 +118,12 @@ pub const Diags = struct {
97118 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
98119 }
99120
100 pub fn addNote(
101 err: *ErrorWithNotes,
102 comptime format: []const u8,
103 args: anytype,
104 ) error{OutOfMemory}!void {
121 pub fn addNote(err: *ErrorWithNotes, comptime format: []const u8, args: anytype) void {
105122 const gpa = err.diags.gpa;
123 const msg = std.fmt.allocPrint(gpa, format, args) catch return err.diags.setAllocFailure();
106124 const err_msg = &err.diags.msgs.items[err.index];
107125 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 };
109127 err.note_slot += 1;
110128 }
111129 };
......@@ -196,22 +214,35 @@ pub const Diags = struct {
196214 return error.LinkFailure;
197215 }
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
199223 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 {
200228 @branchHint(.cold);
201229 const gpa = diags.gpa;
202230 const eu_main_msg = std.fmt.allocPrint(gpa, format, args);
203231 diags.mutex.lock();
204232 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) {
206234 error.OutOfMemory => diags.setAllocFailureLocked(),
207235 };
208236 }
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 {
211239 const gpa = diags.gpa;
212240 const main_msg = try eu_main_msg;
213241 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 });
215246 }
216247
217248 pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
......@@ -329,16 +360,16 @@ pub const Diags = struct {
329360 diags.flags.alloc_failure_occurred = true;
330361 }
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 {
333364 for (diags.msgs.items) |link_err| {
334365 try bundle.addRootErrorMessage(.{
335 .msg = try bundle.addString(link_err.msg),
366 .msg = try link_err.string(bundle, base),
336367 .notes_len = @intCast(link_err.notes.len),
337368 });
338369 const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
339370 for (link_err.notes, 0..) |note, i| {
340371 bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
341 .msg = try bundle.addString(note.msg),
372 .msg = try note.string(bundle, base),
342373 }));
343374 }
344375 }
......@@ -364,6 +395,7 @@ pub const File = struct {
364395 build_id: std.zig.BuildId,
365396 allow_shlib_undefined: bool,
366397 stack_size: u64,
398 post_prelink: bool = false,
367399
368400 /// Prevents other processes from clobbering files in the output directory
369401 /// of this linking operation.
......@@ -400,6 +432,7 @@ pub const File = struct {
400432 export_table: bool,
401433 initial_memory: ?u64,
402434 max_memory: ?u64,
435 object_host_name: ?[]const u8,
403436 export_symbol_names: []const []const u8,
404437 global_base: ?u64,
405438 build_id: std.zig.BuildId,
......@@ -632,43 +665,15 @@ pub const File = struct {
632665 pub const UpdateDebugInfoError = Dwarf.UpdateError;
633666 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`.
635670 pub const UpdateNavError = error{
636 OutOfMemory,
637671 Overflow,
638 Underflow,
639 FileTooBig,
640 InputOutput,
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,
672 OutOfMemory,
673 /// Indicates the error is already reported and stored in
674 /// `failed_codegen` on the Zcu.
662675 CodegenFail,
663 EmitFail,
664 NameTooLong,
665 CurrentWorkingDirectoryUnlinked,
666 LockViolation,
667 NetNameDeleted,
668 DeviceBusy,
669 InvalidArgument,
670 HotSwapUnavailableOnHostOperatingSystem,
671 } || UpdateDebugInfoError;
676 };
672677
673678 /// Called from within CodeGen to retrieve the symbol index of a global symbol.
674679 /// If no symbol exists yet with this name, a new undefined global symbol will
......@@ -701,7 +706,13 @@ pub const File = struct {
701706 }
702707 }
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 {
705716 switch (base.tag) {
706717 else => {},
707718 inline .elf => |tag| {
......@@ -727,9 +738,15 @@ pub const File = struct {
727738 }
728739 }
729740
741 pub const UpdateLineNumberError = error{
742 OutOfMemory,
743 Overflow,
744 LinkFailure,
745 };
746
730747 /// On an incremental update, fixup the line number of all `Nav`s at the given `TrackedInst`, because
731748 /// 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 {
733750 {
734751 const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?;
735752 const file = pt.zcu.fileByIndex(ti.file);
......@@ -771,83 +788,11 @@ pub const File = struct {
771788 }
772789 }
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.
776791 pub const FlushError = error{
777 CacheCheckFailed,
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`.
792 /// Indicates an error will be present in `Compilation.link_diags`.
789793 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,
821794 OutOfMemory,
822 Overflow,
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;
795 };
851796
852797 /// Commit pending changes and write headers. Takes into account final output mode
853798 /// and `use_lld`, not only `effectiveOutputMode`.
......@@ -864,10 +809,17 @@ pub const File = struct {
864809 assert(comp.c_object_table.count() == 1);
865810 const the_key = comp.c_object_table.keys()[0];
866811 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 };
868818 return;
869819 }
870820
821 assert(base.post_prelink);
822
871823 const use_lld = build_options.have_llvm and comp.config.use_lld;
872824 const output_mode = comp.config.output_mode;
873825 const link_mode = comp.config.link_mode;
......@@ -893,16 +845,6 @@ pub const File = struct {
893845 }
894846 }
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
906848 pub const UpdateExportsError = error{
907849 OutOfMemory,
908850 AnalysisFail,
......@@ -916,7 +858,7 @@ pub const File = struct {
916858 base: *File,
917859 pt: Zcu.PerThread,
918860 exported: Zcu.Exported,
919 export_indices: []const u32,
861 export_indices: []const Zcu.Export.Index,
920862 ) UpdateExportsError!void {
921863 switch (base.tag) {
922864 inline else => |tag| {
......@@ -932,6 +874,7 @@ pub const File = struct {
932874 addend: u32,
933875
934876 pub const Parent = union(enum) {
877 none,
935878 atom_index: u32,
936879 debug_output: DebugInfoOutput,
937880 };
......@@ -948,6 +891,7 @@ pub const File = struct {
948891 .c => unreachable,
949892 .spirv => unreachable,
950893 .nvptx => unreachable,
894 .wasm => unreachable,
951895 inline else => |tag| {
952896 dev.check(tag.devFeature());
953897 return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);
......@@ -966,6 +910,7 @@ pub const File = struct {
966910 .c => unreachable,
967911 .spirv => unreachable,
968912 .nvptx => unreachable,
913 .wasm => unreachable,
969914 inline else => |tag| {
970915 dev.check(tag.devFeature());
971916 return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc);
......@@ -978,6 +923,7 @@ pub const File = struct {
978923 .c => unreachable,
979924 .spirv => unreachable,
980925 .nvptx => unreachable,
926 .wasm => unreachable,
981927 inline else => |tag| {
982928 dev.check(tag.devFeature());
983929 return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);
......@@ -1099,12 +1045,44 @@ pub const File = struct {
10991045 }
11001046 }
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
11021069 pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void {
11031070 dev.check(.lld_linker);
11041071
11051072 const tracy = trace(@src());
11061073 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 {
11081086 const comp = base.comp;
11091087
11101088 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
......@@ -1364,6 +1342,16 @@ pub const File = struct {
13641342 }, llvm_object, prog_node);
13651343 }
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
13671355 pub const C = @import("link/C.zig");
13681356 pub const Coff = @import("link/Coff.zig");
13691357 pub const Plan9 = @import("link/Plan9.zig");
......@@ -1379,12 +1367,32 @@ pub const File = struct {
13791367/// from the rest of compilation. All tasks performed here are
13801368/// single-threaded with respect to one another.
13811369pub fn flushTaskQueue(tid: usize, comp: *Compilation) void {
1370 const diags = &comp.link_diags;
13821371 // As soon as check() is called, another `flushTaskQueue` call could occur,
13831372 // so the safety lock must go after the check.
13841373 while (comp.link_task_queue.check()) |tasks| {
13851374 comp.link_task_queue_safety.lock();
13861375 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
13871383 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 }
13881396 }
13891397}
13901398
......@@ -1428,6 +1436,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
14281436 const diags = &comp.link_diags;
14291437 switch (task) {
14301438 .load_explicitly_provided => if (comp.bin_file) |base| {
1439 comp.remaining_prelink_tasks -= 1;
14311440 const prog_node = comp.work_queue_progress_node.start("Parse Linker Inputs", comp.link_inputs.len);
14321441 defer prog_node.end();
14331442 for (comp.link_inputs) |input| {
......@@ -1445,6 +1454,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
14451454 }
14461455 },
14471456 .load_host_libc => if (comp.bin_file) |base| {
1457 comp.remaining_prelink_tasks -= 1;
14481458 const prog_node = comp.work_queue_progress_node.start("Linker Parse Host libc", 0);
14491459 defer prog_node.end();
14501460
......@@ -1504,6 +1514,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
15041514 }
15051515 },
15061516 .load_object => |path| if (comp.bin_file) |base| {
1517 comp.remaining_prelink_tasks -= 1;
15071518 const prog_node = comp.work_queue_progress_node.start("Linker Parse Object", 0);
15081519 defer prog_node.end();
15091520 base.openLoadObject(path) catch |err| switch (err) {
......@@ -1512,6 +1523,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
15121523 };
15131524 },
15141525 .load_archive => |path| if (comp.bin_file) |base| {
1526 comp.remaining_prelink_tasks -= 1;
15151527 const prog_node = comp.work_queue_progress_node.start("Linker Parse Archive", 0);
15161528 defer prog_node.end();
15171529 base.openLoadArchive(path, null) catch |err| switch (err) {
......@@ -1520,6 +1532,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
15201532 };
15211533 },
15221534 .load_dso => |path| if (comp.bin_file) |base| {
1535 comp.remaining_prelink_tasks -= 1;
15231536 const prog_node = comp.work_queue_progress_node.start("Linker Parse Shared Library", 0);
15241537 defer prog_node.end();
15251538 base.openLoadDso(path, .{
......@@ -1531,6 +1544,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
15311544 };
15321545 },
15331546 .load_input => |input| if (comp.bin_file) |base| {
1547 comp.remaining_prelink_tasks -= 1;
15341548 const prog_node = comp.work_queue_progress_node.start("Linker Parse Input", 0);
15351549 defer prog_node.end();
15361550 base.loadInput(input) catch |err| switch (err) {
......@@ -1545,26 +1559,38 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
15451559 };
15461560 },
15471561 .codegen_nav => |nav_index| {
1548 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
1549 defer pt.deactivate();
1550 pt.linkerUpdateNav(nav_index) catch |err| switch (err) {
1551 error.OutOfMemory => diags.setAllocFailure(),
1552 };
1562 if (comp.remaining_prelink_tasks == 0) {
1563 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
1564 defer pt.deactivate();
1565 pt.linkerUpdateNav(nav_index) catch |err| switch (err) {
1566 error.OutOfMemory => diags.setAllocFailure(),
1567 };
1568 } else {
1569 comp.link_task_queue_postponed.appendAssumeCapacity(task);
1570 }
15531571 },
15541572 .codegen_func => |func| {
1555 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
1556 defer pt.deactivate();
1557 // This call takes ownership of `func.air`.
1558 pt.linkerUpdateFunc(func.func, func.air) catch |err| switch (err) {
1559 error.OutOfMemory => diags.setAllocFailure(),
1560 };
1573 if (comp.remaining_prelink_tasks == 0) {
1574 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
1575 defer pt.deactivate();
1576 // This call takes ownership of `func.air`.
1577 pt.linkerUpdateFunc(func.func, func.air) catch |err| switch (err) {
1578 error.OutOfMemory => diags.setAllocFailure(),
1579 };
1580 } else {
1581 comp.link_task_queue_postponed.appendAssumeCapacity(task);
1582 }
15611583 },
15621584 .codegen_type => |ty| {
1563 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
1564 defer pt.deactivate();
1565 pt.linkerUpdateContainerType(ty) catch |err| switch (err) {
1566 error.OutOfMemory => diags.setAllocFailure(),
1567 };
1585 if (comp.remaining_prelink_tasks == 0) {
1586 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
1587 defer pt.deactivate();
1588 pt.linkerUpdateContainerType(ty) catch |err| switch (err) {
1589 error.OutOfMemory => diags.setAllocFailure(),
1590 };
1591 } else {
1592 comp.link_task_queue_postponed.appendAssumeCapacity(task);
1593 }
15681594 },
15691595 .update_line_number => |ti| {
15701596 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
......@@ -1593,7 +1619,7 @@ pub fn spawnLld(
15931619 const exit_code = try lldMain(arena, argv, false);
15941620 if (exit_code == 0) return;
15951621 if (comp.clang_passthrough_mode) std.process.exit(exit_code);
1596 return error.LLDReportedFailure;
1622 return error.LinkFailure;
15971623 }
15981624
15991625 var stderr: []u8 = &.{};
......@@ -1670,17 +1696,16 @@ pub fn spawnLld(
16701696 return error.UnableToSpawnSelf;
16711697 };
16721698
1699 const diags = &comp.link_diags;
16731700 switch (term) {
16741701 .Exited => |code| if (code != 0) {
16751702 if (comp.clang_passthrough_mode) std.process.exit(code);
1676 const diags = &comp.link_diags;
16771703 diags.lockAndParseLldStderr(argv[1], stderr);
1678 return error.LLDReportedFailure;
1704 return error.LinkFailure;
16791705 },
16801706 else => {
16811707 if (comp.clang_passthrough_mode) std.process.abort();
1682 log.err("{s} terminated with stderr:\n{s}", .{ argv[0], stderr });
1683 return error.LLDCrashed;
1708 return diags.fail("{s} terminated with stderr:\n{s}", .{ argv[0], stderr });
16841709 },
16851710 }
16861711
......@@ -2239,7 +2264,7 @@ fn resolvePathInputLib(
22392264 try wip_errors.init(gpa);
22402265 defer wip_errors.deinit();
22412266
2242 try diags.addMessagesToBundle(&wip_errors);
2267 try diags.addMessagesToBundle(&wip_errors, null);
22432268
22442269 var error_bundle = try wip_errors.toOwnedBundle("");
22452270 defer error_bundle.deinit(gpa);
src/link/C.zig+11-16
......@@ -175,21 +175,13 @@ pub fn deinit(self: *C) void {
175175 self.lazy_code_buf.deinit(gpa);
176176}
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
186178pub fn updateFunc(
187179 self: *C,
188180 pt: Zcu.PerThread,
189181 func_index: InternPool.Index,
190182 air: Air,
191183 liveness: Liveness,
192) !void {
184) link.File.UpdateNavError!void {
193185 const zcu = pt.zcu;
194186 const gpa = zcu.gpa;
195187 const func = zcu.funcInfo(func_index);
......@@ -313,7 +305,7 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void {
313305 };
314306}
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 {
317309 const tracy = trace(@src());
318310 defer tracy.end();
319311
......@@ -390,7 +382,7 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn
390382 _ = ti_id;
391383}
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 {
394386 return self.flushModule(arena, tid, prog_node);
395387}
396388
......@@ -409,7 +401,7 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) {
409401 return defines;
410402}
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 {
413405 _ = arena; // Has the same lifetime as the call to Compilation.update.
414406
415407 const tracy = trace(@src());
......@@ -419,6 +411,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
419411 defer sub_prog_node.end();
420412
421413 const comp = self.base.comp;
414 const diags = &comp.link_diags;
422415 const gpa = comp.gpa;
423416 const zcu = self.base.comp.zcu.?;
424417 const ip = &zcu.intern_pool;
......@@ -476,7 +469,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
476469 defer export_names.deinit(gpa);
477470 try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count()));
478471 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, {});
480473 }
481474 for (zcu.multi_exports.values()) |info| {
482475 try export_names.ensureUnusedCapacity(gpa, info.len);
......@@ -554,8 +547,10 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
554547 }, self.getString(av_block.code));
555548
556549 const file = self.base.file.?;
557 try file.setEndPos(f.file_size);
558 try file.pwritevAll(f.all_buffers.items, 0);
550 file.setEndPos(f.file_size) catch |err| return diags.fail("failed to allocate file: {s}", .{@errorName(err)});
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 });
559554}
560555
561556const Flush = struct {
......@@ -845,7 +840,7 @@ pub fn updateExports(
845840 self: *C,
846841 pt: Zcu.PerThread,
847842 exported: Zcu.Exported,
848 export_indices: []const u32,
843 export_indices: []const Zcu.Export.Index,
849844) !void {
850845 const zcu = pt.zcu;
851846 const gpa = zcu.gpa;
src/link/Coff.zig+126-85
......@@ -408,7 +408,7 @@ pub fn createEmpty(
408408 max_file_offset = header.pointer_to_raw_data + header.size_of_raw_data;
409409 }
410410 }
411 try coff.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset);
411 try coff.pwriteAll(&[_]u8{0}, max_file_offset);
412412 }
413413
414414 return coff;
......@@ -858,7 +858,7 @@ fn writeAtom(coff: *Coff, atom_index: Atom.Index, code: []u8) !void {
858858 }
859859
860860 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
863863 // Now we can mark the relocs as resolved.
864864 while (relocs.popOrNull()) |reloc| {
......@@ -891,7 +891,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void {
891891 const sect_id = coff.got_section_index.?;
892892
893893 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());
895895 try coff.growSection(sect_id, needed_size);
896896 coff.got_table_count_dirty = false;
897897 }
......@@ -908,7 +908,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void {
908908 switch (coff.ptr_width) {
909909 .p32 => {
910910 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);
912912 try coff.base.file.?.pwriteAll(&buf, file_offset);
913913 },
914914 .p64 => {
......@@ -1093,7 +1093,13 @@ fn freeAtom(coff: *Coff, atom_index: Atom.Index) void {
10931093 coff.getAtomPtr(atom_index).sym_index = 0;
10941094}
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 {
10971103 if (build_options.skip_non_native and builtin.object_format != .coff) {
10981104 @panic("Attempted to compile for object format that was disabled by build configuration");
10991105 }
......@@ -1106,34 +1112,41 @@ pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index,
11061112 const zcu = pt.zcu;
11071113 const gpa = zcu.gpa;
11081114 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);
11111118 coff.freeRelocations(atom_index);
11121119
11131120 coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?;
11141121
1115 var code_buffer = std.ArrayList(u8).init(gpa);
1116 defer code_buffer.deinit();
1122 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1123 defer code_buffer.deinit(gpa);
11171124
1118 const res = try codegen.generateFunction(
1125 codegen.generateFunction(
11191126 &coff.base,
11201127 pt,
1121 zcu.navSrcLoc(func.owner_nav),
1128 zcu.navSrcLoc(nav_index),
11221129 func_index,
11231130 air,
11241131 liveness,
11251132 &code_buffer,
11261133 .none,
1127 );
1128 const code = switch (res) {
1129 .ok => code_buffer.items,
1130 .fail => |em| {
1131 try zcu.failed_codegen.put(zcu.gpa, func.owner_nav, em);
1132 return;
1134 ) catch |err| switch (err) {
1135 error.CodegenFail => return error.CodegenFail,
1136 error.OutOfMemory => return error.OutOfMemory,
1137 error.Overflow => |e| {
1138 try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
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;
11331146 },
11341147 };
11351148
1136 try coff.updateNavCode(pt, func.owner_nav, code, .FUNCTION);
1149 try coff.updateNavCode(pt, nav_index, code_buffer.items, .FUNCTION);
11371150
11381151 // Exports will be updated by `Zcu.processExports` after the update.
11391152}
......@@ -1154,24 +1167,21 @@ fn lowerConst(
11541167) !LowerConstResult {
11551168 const gpa = coff.base.comp.gpa;
11561169
1157 var code_buffer = std.ArrayList(u8).init(gpa);
1158 defer code_buffer.deinit();
1170 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1171 defer code_buffer.deinit(gpa);
11591172
11601173 const atom_index = try coff.createAtom();
11611174 const sym = coff.getAtom(atom_index).getSymbolPtr(coff);
11621175 try coff.setSymbolName(sym, name);
11631176 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, .{
11661179 .atom_index = coff.getAtom(atom_index).getSymbolIndex().?,
11671180 });
1168 const code = switch (res) {
1169 .ok => code_buffer.items,
1170 .fail => |em| return .{ .fail = em },
1171 };
1181 const code = code_buffer.items;
11721182
11731183 const atom = coff.getAtomPtr(atom_index);
1174 atom.size = @as(u32, @intCast(code.len));
1184 atom.size = @intCast(code.len);
11751185 atom.getSymbolPtr(coff).value = try coff.allocateAtom(
11761186 atom_index,
11771187 atom.size,
......@@ -1227,10 +1237,10 @@ pub fn updateNav(
12271237
12281238 coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index);
12291239
1230 var code_buffer = std.ArrayList(u8).init(gpa);
1231 defer code_buffer.deinit();
1240 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1241 defer code_buffer.deinit(gpa);
12321242
1233 const res = try codegen.generateSymbol(
1243 try codegen.generateSymbol(
12341244 &coff.base,
12351245 pt,
12361246 zcu.navSrcLoc(nav_index),
......@@ -1238,15 +1248,8 @@ pub fn updateNav(
12381248 &code_buffer,
12391249 .{ .atom_index = atom.getSymbolIndex().? },
12401250 );
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);
12501253 }
12511254
12521255 // Exports will be updated by `Zcu.processExports` after the update.
......@@ -1260,11 +1263,12 @@ fn updateLazySymbolAtom(
12601263 section_index: u16,
12611264) !void {
12621265 const zcu = pt.zcu;
1263 const gpa = zcu.gpa;
1266 const comp = coff.base.comp;
1267 const gpa = comp.gpa;
12641268
12651269 var required_alignment: InternPool.Alignment = .none;
1266 var code_buffer = std.ArrayList(u8).init(gpa);
1267 defer code_buffer.deinit();
1270 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1271 defer code_buffer.deinit(gpa);
12681272
12691273 const name = try allocPrint(gpa, "__lazy_{s}_{}", .{
12701274 @tagName(sym.kind),
......@@ -1276,7 +1280,7 @@ fn updateLazySymbolAtom(
12761280 const local_sym_index = atom.getSymbolIndex().?;
12771281
12781282 const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;
1279 const res = try codegen.generateLazySymbol(
1283 try codegen.generateLazySymbol(
12801284 &coff.base,
12811285 pt,
12821286 src,
......@@ -1286,13 +1290,7 @@ fn updateLazySymbolAtom(
12861290 .none,
12871291 .{ .atom_index = local_sym_index },
12881292 );
1289 const code = switch (res) {
1290 .ok => code_buffer.items,
1291 .fail => |em| {
1292 log.err("{s}", .{em.msg});
1293 return error.CodegenFail;
1294 },
1295 };
1293 const code = code_buffer.items;
12961294
12971295 const code_len: u32 = @intCast(code.len);
12981296 const symbol = atom.getSymbolPtr(coff);
......@@ -1387,7 +1385,7 @@ fn updateNavCode(
13871385 nav_index: InternPool.Nav.Index,
13881386 code: []u8,
13891387 complex_type: coff_util.ComplexType,
1390) !void {
1388) link.File.UpdateNavError!void {
13911389 const zcu = pt.zcu;
13921390 const ip = &zcu.intern_pool;
13931391 const nav = ip.getNav(nav_index);
......@@ -1405,18 +1403,21 @@ fn updateNavCode(
14051403 const atom = coff.getAtom(atom_index);
14061404 const sym_index = atom.getSymbolIndex().?;
14071405 const sect_index = nav_metadata.section;
1408 const code_len = @as(u32, @intCast(code.len));
1406 const code_len: u32 = @intCast(code.len);
14091407
14101408 if (atom.size != 0) {
14111409 const sym = atom.getSymbolPtr(coff);
14121410 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);
14141412 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };
14151413
14161414 const capacity = atom.capacity(coff);
14171415 const need_realloc = code.len > capacity or !required_alignment.check(sym.value);
14181416 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 };
14201421 log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), sym.value, vaddr });
14211422 log.debug(" (required alignment 0x{x}", .{required_alignment});
14221423
......@@ -1424,7 +1425,10 @@ fn updateNavCode(
14241425 sym.value = vaddr;
14251426 log.debug(" (updating GOT entry)", .{});
14261427 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 };
14281432 coff.markRelocsDirtyByTarget(.{ .sym_index = sym_index });
14291433 }
14301434 } else if (code_len < atom.size) {
......@@ -1434,26 +1438,34 @@ fn updateNavCode(
14341438 } else {
14351439 const sym = atom.getSymbolPtr(coff);
14361440 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);
14381442 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 };
14411448 errdefer coff.freeAtom(atom_index);
14421449 log.debug("allocated atom for {} at 0x{x}", .{ nav.fqn.fmt(ip), vaddr });
14431450 coff.getAtomPtr(atom_index).size = code_len;
14441451 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 };
14471457 }
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 };
14501463}
14511464
14521465pub fn freeNav(coff: *Coff, nav_index: InternPool.NavIndex) void {
14531466 if (coff.llvm_object) |llvm_object| return llvm_object.freeNav(nav_index);
14541467
14551468 const gpa = coff.base.comp.gpa;
1456 log.debug("freeDecl 0x{x}", .{nav_index});
14571469
14581470 if (coff.decls.fetchOrderedRemove(nav_index)) |const_kv| {
14591471 var kv = const_kv;
......@@ -1466,7 +1478,7 @@ pub fn updateExports(
14661478 coff: *Coff,
14671479 pt: Zcu.PerThread,
14681480 exported: Zcu.Exported,
1469 export_indices: []const u32,
1481 export_indices: []const Zcu.Export.Index,
14701482) link.File.UpdateExportsError!void {
14711483 if (build_options.skip_non_native and builtin.object_format != .coff) {
14721484 @panic("Attempted to compile for object format that was disabled by build configuration");
......@@ -1481,7 +1493,7 @@ pub fn updateExports(
14811493 // Even in the case of LLVM, we need to notice certain exported symbols in order to
14821494 // detect the default subsystem.
14831495 for (export_indices) |export_idx| {
1484 const exp = zcu.all_exports.items[export_idx];
1496 const exp = export_idx.ptr(zcu);
14851497 const exported_nav_index = switch (exp.exported) {
14861498 .nav => |nav| nav,
14871499 .uav => continue,
......@@ -1524,7 +1536,7 @@ pub fn updateExports(
15241536 break :blk coff.navs.getPtr(nav).?;
15251537 },
15261538 .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);
15281540 const res = try coff.lowerUav(pt, uav, .none, first_exp.src);
15291541 switch (res) {
15301542 .mcv => {},
......@@ -1543,7 +1555,7 @@ pub fn updateExports(
15431555 const atom = coff.getAtom(atom_index);
15441556
15451557 for (export_indices) |export_idx| {
1546 const exp = zcu.all_exports.items[export_idx];
1558 const exp = export_idx.ptr(zcu);
15471559 log.debug("adding new export '{}'", .{exp.opts.name.fmt(&zcu.intern_pool)});
15481560
15491561 if (exp.opts.section.toSlice(&zcu.intern_pool)) |section_name| {
......@@ -1671,12 +1683,17 @@ fn resolveGlobalSymbol(coff: *Coff, current: SymbolWithLoc) !void {
16711683pub fn flush(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
16721684 const comp = coff.base.comp;
16731685 const use_lld = build_options.have_llvm and comp.config.use_lld;
1686 const diags = &comp.link_diags;
16741687 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 };
16761693 }
16771694 switch (comp.config.output_mode) {
16781695 .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", .{}),
16801697 }
16811698}
16821699
......@@ -2207,12 +2224,16 @@ fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Director
22072224 return null;
22082225}
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 {
22112233 const tracy = trace(@src());
22122234 defer tracy.end();
22132235
22142236 const comp = coff.base.comp;
2215 const gpa = comp.gpa;
22162237 const diags = &comp.link_diags;
22172238
22182239 if (coff.llvm_object) |llvm_object| {
......@@ -2223,8 +2244,22 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
22232244 const sub_prog_node = prog_node.start("COFF Flush", 0);
22242245 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
22262261 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", .{}),
22282263 tid,
22292264 );
22302265 defer pt.deactivate();
......@@ -2232,24 +2267,18 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
22322267 if (coff.lazy_syms.getPtr(.anyerror_type)) |metadata| {
22332268 // Most lazy symbols can be updated on first use, but
22342269 // 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(
22362271 pt,
22372272 .{ .kind = .code, .ty = .anyerror_type },
22382273 metadata.text_atom,
22392274 coff.text_section_index.?,
2240 ) catch |err| return switch (err) {
2241 error.CodegenFail => error.FlushFailure,
2242 else => |e| e,
2243 };
2244 if (metadata.rdata_state != .unused) coff.updateLazySymbolAtom(
2275 );
2276 if (metadata.rdata_state != .unused) try coff.updateLazySymbolAtom(
22452277 pt,
22462278 .{ .kind = .const_data, .ty = .anyerror_type },
22472279 metadata.rdata_atom,
22482280 coff.rdata_section_index.?,
2249 ) catch |err| return switch (err) {
2250 error.CodegenFail => error.FlushFailure,
2251 else => |e| e,
2252 };
2281 );
22532282 }
22542283 for (coff.lazy_syms.values()) |*metadata| {
22552284 if (metadata.text_state != .unused) metadata.text_state = .flushed;
......@@ -2594,7 +2623,7 @@ fn writeBaseRelocations(coff: *Coff) !void {
25942623 const needed_size = @as(u32, @intCast(buffer.items.len));
25952624 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
25992628 coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.BASERELOC)] = .{
26002629 .virtual_address = header.virtual_address,
......@@ -2727,7 +2756,7 @@ fn writeImportTables(coff: *Coff) !void {
27272756
27282757 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
27322761 coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.IMPORT)] = .{
27332762 .virtual_address = header.virtual_address + iat_size,
......@@ -2744,17 +2773,19 @@ fn writeImportTables(coff: *Coff) !void {
27442773fn writeStrtab(coff: *Coff) !void {
27452774 if (coff.strtab_offset == null) return;
27462775
2776 const comp = coff.base.comp;
2777 const gpa = comp.gpa;
2778 const diags = &comp.link_diags;
27472779 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
27502782 if (needed_size > allocated_size) {
27512783 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)));
27532785 }
27542786
27552787 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;
27582789 var buffer = std.ArrayList(u8).init(gpa);
27592790 defer buffer.deinit();
27602791 try buffer.ensureTotalCapacityPrecise(needed_size);
......@@ -2763,17 +2794,19 @@ fn writeStrtab(coff: *Coff) !void {
27632794 // we write the length of the strtab to a temporary buffer that goes to file.
27642795 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 };
27672800}
27682801
27692802fn writeSectionHeaders(coff: *Coff) !void {
27702803 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);
27722805}
27732806
27742807fn writeDataDirectoriesHeaders(coff: *Coff) !void {
27752808 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);
27772810}
27782811
27792812fn writeHeader(coff: *Coff) !void {
......@@ -2913,7 +2946,7 @@ fn writeHeader(coff: *Coff) !void {
29132946 },
29142947 }
29152948
2916 try coff.base.file.?.pwriteAll(buffer.items, 0);
2949 try coff.pwriteAll(buffer.items, 0);
29172950}
29182951
29192952pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
......@@ -3710,6 +3743,14 @@ const ImportTable = struct {
37103743 const ImportIndex = u32;
37113744};
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
37133754const Coff = @This();
37143755
37153756const std = @import("std");
src/link/Dwarf.zig+35-15
......@@ -21,7 +21,6 @@ debug_rnglists: DebugRngLists,
2121debug_str: StringSection,
2222
2323pub const UpdateError = error{
24 CodegenFail,
2524 ReinterpretDeclRef,
2625 Unimplemented,
2726 OutOfMemory,
......@@ -451,7 +450,6 @@ pub const Section = struct {
451450 const zo = elf_file.zigObjectPtr().?;
452451 const atom = zo.symbol(sec.index).atom(elf_file).?;
453452 if (atom.prevAtom(elf_file)) |_| {
454 // FIXME:JK trimming/shrinking has to be reworked on ZigObject/Elf level
455453 atom.value += len;
456454 } else {
457455 const shdr = &elf_file.sections.items(.shdr)[atom.output_section_index];
......@@ -600,12 +598,13 @@ const Unit = struct {
600598
601599 fn move(unit: *Unit, sec: *Section, dwarf: *Dwarf, new_off: u32) UpdateError!void {
602600 if (unit.off == new_off) return;
603 if (try dwarf.getFile().?.copyRangeAll(
601 const n = try dwarf.getFile().?.copyRangeAll(
604602 sec.off(dwarf) + unit.off,
605603 dwarf.getFile().?,
606604 sec.off(dwarf) + new_off,
607605 unit.len,
608 ) != unit.len) return error.InputOutput;
606 );
607 if (n != unit.len) return error.InputOutput;
609608 unit.off = new_off;
610609 }
611610
......@@ -1891,19 +1890,16 @@ pub const WipNav = struct {
18911890 const bytes = if (ty.hasRuntimeBits(wip_nav.pt.zcu)) ty.abiSize(wip_nav.pt.zcu) else 0;
18921891 try uleb128(diw, bytes);
18931892 if (bytes == 0) return;
1894 var dim = wip_nav.debug_info.toManaged(wip_nav.dwarf.gpa);
1895 defer wip_nav.debug_info = dim.moveToUnmanaged();
1896 switch (try codegen.generateSymbol(
1893 const old_len = wip_nav.debug_info.items.len;
1894 try codegen.generateSymbol(
18971895 wip_nav.dwarf.bin_file,
18981896 wip_nav.pt,
18991897 src_loc,
19001898 val,
1901 &dim,
1899 &wip_nav.debug_info,
19021900 .{ .debug_output = .{ .dwarf = wip_nav } },
1903 )) {
1904 .ok => assert(dim.items.len == wip_nav.debug_info.items.len + bytes),
1905 .fail => unreachable,
1906 }
1901 );
1902 assert(old_len + bytes == wip_nav.debug_info.items.len);
19071903 }
19081904
19091905 const AbbrevCodeForForm = struct {
......@@ -2278,7 +2274,7 @@ pub fn deinit(dwarf: *Dwarf) void {
22782274 dwarf.* = undefined;
22792275}
22802276
2281fn getUnit(dwarf: *Dwarf, mod: *Module) UpdateError!Unit.Index {
2277fn getUnit(dwarf: *Dwarf, mod: *Module) !Unit.Index {
22822278 const mod_gop = try dwarf.mods.getOrPut(dwarf.gpa, mod);
22832279 const unit: Unit.Index = @enumFromInt(mod_gop.index);
22842280 if (!mod_gop.found_existing) {
......@@ -2338,7 +2334,24 @@ fn getModInfo(dwarf: *Dwarf, unit: Unit.Index) *ModInfo {
23382334 return &dwarf.mods.values()[@intFromEnum(unit)];
23392335}
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 {
23422355 const zcu = pt.zcu;
23432356 const ip = &zcu.intern_pool;
23442357
......@@ -2667,7 +2680,14 @@ pub fn finishWipNav(
26672680 try wip_nav.updateLazy(zcu.navSrcLoc(nav_index));
26682681}
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 {
26712691 const zcu = pt.zcu;
26722692 const ip = &zcu.intern_pool;
26732693 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 {
795795}
796796
797797pub 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;
799801 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 };
801807 }
802808 try self.flushModule(arena, tid, prog_node);
803809}
......@@ -807,7 +813,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
807813 defer tracy.end();
808814
809815 const comp = self.base.comp;
810 const gpa = comp.gpa;
811816 const diags = &comp.link_diags;
812817
813818 if (self.llvm_object) |llvm_object| {
......@@ -821,6 +826,18 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
821826 const sub_prog_node = prog_node.start("ELF Flush", 0);
822827 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
824841 const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{
825842 .root_dir = self.base.emit.root_dir,
826843 .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
842859 .Exe => {},
843860 }
844861
845 if (diags.hasErrors()) return error.FlushFailure;
862 if (diags.hasErrors()) return error.LinkFailure;
846863
847864 // If we haven't already, create a linker-generated input file comprising of
848865 // linker-defined synthetic symbols only such as `_DYNAMIC`, etc.
849866 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));
851868 self.files.set(index, .{ .linker_defined = .{ .index = index } });
852869 self.linker_defined_index = index;
853870 const object = self.linkerDefinedPtr().?;
......@@ -878,7 +895,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
878895 }
879896
880897 self.checkDuplicates() catch |err| switch (err) {
881 error.HasDuplicates => return error.FlushFailure,
898 error.HasDuplicates => return error.LinkFailure,
882899 else => |e| return e,
883900 };
884901
......@@ -956,14 +973,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
956973 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
957974 error.UnsupportedCpuArch => {
958975 try self.reportUnsupportedCpuArch();
959 return error.FlushFailure;
976 return error.LinkFailure;
960977 },
961978 else => |e| return e,
962979 };
963 try self.base.file.?.pwriteAll(code, file_offset);
980 try self.pwriteAll(code, file_offset);
964981 }
965982
966 if (has_reloc_errors) return error.FlushFailure;
983 if (has_reloc_errors) return error.LinkFailure;
967984 }
968985
969986 try self.writePhdrTable();
......@@ -972,10 +989,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
972989 try self.writeMergeSections();
973990
974991 self.writeSyntheticSections() catch |err| switch (err) {
975 error.RelocFailure => return error.FlushFailure,
992 error.RelocFailure => return error.LinkFailure,
976993 error.UnsupportedCpuArch => {
977994 try self.reportUnsupportedCpuArch();
978 return error.FlushFailure;
995 return error.LinkFailure;
979996 },
980997 else => |e| return e,
981998 };
......@@ -989,7 +1006,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
9891006 try self.writeElfHeader();
9901007 }
9911008
992 if (diags.hasErrors()) return error.FlushFailure;
1009 if (diags.hasErrors()) return error.LinkFailure;
9931010}
9941011
9951012fn dumpArgvInit(self: *Elf, arena: Allocator) !void {
......@@ -1389,7 +1406,7 @@ fn scanRelocs(self: *Elf) !void {
13891406 error.RelaxFailure => unreachable,
13901407 error.UnsupportedCpuArch => {
13911408 try self.reportUnsupportedCpuArch();
1392 return error.FlushFailure;
1409 return error.LinkFailure;
13931410 },
13941411 error.RelocFailure => has_reloc_errors = true,
13951412 else => |e| return e,
......@@ -1400,7 +1417,7 @@ fn scanRelocs(self: *Elf) !void {
14001417 error.RelaxFailure => unreachable,
14011418 error.UnsupportedCpuArch => {
14021419 try self.reportUnsupportedCpuArch();
1403 return error.FlushFailure;
1420 return error.LinkFailure;
14041421 },
14051422 error.RelocFailure => has_reloc_errors = true,
14061423 else => |e| return e,
......@@ -1409,7 +1426,7 @@ fn scanRelocs(self: *Elf) !void {
14091426
14101427 try self.reportUndefinedSymbols(&undefs);
14111428
1412 if (has_reloc_errors) return error.FlushFailure;
1429 if (has_reloc_errors) return error.LinkFailure;
14131430
14141431 if (self.zigObjectPtr()) |zo| {
14151432 try zo.asFile().createSymbolIndirection(self);
......@@ -2117,7 +2134,7 @@ pub fn writeShdrTable(self: *Elf) !void {
21172134 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
21182135 }
21192136 }
2120 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
2137 try self.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
21212138 },
21222139 .p64 => {
21232140 const buf = try gpa.alloc(elf.Elf64_Shdr, self.sections.items(.shdr).len);
......@@ -2130,7 +2147,7 @@ pub fn writeShdrTable(self: *Elf) !void {
21302147 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
21312148 }
21322149 }
2133 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
2150 try self.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
21342151 },
21352152 }
21362153}
......@@ -2157,7 +2174,7 @@ fn writePhdrTable(self: *Elf) !void {
21572174 mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);
21582175 }
21592176 }
2160 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
2177 try self.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
21612178 },
21622179 .p64 => {
21632180 const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len);
......@@ -2169,7 +2186,7 @@ fn writePhdrTable(self: *Elf) !void {
21692186 mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);
21702187 }
21712188 }
2172 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
2189 try self.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
21732190 },
21742191 }
21752192}
......@@ -2319,7 +2336,7 @@ pub fn writeElfHeader(self: *Elf) !void {
23192336
23202337 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);
23232340}
23242341
23252342pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void {
......@@ -2327,7 +2344,13 @@ pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void {
23272344 return self.zigObjectPtr().?.freeNav(self, nav);
23282345}
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 {
23312354 if (build_options.skip_non_native and builtin.object_format != .elf) {
23322355 @panic("Attempted to compile for object format that was disabled by build configuration");
23332356 }
......@@ -2351,19 +2374,32 @@ pub fn updateContainerType(
23512374 self: *Elf,
23522375 pt: Zcu.PerThread,
23532376 ty: InternPool.Index,
2354) link.File.UpdateNavError!void {
2377) link.File.UpdateContainerTypeError!void {
23552378 if (build_options.skip_non_native and builtin.object_format != .elf) {
23562379 @panic("Attempted to compile for object format that was disabled by build configuration");
23572380 }
23582381 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 };
23602396}
23612397
23622398pub fn updateExports(
23632399 self: *Elf,
23642400 pt: Zcu.PerThread,
23652401 exported: Zcu.Exported,
2366 export_indices: []const u32,
2402 export_indices: []const Zcu.Export.Index,
23672403) link.File.UpdateExportsError!void {
23682404 if (build_options.skip_non_native and builtin.object_format != .elf) {
23692405 @panic("Attempted to compile for object format that was disabled by build configuration");
......@@ -2441,7 +2477,7 @@ pub fn resolveMergeSections(self: *Elf) !void {
24412477 };
24422478 }
24432479
2444 if (has_errors) return error.FlushFailure;
2480 if (has_errors) return error.LinkFailure;
24452481
24462482 for (self.objects.items) |index| {
24472483 const object = self.file(index).?.object;
......@@ -2491,8 +2527,8 @@ pub fn writeMergeSections(self: *Elf) !void {
24912527
24922528 for (self.merge_sections.items) |*msec| {
24932529 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;
2495 const size = math.cast(usize, msec.size) orelse return error.Overflow;
2530 const fileoff = try self.cast(usize, msec.value + shdr.sh_offset);
2531 const size = try self.cast(usize, msec.size);
24962532 try buffer.ensureTotalCapacity(size);
24972533 buffer.appendNTimesAssumeCapacity(0, size);
24982534
......@@ -2500,11 +2536,11 @@ pub fn writeMergeSections(self: *Elf) !void {
25002536 const msub = msec.mergeSubsection(msub_index);
25012537 assert(msub.alive);
25022538 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);
25042540 @memcpy(buffer.items[off..][0..string.len], string);
25052541 }
25062542
2507 try self.base.file.?.pwriteAll(buffer.items, fileoff);
2543 try self.pwriteAll(buffer.items, fileoff);
25082544 buffer.clearRetainingCapacity();
25092545 }
25102546}
......@@ -3121,9 +3157,6 @@ pub fn sortShdrs(
31213157 fileLookup(files, ref.file, zig_object_ptr).?.atom(ref.index).?.output_section_index = atom_list.output_section_index;
31223158 }
31233159 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];
31273160 shdr.sh_link = section_indexes.symtab.?;
31283161 shdr.sh_info = backlinks[shdr.sh_info];
31293162 }
......@@ -3211,7 +3244,7 @@ fn updateSectionSizes(self: *Elf) !void {
32113244 atom_list.dirty = false;
32123245 }
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.
32153248 for (self.thunks.items) |*th| {
32163249 th.value += slice.items(.atom_list_2)[th.output_section_index].value;
32173250 }
......@@ -3297,7 +3330,6 @@ fn updateSectionSizes(self: *Elf) !void {
32973330 self.updateShStrtabSize();
32983331}
32993332
3300// FIXME:JK this is very much obsolete, remove!
33013333pub fn updateShStrtabSize(self: *Elf) void {
33023334 if (self.section_indexes.shstrtab) |index| {
33033335 self.sections.items(.shdr)[index].sh_size = self.shstrtab.items.len;
......@@ -3362,7 +3394,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
33623394 // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op
33633395 var err = try diags.addErrorWithNotes(1);
33643396 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 });
33663398 }
33673399
33683400 phdr_table_load.p_filesz = needed_size + ehsize;
......@@ -3658,7 +3690,7 @@ fn writeAtoms(self: *Elf) !void {
36583690 atom_list.write(&buffer, &undefs, self) catch |err| switch (err) {
36593691 error.UnsupportedCpuArch => {
36603692 try self.reportUnsupportedCpuArch();
3661 return error.FlushFailure;
3693 return error.LinkFailure;
36623694 },
36633695 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
36643696 else => |e| return e,
......@@ -3666,7 +3698,7 @@ fn writeAtoms(self: *Elf) !void {
36663698 }
36673699
36683700 try self.reportUndefinedSymbols(&undefs);
3669 if (has_reloc_errors) return error.FlushFailure;
3701 if (has_reloc_errors) return error.LinkFailure;
36703702
36713703 if (self.requiresThunks()) {
36723704 for (self.thunks.items) |th| {
......@@ -3676,7 +3708,7 @@ fn writeAtoms(self: *Elf) !void {
36763708 const offset = @as(u64, @intCast(th.value)) + shdr.sh_offset;
36773709 try th.write(self, buffer.writer());
36783710 assert(buffer.items.len == thunk_size);
3679 try self.base.file.?.pwriteAll(buffer.items, offset);
3711 try self.pwriteAll(buffer.items, offset);
36803712 buffer.clearRetainingCapacity();
36813713 }
36823714 }
......@@ -3784,12 +3816,12 @@ fn writeSyntheticSections(self: *Elf) !void {
37843816 const contents = buffer[0 .. interp.len + 1];
37853817 const shdr = slice.items(.shdr)[shndx];
37863818 assert(shdr.sh_size == contents.len);
3787 try self.base.file.?.pwriteAll(contents, shdr.sh_offset);
3819 try self.pwriteAll(contents, shdr.sh_offset);
37883820 }
37893821
37903822 if (self.section_indexes.hash) |shndx| {
37913823 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);
37933825 }
37943826
37953827 if (self.section_indexes.gnu_hash) |shndx| {
......@@ -3797,12 +3829,12 @@ fn writeSyntheticSections(self: *Elf) !void {
37973829 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.gnu_hash.size());
37983830 defer buffer.deinit();
37993831 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);
38013833 }
38023834
38033835 if (self.section_indexes.versym) |shndx| {
38043836 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);
38063838 }
38073839
38083840 if (self.section_indexes.verneed) |shndx| {
......@@ -3810,7 +3842,7 @@ fn writeSyntheticSections(self: *Elf) !void {
38103842 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.verneed.size());
38113843 defer buffer.deinit();
38123844 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);
38143846 }
38153847
38163848 if (self.section_indexes.dynamic) |shndx| {
......@@ -3818,7 +3850,7 @@ fn writeSyntheticSections(self: *Elf) !void {
38183850 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynamic.size(self));
38193851 defer buffer.deinit();
38203852 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);
38223854 }
38233855
38243856 if (self.section_indexes.dynsymtab) |shndx| {
......@@ -3826,12 +3858,12 @@ fn writeSyntheticSections(self: *Elf) !void {
38263858 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynsym.size());
38273859 defer buffer.deinit();
38283860 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);
38303862 }
38313863
38323864 if (self.section_indexes.dynstrtab) |shndx| {
38333865 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);
38353867 }
38363868
38373869 if (self.section_indexes.eh_frame) |shndx| {
......@@ -3841,21 +3873,21 @@ fn writeSyntheticSections(self: *Elf) !void {
38413873 break :existing_size sym.atom(self).?.size;
38423874 };
38433875 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);
38453877 var buffer = try std.ArrayList(u8).initCapacity(gpa, @intCast(sh_size - existing_size));
38463878 defer buffer.deinit();
38473879 try eh_frame.writeEhFrame(self, buffer.writer());
38483880 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);
38503882 }
38513883
38523884 if (self.section_indexes.eh_frame_hdr) |shndx| {
38533885 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);
38553887 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
38563888 defer buffer.deinit();
38573889 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);
38593891 }
38603892
38613893 if (self.section_indexes.got) |index| {
......@@ -3863,7 +3895,7 @@ fn writeSyntheticSections(self: *Elf) !void {
38633895 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self));
38643896 defer buffer.deinit();
38653897 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);
38673899 }
38683900
38693901 if (self.section_indexes.rela_dyn) |shndx| {
......@@ -3871,7 +3903,7 @@ fn writeSyntheticSections(self: *Elf) !void {
38713903 try self.got.addRela(self);
38723904 try self.copy_rel.addRela(self);
38733905 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);
38753907 }
38763908
38773909 if (self.section_indexes.plt) |shndx| {
......@@ -3879,7 +3911,7 @@ fn writeSyntheticSections(self: *Elf) !void {
38793911 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size(self));
38803912 defer buffer.deinit();
38813913 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);
38833915 }
38843916
38853917 if (self.section_indexes.got_plt) |shndx| {
......@@ -3887,7 +3919,7 @@ fn writeSyntheticSections(self: *Elf) !void {
38873919 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got_plt.size(self));
38883920 defer buffer.deinit();
38893921 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);
38913923 }
38923924
38933925 if (self.section_indexes.plt_got) |shndx| {
......@@ -3895,25 +3927,24 @@ fn writeSyntheticSections(self: *Elf) !void {
38953927 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt_got.size(self));
38963928 defer buffer.deinit();
38973929 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);
38993931 }
39003932
39013933 if (self.section_indexes.rela_plt) |shndx| {
39023934 const shdr = slice.items(.shdr)[shndx];
39033935 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);
39053937 }
39063938
39073939 try self.writeSymtab();
39083940 try self.writeShStrtab();
39093941}
39103942
3911// FIXME:JK again, why is this needed?
39123943pub fn writeShStrtab(self: *Elf) !void {
39133944 if (self.section_indexes.shstrtab) |index| {
39143945 const shdr = self.sections.items(.shdr)[index];
39153946 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);
39173948 }
39183949}
39193950
......@@ -3928,7 +3959,7 @@ pub fn writeSymtab(self: *Elf) !void {
39283959 .p32 => @sizeOf(elf.Elf32_Sym),
39293960 .p64 => @sizeOf(elf.Elf64_Sym),
39303961 };
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
39333964 log.debug("writing {d} symbols in .symtab from 0x{x} to 0x{x}", .{
39343965 nsyms,
......@@ -3941,7 +3972,7 @@ pub fn writeSymtab(self: *Elf) !void {
39413972 });
39423973
39433974 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);
39453976 // TODO we could resize instead and in ZigObject/Object always access as slice
39463977 self.strtab.clearRetainingCapacity();
39473978 self.strtab.appendAssumeCapacity(0);
......@@ -4010,17 +4041,17 @@ pub fn writeSymtab(self: *Elf) !void {
40104041 };
40114042 if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out);
40124043 }
4013 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset);
4044 try self.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset);
40144045 },
40154046 .p64 => {
40164047 if (foreign_endian) {
40174048 for (self.symtab.items) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym);
40184049 }
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);
40204051 },
40214052 }
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);
40244055}
40254056
40264057/// 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 {
45144545 for (refs.items[0..nrefs]) |ref| {
45154546 const atom_ptr = self.atom(ref).?;
45164547 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) });
45184549 }
45194550
45204551 if (refs.items.len > max_notes) {
45214552 const remaining = refs.items.len - max_notes;
4522 try err.addNote("referenced {d} more times", .{remaining});
4553 err.addNote("referenced {d} more times", .{remaining});
45234554 }
45244555 }
45254556}
......@@ -4536,17 +4567,17 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor
45364567
45374568 var err = try diags.addErrorWithNotes(nnotes + 1);
45384569 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
45414572 var inote: usize = 0;
45424573 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
45434574 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()});
45454576 }
45464577
45474578 if (notes.items.len > max_notes) {
45484579 const remaining = notes.items.len - max_notes;
4549 try err.addNote("defined {d} more times", .{remaining});
4580 err.addNote("defined {d} more times", .{remaining});
45504581 }
45514582 }
45524583
......@@ -4570,7 +4601,7 @@ pub fn addFileError(
45704601 const diags = &self.base.comp.link_diags;
45714602 var err = try diags.addErrorWithNotes(1);
45724603 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()});
45744605}
45754606
45764607pub fn failFile(
......@@ -5184,6 +5215,30 @@ pub fn stringTableLookup(strtab: []const u8, off: u32) [:0]const u8 {
51845215 return slice[0..mem.indexOfScalar(u8, slice, 0).? :0];
51855216}
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
51875242const std = @import("std");
51885243const build_options = @import("build_options");
51895244const 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
523523 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
524524 rel.r_offset,
525525 });
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) });
527527 return error.RelocFailure;
528528}
529529
......@@ -539,7 +539,7 @@ fn reportTextRelocError(
539539 rel.r_offset,
540540 symbol.name(elf_file),
541541 });
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) });
543543 return error.RelocFailure;
544544}
545545
......@@ -555,8 +555,8 @@ fn reportPicError(
555555 rel.r_offset,
556556 symbol.name(elf_file),
557557 });
558 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
559 try err.addNote("recompile with -fPIC", .{});
558 err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
559 err.addNote("recompile with -fPIC", .{});
560560 return error.RelocFailure;
561561}
562562
......@@ -572,8 +572,8 @@ fn reportNoPicError(
572572 rel.r_offset,
573573 symbol.name(elf_file),
574574 });
575 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
576 try err.addNote("recompile with -fno-PIC", .{});
575 err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
576 err.addNote("recompile with -fno-PIC", .{});
577577 return error.RelocFailure;
578578}
579579
......@@ -1187,7 +1187,7 @@ const x86_64 = struct {
11871187 x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {
11881188 var err = try diags.addErrorWithNotes(1);
11891189 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}", .{
11911191 atom.file(elf_file).?.fmtPath(),
11921192 atom.name(elf_file),
11931193 rel.r_offset,
......@@ -1332,7 +1332,7 @@ const x86_64 = struct {
13321332 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
13331333 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
13341334 });
1335 try err.addNote("in {}:{s} at offset 0x{x}", .{
1335 err.addNote("in {}:{s} at offset 0x{x}", .{
13361336 self.file(elf_file).?.fmtPath(),
13371337 self.name(elf_file),
13381338 rels[0].r_offset,
......@@ -1388,7 +1388,7 @@ const x86_64 = struct {
13881388 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
13891389 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
13901390 });
1391 try err.addNote("in {}:{s} at offset 0x{x}", .{
1391 err.addNote("in {}:{s} at offset 0x{x}", .{
13921392 self.file(elf_file).?.fmtPath(),
13931393 self.name(elf_file),
13941394 rels[0].r_offset,
......@@ -1485,7 +1485,7 @@ const x86_64 = struct {
14851485 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
14861486 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
14871487 });
1488 try err.addNote("in {}:{s} at offset 0x{x}", .{
1488 err.addNote("in {}:{s} at offset 0x{x}", .{
14891489 self.file(elf_file).?.fmtPath(),
14901490 self.name(elf_file),
14911491 rels[0].r_offset,
......@@ -1672,7 +1672,7 @@ const aarch64 = struct {
16721672 // TODO: relax
16731673 var err = try diags.addErrorWithNotes(1);
16741674 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}", .{
16761676 atom.file(elf_file).?.fmtPath(),
16771677 atom.name(elf_file),
16781678 r_offset,
......@@ -1959,7 +1959,7 @@ const riscv = struct {
19591959 // TODO: implement searching forward
19601960 var err = try diags.addErrorWithNotes(1);
19611961 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}", .{
19631963 atom.file(elf_file).?.fmtPath(),
19641964 atom.name(elf_file),
19651965 rel.r_offset,
src/link/Elf/AtomList.zig+3-2
......@@ -58,7 +58,7 @@ pub fn allocate(list: *AtomList, elf_file: *Elf) !void {
5858 if (expand_section) last_atom_ref.* = list.lastAtom(elf_file).ref();
5959 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.
6262 {
6363 var idx: usize = 0;
6464 while (idx < list.atoms.keys().len) : (idx += 1) {
......@@ -78,7 +78,8 @@ pub fn allocate(list: *AtomList, elf_file: *Elf) !void {
7878 placement_atom.next_atom_ref = list.firstAtom(elf_file).ref();
7979 }
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 index
81 // If we had a link from Atom to parent AtomList we would not need to
82 // update Atom's value or osec index.
8283 for (list.atoms.keys()) |ref| {
8384 const atom_ptr = elf_file.atom(ref).?;
8485 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 {
797797 if (!isNull(data[end .. end + sh_entsize])) {
798798 var err = try diags.addErrorWithNotes(1);
799799 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) });
801801 return error.LinkFailure;
802802 }
803803 end += sh_entsize;
......@@ -812,7 +812,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void {
812812 if (shdr.sh_size % sh_entsize != 0) {
813813 var err = try diags.addErrorWithNotes(1);
814814 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) });
816816 return error.LinkFailure;
817817 }
818818
......@@ -889,8 +889,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{
889889 const res = imsec.findSubsection(@intCast(esym.st_value)) orelse {
890890 var err = try diags.addErrorWithNotes(2);
891891 try err.addMsg("invalid symbol value: {x}", .{esym.st_value});
892 try err.addNote("for symbol {s}", .{sym.name(elf_file)});
893 try err.addNote("in {}", .{self.fmtPath()});
892 err.addNote("for symbol {s}", .{sym.name(elf_file)});
893 err.addNote("in {}", .{self.fmtPath()});
894894 return error.LinkFailure;
895895 };
896896
......@@ -915,7 +915,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{
915915 const res = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {
916916 var err = try diags.addErrorWithNotes(1);
917917 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) });
919919 return error.LinkFailure;
920920 };
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 {
278278 .{ .kind = .code, .ty = .anyerror_type },
279279 metadata.text_symbol_index,
280280 ) catch |err| return switch (err) {
281 error.CodegenFail => error.FlushFailure,
282 else => |e| e,
281 error.CodegenFail => error.LinkFailure,
282 else => |e| return e,
283283 };
284284 if (metadata.rodata_state != .unused) self.updateLazySymbol(
285285 elf_file,
......@@ -287,8 +287,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
287287 .{ .kind = .const_data, .ty = .anyerror_type },
288288 metadata.rodata_symbol_index,
289289 ) catch |err| return switch (err) {
290 error.CodegenFail => error.FlushFailure,
291 else => |e| e,
290 error.CodegenFail => error.LinkFailure,
291 else => |e| return e,
292292 };
293293 }
294294 for (self.lazy_syms.values()) |*metadata| {
......@@ -933,6 +933,7 @@ pub fn getNavVAddr(
933933 const this_sym = self.symbol(this_sym_index);
934934 const vaddr = this_sym.address(.{}, elf_file);
935935 switch (reloc_info.parent) {
936 .none => unreachable,
936937 .atom_index => |atom_index| {
937938 const parent_atom = self.symbol(atom_index).atom(elf_file).?;
938939 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
......@@ -965,6 +966,7 @@ pub fn getUavVAddr(
965966 const sym = self.symbol(sym_index);
966967 const vaddr = sym.address(.{}, elf_file);
967968 switch (reloc_info.parent) {
969 .none => unreachable,
968970 .atom_index => |atom_index| {
969971 const parent_atom = self.symbol(atom_index).atom(elf_file).?;
970972 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
......@@ -1261,7 +1263,7 @@ fn updateNavCode(
12611263 shdr_index: u32,
12621264 code: []const u8,
12631265 stt_bits: u8,
1264) !void {
1266) link.File.UpdateNavError!void {
12651267 const zcu = pt.zcu;
12661268 const gpa = zcu.gpa;
12671269 const ip = &zcu.intern_pool;
......@@ -1298,7 +1300,9 @@ fn updateNavCode(
12981300 const capacity = atom_ptr.capacity(elf_file);
12991301 const need_realloc = code.len > capacity or !required_alignment.check(@intCast(atom_ptr.value));
13001302 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
13021306 log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom_ptr.value });
13031307 if (old_vaddr != atom_ptr.value) {
13041308 sym.value = 0;
......@@ -1308,7 +1312,9 @@ fn updateNavCode(
13081312 // TODO shrink section size
13091313 }
13101314 } 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
13121318 errdefer self.freeNavMetadata(elf_file, sym_index);
13131319 sym.value = 0;
13141320 esym.st_value = 0;
......@@ -1333,14 +1339,15 @@ fn updateNavCode(
13331339 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
13341340 }
13351341 },
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)}),
13371343 }
13381344 }
13391345
13401346 const shdr = elf_file.sections.items(.shdr)[shdr_index];
13411347 if (shdr.sh_type != elf.SHT_NOBITS) {
13421348 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)});
13441351 log.debug("writing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), file_offset, file_offset + code.len });
13451352 }
13461353}
......@@ -1353,7 +1360,7 @@ fn updateTlv(
13531360 sym_index: Symbol.Index,
13541361 shndx: u32,
13551362 code: []const u8,
1356) !void {
1363) link.File.UpdateNavError!void {
13571364 const zcu = pt.zcu;
13581365 const ip = &zcu.intern_pool;
13591366 const gpa = zcu.gpa;
......@@ -1383,7 +1390,8 @@ fn updateTlv(
13831390 const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index);
13841391 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)});
13871395 sym.value = 0;
13881396 esym.st_value = 0;
13891397
......@@ -1392,7 +1400,8 @@ fn updateTlv(
13921400 const shdr = elf_file.sections.items(.shdr)[shndx];
13931401 if (shdr.sh_type != elf.SHT_NOBITS) {
13941402 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)});
13961405 log.debug("writing TLV {s} from 0x{x} to 0x{x}", .{
13971406 atom_ptr.name(elf_file),
13981407 file_offset,
......@@ -1408,7 +1417,7 @@ pub fn updateFunc(
14081417 func_index: InternPool.Index,
14091418 air: Air,
14101419 liveness: Liveness,
1411) !void {
1420) link.File.UpdateNavError!void {
14121421 const tracy = trace(@src());
14131422 defer tracy.end();
14141423
......@@ -1422,13 +1431,13 @@ pub fn updateFunc(
14221431 const sym_index = try self.getOrCreateMetadataForNav(zcu, func.owner_nav);
14231432 self.atom(self.symbol(sym_index).ref.index).?.freeRelocs(self);
14241433
1425 var code_buffer = std.ArrayList(u8).init(gpa);
1426 defer code_buffer.deinit();
1434 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1435 defer code_buffer.deinit(gpa);
14271436
14281437 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;
14291438 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
14301439
1431 const res = try codegen.generateFunction(
1440 try codegen.generateFunction(
14321441 &elf_file.base,
14331442 pt,
14341443 zcu.navSrcLoc(func.owner_nav),
......@@ -1438,14 +1447,7 @@ pub fn updateFunc(
14381447 &code_buffer,
14391448 if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none,
14401449 );
1441
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 };
1450 const code = code_buffer.items;
14491451
14501452 const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, sym_index, code);
14511453 log.debug("setting shdr({x},{s}) for {}", .{
......@@ -1463,7 +1465,8 @@ pub fn updateFunc(
14631465 break :blk .{ atom_ptr.value, atom_ptr.alignment };
14641466 };
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
14681471 // Exports will be updated by `Zcu.processExports` after the update.
14691472
......@@ -1511,7 +1514,8 @@ pub fn updateFunc(
15111514 target_sym.flags.has_trampoline = true;
15121515 }
15131516 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)});
15151519 }
15161520}
15171521
......@@ -1547,7 +1551,11 @@ pub fn updateNav(
15471551 if (self.dwarf) |*dwarf| dwarf: {
15481552 var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf;
15491553 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 };
15511559 }
15521560 return;
15531561 },
......@@ -1558,13 +1566,13 @@ pub fn updateNav(
15581566 const sym_index = try self.getOrCreateMetadataForNav(zcu, nav_index);
15591567 self.symbol(sym_index).atom(elf_file).?.freeRelocs(self);
15601568
1561 var code_buffer = std.ArrayList(u8).init(zcu.gpa);
1562 defer code_buffer.deinit();
1569 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1570 defer code_buffer.deinit(zcu.gpa);
15631571
15641572 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;
15651573 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
15661574
1567 const res = try codegen.generateSymbol(
1575 try codegen.generateSymbol(
15681576 &elf_file.base,
15691577 pt,
15701578 zcu.navSrcLoc(nav_index),
......@@ -1572,14 +1580,7 @@ pub fn updateNav(
15721580 &code_buffer,
15731581 .{ .atom_index = sym_index },
15741582 );
1575
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 };
1583 const code = code_buffer.items;
15831584
15841585 const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, sym_index, code);
15851586 log.debug("setting shdr({x},{s}) for {}", .{
......@@ -1592,7 +1593,11 @@ pub fn updateNav(
15921593 else
15931594 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 };
15961601 } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index);
15971602
15981603 // Exports will be updated by `Zcu.processExports` after the update.
......@@ -1602,7 +1607,7 @@ pub fn updateContainerType(
16021607 self: *ZigObject,
16031608 pt: Zcu.PerThread,
16041609 ty: InternPool.Index,
1605) link.File.UpdateNavError!void {
1610) !void {
16061611 const tracy = trace(@src());
16071612 defer tracy.end();
16081613
......@@ -1620,8 +1625,8 @@ fn updateLazySymbol(
16201625 const gpa = zcu.gpa;
16211626
16221627 var required_alignment: InternPool.Alignment = .none;
1623 var code_buffer = std.ArrayList(u8).init(gpa);
1624 defer code_buffer.deinit();
1628 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1629 defer code_buffer.deinit(gpa);
16251630
16261631 const name_str_index = blk: {
16271632 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{
......@@ -1633,7 +1638,7 @@ fn updateLazySymbol(
16331638 };
16341639
16351640 const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;
1636 const res = try codegen.generateLazySymbol(
1641 try codegen.generateLazySymbol(
16371642 &elf_file.base,
16381643 pt,
16391644 src,
......@@ -1643,13 +1648,7 @@ fn updateLazySymbol(
16431648 .none,
16441649 .{ .atom_index = symbol_index },
16451650 );
1646 const code = switch (res) {
1647 .ok => code_buffer.items,
1648 .fail => |em| {
1649 log.err("{s}", .{em.msg});
1650 return error.CodegenFail;
1651 },
1652 };
1651 const code = code_buffer.items;
16531652
16541653 const output_section_index = switch (sym.kind) {
16551654 .code => if (self.text_index) |sym_index|
......@@ -1696,7 +1695,7 @@ fn updateLazySymbol(
16961695 local_sym.value = 0;
16971696 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));
17001699}
17011700
17021701const LowerConstResult = union(enum) {
......@@ -1716,13 +1715,13 @@ fn lowerConst(
17161715) !LowerConstResult {
17171716 const gpa = pt.zcu.gpa;
17181717
1719 var code_buffer = std.ArrayList(u8).init(gpa);
1720 defer code_buffer.deinit();
1718 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1719 defer code_buffer.deinit(gpa);
17211720
17221721 const name_off = try self.addString(gpa, name);
17231722 const sym_index = try self.newSymbolWithAtom(gpa, name_off);
17241723
1725 const res = try codegen.generateSymbol(
1724 try codegen.generateSymbol(
17261725 &elf_file.base,
17271726 pt,
17281727 src_loc,
......@@ -1730,10 +1729,7 @@ fn lowerConst(
17301729 &code_buffer,
17311730 .{ .atom_index = sym_index },
17321731 );
1733 const code = switch (res) {
1734 .ok => code_buffer.items,
1735 .fail => |em| return .{ .fail = em },
1736 };
1732 const code = code_buffer.items;
17371733
17381734 const local_sym = self.symbol(sym_index);
17391735 const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index];
......@@ -1748,7 +1744,7 @@ fn lowerConst(
17481744 try self.allocateAtom(atom_ptr, true, elf_file);
17491745 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
17531749 return .{ .ok = sym_index };
17541750}
......@@ -1758,7 +1754,7 @@ pub fn updateExports(
17581754 elf_file: *Elf,
17591755 pt: Zcu.PerThread,
17601756 exported: Zcu.Exported,
1761 export_indices: []const u32,
1757 export_indices: []const Zcu.Export.Index,
17621758) link.File.UpdateExportsError!void {
17631759 const tracy = trace(@src());
17641760 defer tracy.end();
......@@ -1771,7 +1767,7 @@ pub fn updateExports(
17711767 break :blk self.navs.getPtr(nav).?;
17721768 },
17731769 .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);
17751771 const res = try self.lowerUav(elf_file, pt, uav, .none, first_exp.src);
17761772 switch (res) {
17771773 .mcv => {},
......@@ -1792,7 +1788,7 @@ pub fn updateExports(
17921788 const esym_shndx = self.symtab.items(.shndx)[esym_index];
17931789
17941790 for (export_indices) |export_idx| {
1795 const exp = zcu.all_exports.items[export_idx];
1791 const exp = export_idx.ptr(zcu);
17961792 if (exp.opts.section.unwrap()) |section_name| {
17971793 if (!section_name.eqlSlice(".text", &zcu.intern_pool)) {
17981794 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
......@@ -1849,7 +1845,13 @@ pub fn updateExports(
18491845
18501846pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
18511847 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 };
18531855 }
18541856}
18551857
......@@ -1935,8 +1937,8 @@ pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, e
19351937 const shdr = &slice.items(.shdr)[atom_ptr.output_section_index];
19361938 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 section
1939 // In every other case, we need to redo the prev/next links
1940 // This only works if this atom is the only atom in the output section. In
1941 // every other case, we need to redo the prev/next links.
19401942 if (last_atom_ref.eql(atom_ptr.ref())) last_atom_ref.* = .{};
19411943
19421944 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 {
611611 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
612612 rel.r_offset,
613613 });
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()});
615615 return error.RelocFailure;
616616}
617617
src/link/Elf/relocatable.zig+6-6
......@@ -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 {
22 const gpa = comp.gpa;
33 const diags = &comp.link_diags;
44
5 if (diags.hasErrors()) return error.FlushFailure;
5 if (diags.hasErrors()) return error.LinkFailure;
66
77 // First, we flush relocatable object file generated with our backends.
88 if (elf_file.zigObjectPtr()) |zig_object| {
......@@ -127,13 +127,13 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!v
127127 try elf_file.base.file.?.setEndPos(total_size);
128128 try elf_file.base.file.?.pwriteAll(buffer.items, 0);
129129
130 if (diags.hasErrors()) return error.FlushFailure;
130 if (diags.hasErrors()) return error.LinkFailure;
131131}
132132
133pub fn flushObject(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void {
133pub fn flushObject(elf_file: *Elf, comp: *Compilation) !void {
134134 const diags = &comp.link_diags;
135135
136 if (diags.hasErrors()) return error.FlushFailure;
136 if (diags.hasErrors()) return error.LinkFailure;
137137
138138 // Now, we are ready to resolve the symbols across all input files.
139139 // 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
179179 try elf_file.writeShdrTable();
180180 try elf_file.writeElfHeader();
181181
182 if (diags.hasErrors()) return error.FlushFailure;
182 if (diags.hasErrors()) return error.LinkFailure;
183183}
184184
185185fn 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
434434 // libc/libSystem dep
435435 self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) {
436436 error.MissingLibSystem => {}, // already reported
437 else => |e| return e, // TODO: convert into an error
437 else => |e| return diags.fail("failed to resolve libSystem: {s}", .{@errorName(e)}),
438438 };
439439
440440 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
481481 }
482482 };
483483
484 if (diags.hasErrors()) return error.FlushFailure;
484 if (diags.hasErrors()) return error.LinkFailure;
485485
486486 {
487487 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
494494
495495 try self.resolveSymbols();
496496 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
499502 if (self.base.gc_sections) {
500503 try dead_strip.gcAtoms(self);
501504 }
502505
503506 self.checkDuplicates() catch |err| switch (err) {
504 error.HasDuplicates => return error.FlushFailure,
507 error.HasDuplicates => return error.LinkFailure,
505508 else => |e| return diags.fail("failed to check for duplicate symbol definitions: {s}", .{@errorName(e)}),
506509 };
507510
......@@ -516,7 +519,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
516519 self.claimUnresolved();
517520
518521 self.scanRelocs() catch |err| switch (err) {
519 error.HasUndefinedSymbols => return error.FlushFailure,
522 error.HasUndefinedSymbols => return error.LinkFailure,
520523 else => |e| return diags.fail("failed to scan relocations: {s}", .{@errorName(e)}),
521524 };
522525
......@@ -529,7 +532,10 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
529532 try self.generateUnwindInfo();
530533
531534 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 };
533539 self.allocateSegments();
534540 self.allocateSyntheticSymbols();
535541
......@@ -543,7 +549,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
543549
544550 if (self.getZigObject()) |zo| {
545551 zo.resolveRelocs(self) catch |err| switch (err) {
546 error.ResolveFailed => return error.FlushFailure,
552 error.ResolveFailed => return error.LinkFailure,
547553 else => |e| return e,
548554 };
549555 }
......@@ -551,7 +557,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
551557
552558 try self.writeSectionsToFile();
553559 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
556566 var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: {
557567 // Preallocate space for the code signature.
......@@ -561,7 +571,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
561571 // where the code signature goes into.
562572 var codesig = CodeSignature.init(self.getPageSize());
563573 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) });
565576 try self.writeCodeSignaturePadding(&codesig);
566577 break :blk codesig;
567578 } else null;
......@@ -573,15 +584,34 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
573584 self.getPageSize(),
574585 );
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 };
577592 try self.writeHeader(ncmds, sizeofcmds);
578 try self.writeUuid(uuid_cmd_offset, self.requiresCodeSig());
579 if (self.getDebugSymbols()) |dsym| try dsym.flushModule(self);
593 self.writeUuid(uuid_cmd_offset, self.requiresCodeSig()) catch |err| switch (err) {
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.
581604 if (codesig) |*csig| {
582 try self.writeCodeSignature(csig); // code signing always comes last
605 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 };
583610 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 };
585615 }
586616}
587617
......@@ -1545,21 +1575,21 @@ fn reportUndefs(self: *MachO) !void {
15451575 try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});
15461576
15471577 switch (notes) {
1548 .force_undefined => try err.addNote("referenced with linker flag -u", .{}),
1549 .entry => try err.addNote("referenced with linker flag -e", .{}),
1550 .dyld_stub_binder, .objc_msgsend => try err.addNote("referenced implicitly", .{}),
1578 .force_undefined => err.addNote("referenced with linker flag -u", .{}),
1579 .entry => err.addNote("referenced with linker flag -e", .{}),
1580 .dyld_stub_binder, .objc_msgsend => err.addNote("referenced implicitly", .{}),
15511581 .refs => |refs| {
15521582 var inote: usize = 0;
15531583 while (inote < @min(refs.items.len, max_notes)) : (inote += 1) {
15541584 const ref = refs.items[inote];
15551585 const file = self.getFile(ref.file).?;
15561586 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) });
15581588 }
15591589
15601590 if (refs.items.len > max_notes) {
15611591 const remaining = refs.items.len - max_notes;
1562 try err.addNote("referenced {d} more times", .{remaining});
1592 err.addNote("referenced {d} more times", .{remaining});
15631593 }
15641594 },
15651595 }
......@@ -2171,7 +2201,7 @@ fn allocateSections(self: *MachO) !void {
21712201 fileoff = mem.alignForward(u32, fileoff, page_size);
21722202 }
21732203
2174 const alignment = try math.powi(u32, 2, header.@"align");
2204 const alignment = try self.alignPow(header.@"align");
21752205
21762206 vmaddr = mem.alignForward(u64, vmaddr, alignment);
21772207 header.addr = vmaddr;
......@@ -2327,7 +2357,7 @@ fn allocateLinkeditSegment(self: *MachO) !void {
23272357 seg.vmaddr = mem.alignForward(u64, vmaddr, page_size);
23282358 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);
23312361 // DYLD_INFO_ONLY
23322362 {
23332363 const cmd = &self.dyld_info_cmd;
......@@ -2392,7 +2422,7 @@ fn resizeSections(self: *MachO) !void {
23922422 if (header.isZerofill()) continue;
23932423 if (self.isZigSection(@intCast(n_sect))) continue; // TODO this is horrible
23942424 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);
23962426 try out.resize(self.base.comp.gpa, size);
23972427 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
23982428 @memset(out.items, padding_byte);
......@@ -2489,7 +2519,7 @@ fn writeThunkWorker(self: *MachO, thunk: Thunk) void {
24892519
24902520 const doWork = struct {
24912521 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);
24932523 const size = th.size();
24942524 var stream = std.io.fixedBufferStream(buffer[off..][0..size]);
24952525 try th.write(macho_file, stream.writer());
......@@ -2601,7 +2631,7 @@ fn writeSectionsToFile(self: *MachO) !void {
26012631
26022632 const slice = self.sections.slice();
26032633 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);
26052635 }
26062636}
26072637
......@@ -2644,7 +2674,7 @@ fn writeDyldInfo(self: *MachO) !void {
26442674 try self.lazy_bind_section.write(writer);
26452675 try stream.seekTo(cmd.export_off - base_off);
26462676 try self.export_trie.write(writer);
2647 try self.base.file.?.pwriteAll(buffer, cmd.rebase_off);
2677 try self.pwriteAll(buffer, cmd.rebase_off);
26482678}
26492679
26502680pub fn writeDataInCode(self: *MachO) !void {
......@@ -2655,7 +2685,7 @@ pub fn writeDataInCode(self: *MachO) !void {
26552685 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.data_in_code.size());
26562686 defer buffer.deinit();
26572687 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);
26592689}
26602690
26612691fn writeIndsymtab(self: *MachO) !void {
......@@ -2667,15 +2697,15 @@ fn writeIndsymtab(self: *MachO) !void {
26672697 var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size);
26682698 defer buffer.deinit();
26692699 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);
26712701}
26722702
26732703pub fn writeSymtabToFile(self: *MachO) !void {
26742704 const tracy = trace(@src());
26752705 defer tracy.end();
26762706 const cmd = self.symtab_cmd;
2677 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
2678 try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff);
2707 try self.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
2708 try self.pwriteAll(self.strtab.items, cmd.stroff);
26792709}
26802710
26812711fn writeUnwindInfo(self: *MachO) !void {
......@@ -2686,20 +2716,20 @@ fn writeUnwindInfo(self: *MachO) !void {
26862716
26872717 if (self.eh_frame_sect_index) |index| {
26882718 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);
26902720 const buffer = try gpa.alloc(u8, size);
26912721 defer gpa.free(buffer);
26922722 eh_frame.write(self, buffer);
2693 try self.base.file.?.pwriteAll(buffer, header.offset);
2723 try self.pwriteAll(buffer, header.offset);
26942724 }
26952725
26962726 if (self.unwind_info_sect_index) |index| {
26972727 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);
26992729 const buffer = try gpa.alloc(u8, size);
27002730 defer gpa.free(buffer);
27012731 try self.unwind_info.write(self, buffer);
2702 try self.base.file.?.pwriteAll(buffer, header.offset);
2732 try self.pwriteAll(buffer, header.offset);
27032733 }
27042734}
27052735
......@@ -2890,7 +2920,7 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
28902920
28912921 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
28952925 return .{ ncmds, buffer.len, uuid_cmd_offset };
28962926}
......@@ -2944,7 +2974,7 @@ fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void {
29442974
29452975 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);
29482978}
29492979
29502980fn 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 {
29542984 } else self.codesig_cmd.dataoff;
29552985 try calcUuid(self.base.comp, self.base.file.?, file_size, &self.uuid_cmd.uuid);
29562986 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);
29582988}
29592989
29602990pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
......@@ -2968,7 +2998,7 @@ pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
29682998 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
29692999 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
29703000 // 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
29733003 self.codesig_cmd.dataoff = @as(u32, @intCast(offset));
29743004 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));
......@@ -2995,10 +3025,16 @@ pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {
29953025 offset + buffer.items.len,
29963026 });
29973027
2998 try self.base.file.?.pwriteAll(buffer.items, offset);
3028 try self.pwriteAll(buffer.items, offset);
29993029}
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 {
30023038 if (build_options.skip_non_native and builtin.object_format != .macho) {
30033039 @panic("Attempted to compile for object format that was disabled by build configuration");
30043040 }
......@@ -3006,7 +3042,7 @@ pub fn updateFunc(self: *MachO, pt: Zcu.PerThread, func_index: InternPool.Index,
30063042 return self.getZigObject().?.updateFunc(self, pt, func_index, air, liveness);
30073043}
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 {
30103046 if (build_options.skip_non_native and builtin.object_format != .macho) {
30113047 @panic("Attempted to compile for object format that was disabled by build configuration");
30123048 }
......@@ -3023,7 +3059,7 @@ pub fn updateExports(
30233059 self: *MachO,
30243060 pt: Zcu.PerThread,
30253061 exported: Zcu.Exported,
3026 export_indices: []const u32,
3062 export_indices: []const Zcu.Export.Index,
30273063) link.File.UpdateExportsError!void {
30283064 if (build_options.skip_non_native and builtin.object_format != .macho) {
30293065 @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
31993235 const gpa = self.base.comp.gpa;
32003236 try self.copyRangeAll(old_offset, new_offset, size);
32013237 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.
32033239 defer gpa.free(zeroes);
32043240 @memset(zeroes, 0);
32053241 try self.base.file.?.pwriteAll(zeroes, old_offset);
......@@ -3306,10 +3342,9 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
33063342 const allocSect = struct {
33073343 fn allocSect(macho_file: *MachO, sect_id: u8, size: u64) !void {
33083344 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");
33103346 if (!sect.isZerofill()) {
3311 sect.offset = math.cast(u32, try macho_file.findFreeSpace(size, alignment)) orelse
3312 return error.Overflow;
3347 sect.offset = try macho_file.cast(u32, try macho_file.findFreeSpace(size, alignment));
33133348 }
33143349 sect.addr = macho_file.findFreeSpaceVirtual(size, alignment);
33153350 sect.size = size;
......@@ -3441,8 +3476,8 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo
34413476 seg_id,
34423477 seg.segName(),
34433478 });
3444 try 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", .{});
3479 err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{});
3480 err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
34463481 }
34473482
34483483 seg.vmsize = needed_size;
......@@ -3744,7 +3779,7 @@ pub fn reportParseError2(
37443779 const diags = &self.base.comp.link_diags;
37453780 var err = try diags.addErrorWithNotes(1);
37463781 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()});
37483783}
37493784
37503785fn reportMissingDependencyError(
......@@ -3758,10 +3793,10 @@ fn reportMissingDependencyError(
37583793 const diags = &self.base.comp.link_diags;
37593794 var err = try diags.addErrorWithNotes(2 + checked_paths.len);
37603795 try err.addMsg(format, args);
3761 try err.addNote("while resolving {s}", .{path});
3762 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3796 err.addNote("while resolving {s}", .{path});
3797 err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
37633798 for (checked_paths) |p| {
3764 try err.addNote("tried {s}", .{p});
3799 err.addNote("tried {s}", .{p});
37653800 }
37663801}
37673802
......@@ -3775,8 +3810,8 @@ fn reportDependencyError(
37753810 const diags = &self.base.comp.link_diags;
37763811 var err = try diags.addErrorWithNotes(2);
37773812 try err.addMsg(format, args);
3778 try err.addNote("while parsing {s}", .{path});
3779 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3813 err.addNote("while parsing {s}", .{path});
3814 err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
37803815}
37813816
37823817fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
......@@ -3806,17 +3841,17 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
38063841
38073842 var err = try diags.addErrorWithNotes(nnotes + 1);
38083843 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
38113846 var inote: usize = 0;
38123847 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
38133848 const file = self.getFile(notes.items[inote]).?;
3814 try err.addNote("defined by {}", .{file.fmtPath()});
3849 err.addNote("defined by {}", .{file.fmtPath()});
38153850 }
38163851
38173852 if (notes.items.len > max_notes) {
38183853 const remaining = notes.items.len - max_notes;
3819 try err.addNote("defined {d} more times", .{remaining});
3854 err.addNote("defined {d} more times", .{remaining});
38203855 }
38213856 }
38223857 return error.HasDuplicates;
......@@ -5310,6 +5345,40 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
53105345 return true;
53115346}
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
53135382/// Branch instruction has 26 bits immediate but is 4 byte aligned.
53145383const jump_bits = @bitSizeOf(i28);
53155384const max_distance = (1 << (jump_bits - 1));
src/link/MachO/Atom.zig+7-7
......@@ -909,8 +909,8 @@ const x86_64 = struct {
909909 rel.offset,
910910 rel.fmtPretty(.x86_64),
911911 });
912 try err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)});
913 try err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()});
912 err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)});
913 err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()});
914914 return error.RelaxFailUnexpectedInstruction;
915915 },
916916 }
......@@ -971,7 +971,7 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {
971971 }
972972}
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 {
975975 const tracy = trace(@src());
976976 defer tracy.end();
977977
......@@ -983,15 +983,15 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r
983983 var i: usize = 0;
984984 for (relocs) |rel| {
985985 defer i += 1;
986 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
987 const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow;
986 const rel_offset = try macho_file.cast(usize, rel.offset - self.off);
987 const r_address: i32 = try macho_file.cast(i32, self.value + rel_offset);
988988 assert(r_address >= 0);
989989 const r_symbolnum = r_symbolnum: {
990990 const r_symbolnum: u32 = switch (rel.tag) {
991991 .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1,
992992 .@"extern" => rel.getTargetSymbol(self, macho_file).getOutputSymtabIndex(macho_file).?,
993993 };
994 break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow;
994 break :r_symbolnum try macho_file.cast(u24, r_symbolnum);
995995 };
996996 const r_extern = rel.tag == .@"extern";
997997 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
10271027 } else if (addend > 0) {
10281028 buffer[i] = .{
10291029 .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)),
10311031 .r_pcrel = 0,
10321032 .r_length = 2,
10331033 .r_extern = 0,
src/link/MachO/InternalObject.zig+9-7
......@@ -414,10 +414,11 @@ pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file
414414 const rel = relocs[0];
415415 assert(rel.tag == .@"extern");
416416 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);
418418 try buffer.ensureUnusedCapacity(target_size);
419419 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);
421422 const res = try lp.insert(gpa, header.type(), buffer.items);
422423 buffer.clearRetainingCapacity();
423424 if (!res.found_existing) {
......@@ -607,10 +608,11 @@ pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void {
607608 if (!atom.isAlive()) continue;
608609 const sect = atom.getInputSection(macho_file);
609610 if (sect.isZerofill()) continue;
610 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
611 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
611 const off = try macho_file.cast(usize, atom.value);
612 const size = try macho_file.cast(usize, atom.size);
612613 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);
614616 try atom.resolveRelocs(macho_file, buffer);
615617 }
616618}
......@@ -644,13 +646,13 @@ fn addSection(self: *InternalObject, allocator: Allocator, segname: []const u8,
644646 return n_sect;
645647}
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 {
648650 const slice = self.sections.slice();
649651 assert(index < slice.items(.header).len);
650652 const sect = slice.items(.header)[index];
651653 const extra = slice.items(.extra)[index];
652654 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);
654656 return self.objc_methnames.items[sect.offset..][0..size];
655657 } else if (extra.is_objc_selref)
656658 return &self.objc_selrefs
src/link/MachO/Object.zig+32-34
......@@ -582,7 +582,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO)
582582 );
583583 return error.MalformedObject;
584584 }
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
587587 for (0..num_ptrs) |i| {
588588 const pos: u32 = @as(u32, @intCast(i)) * rec_size;
......@@ -650,8 +650,8 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
650650
651651 for (subs.items) |sub| {
652652 const atom = self.getAtom(sub.atom).?;
653 const atom_off = math.cast(usize, atom.off) orelse return error.Overflow;
654 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
653 const atom_off = try macho_file.cast(usize, atom.off);
654 const atom_size = try macho_file.cast(usize, atom.size);
655655 const atom_data = data[atom_off..][0..atom_size];
656656 const res = try lp.insert(gpa, header.type(), atom_data);
657657 if (!res.found_existing) {
......@@ -674,8 +674,8 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
674674 .local => rel.getTargetAtom(atom.*, macho_file),
675675 .@"extern" => rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?,
676676 };
677 const addend = math.cast(u32, rel.addend) orelse return error.Overflow;
678 const target_size = math.cast(usize, target.size) orelse return error.Overflow;
677 const addend = try macho_file.cast(u32, rel.addend);
678 const target_size = try macho_file.cast(usize, target.size);
679679 try buffer.ensureUnusedCapacity(target_size);
680680 buffer.resize(target_size) catch unreachable;
681681 const gop = try sections_data.getOrPut(target.n_sect);
......@@ -683,7 +683,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
683683 gop.value_ptr.* = try self.readSectionData(gpa, file, @intCast(target.n_sect));
684684 }
685685 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);
687687 @memcpy(buffer.items, data[target_off..][0..target_size]);
688688 const res = try lp.insert(gpa, header.type(), buffer.items[addend..]);
689689 buffer.clearRetainingCapacity();
......@@ -1033,7 +1033,7 @@ fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fi
10331033 const sect = slice.items(.header)[sect_id];
10341034 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);
10371037 try self.eh_frame_data.resize(allocator, size);
10381038 const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset);
10391039 if (amt != self.eh_frame_data.items.len) return error.InputOutput;
......@@ -1696,7 +1696,7 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void {
16961696
16971697pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {
16981698 // 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);
17001700 const basename = std.fs.path.basename(self.path.sub_path);
17011701 try Archive.writeHeader(basename, size, ar_format, writer);
17021702 // Data
......@@ -1826,7 +1826,7 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {
18261826
18271827 for (headers, 0..) |header, n_sect| {
18281828 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);
18301830 const data = try gpa.alloc(u8, size);
18311831 const amt = try file.preadAll(data, header.offset + self.offset);
18321832 if (amt != data.len) return error.InputOutput;
......@@ -1837,9 +1837,9 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {
18371837 if (!atom.isAlive()) continue;
18381838 const sect = atom.getInputSection(macho_file);
18391839 if (sect.isZerofill()) continue;
1840 const value = math.cast(usize, atom.value) orelse return error.Overflow;
1841 const off = math.cast(usize, atom.off) orelse return error.Overflow;
1842 const size = math.cast(usize, atom.size) orelse return error.Overflow;
1840 const value = try macho_file.cast(usize, atom.value);
1841 const off = try macho_file.cast(usize, atom.off);
1842 const size = try macho_file.cast(usize, atom.size);
18431843 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
18441844 const data = sections_data[atom.n_sect];
18451845 @memcpy(buffer[value..][0..size], data[off..][0..size]);
......@@ -1865,7 +1865,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
18651865
18661866 for (headers, 0..) |header, n_sect| {
18671867 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);
18691869 const data = try gpa.alloc(u8, size);
18701870 const amt = try file.preadAll(data, header.offset + self.offset);
18711871 if (amt != data.len) return error.InputOutput;
......@@ -1876,9 +1876,9 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
18761876 if (!atom.isAlive()) continue;
18771877 const sect = atom.getInputSection(macho_file);
18781878 if (sect.isZerofill()) continue;
1879 const value = math.cast(usize, atom.value) orelse return error.Overflow;
1880 const off = math.cast(usize, atom.off) orelse return error.Overflow;
1881 const size = math.cast(usize, atom.size) orelse return error.Overflow;
1879 const value = try macho_file.cast(usize, atom.value);
1880 const off = try macho_file.cast(usize, atom.off);
1881 const size = try macho_file.cast(usize, atom.size);
18821882 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
18831883 const data = sections_data[atom.n_sect];
18841884 @memcpy(buffer[value..][0..size], data[off..][0..size]);
......@@ -1909,29 +1909,27 @@ pub fn calcCompactUnwindSizeRelocatable(self: *Object, macho_file: *MachO) void
19091909 }
19101910}
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
19121927pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
19131928 const tracy = trace(@src());
19141929 defer tracy.end();
19151930
19161931 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
19351933 const nsect = macho_file.unwind_info_sect_index.?;
19361934 const buffer = macho_file.sections.items(.out)[nsect].items;
19371935 const relocs = macho_file.sections.items(.relocs)[nsect].items;
......@@ -1967,7 +1965,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
19671965
19681966 // Personality function
19691967 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).?);
19711969 var reloc = try addReloc(offset + 16, cpu_arch);
19721970 reloc.r_symbolnum = r_symbolnum;
19731971 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
290290/// We need this so that we can write to an archive.
291291/// TODO implement writing ZigObject data directly to a buffer instead.
292292pub fn readFileContents(self: *ZigObject, macho_file: *MachO) !void {
293 const diags = &macho_file.base.comp.link_diags;
293294 // Size of the output object file is always the offset + size of the strtab
294295 const size = macho_file.symtab_cmd.stroff + macho_file.symtab_cmd.strsize;
295296 const gpa = macho_file.base.comp.gpa;
296297 try self.data.resize(gpa, size);
297 const amt = try macho_file.base.file.?.preadAll(self.data.items, 0);
298 if (amt != size) return error.InputOutput;
298 const amt = macho_file.base.file.?.preadAll(self.data.items, 0) catch |err|
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", .{});
299302}
300303
301304pub 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 {
376379 if (atom.getRelocs(macho_file).len == 0) continue;
377380 // TODO: we will resolve and write ZigObject's TLS data twice:
378381 // 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);
380383 const code = try gpa.alloc(u8, atom_size);
381384 defer gpa.free(code);
382385 self.getAtomData(macho_file, atom.*, code) catch |err| {
......@@ -400,7 +403,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
400403 has_error = true;
401404 continue;
402405 };
403 try macho_file.base.file.?.pwriteAll(code, file_offset);
406 try macho_file.pwriteAll(code, file_offset);
404407 }
405408
406409 if (has_error) return error.ResolveFailed;
......@@ -419,7 +422,7 @@ pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {
419422 }
420423}
421424
422pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {
425pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) error{ LinkFailure, OutOfMemory }!void {
423426 const gpa = macho_file.base.comp.gpa;
424427 const diags = &macho_file.base.comp.link_diags;
425428
......@@ -432,14 +435,14 @@ pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {
432435 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
433436 if (atom.getRelocs(macho_file).len == 0) continue;
434437 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);
436439 const code = try gpa.alloc(u8, atom_size);
437440 defer gpa.free(code);
438441 self.getAtomData(macho_file, atom.*, code) catch |err|
439442 return diags.fail("failed to fetch code for '{s}': {s}", .{ atom.getName(macho_file), @errorName(err) });
440443 const file_offset = header.offset + atom.value;
441444 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);
443446 }
444447}
445448
......@@ -457,8 +460,8 @@ pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void {
457460 if (sect.isZerofill()) continue;
458461 if (macho_file.isZigSection(atom.out_n_sect)) continue;
459462 if (atom.getRelocs(macho_file).len == 0) continue;
460 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
461 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
463 const off = try macho_file.cast(usize, atom.value);
464 const size = try macho_file.cast(usize, atom.size);
462465 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
463466 try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
464467 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 {
480483 const sect = atom.getInputSection(macho_file);
481484 if (sect.isZerofill()) continue;
482485 if (macho_file.isZigSection(atom.out_n_sect)) continue;
483 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
484 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
486 const off = try macho_file.cast(usize, atom.value);
487 const size = try macho_file.cast(usize, atom.size);
485488 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
486489 try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
487490 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
546549 return sect;
547550}
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
550555 // Handle any lazy symbols that were emitted by incremental compilation.
551556 if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| {
552557 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)
559564 pt,
560565 .{ .kind = .code, .ty = .anyerror_type },
561566 metadata.text_symbol_index,
562 ) catch |err| return switch (err) {
563 error.CodegenFail => error.FlushFailure,
564 else => |e| e,
567 ) catch |err| switch (err) {
568 error.OutOfMemory => return error.OutOfMemory,
569 error.LinkFailure => return error.LinkFailure,
570 else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}),
565571 };
566572 if (metadata.const_state != .unused) self.updateLazySymbol(
567573 macho_file,
568574 pt,
569575 .{ .kind = .const_data, .ty = .anyerror_type },
570576 metadata.const_symbol_index,
571 ) catch |err| return switch (err) {
572 error.CodegenFail => error.FlushFailure,
573 else => |e| e,
577 ) catch |err| switch (err) {
578 error.OutOfMemory => return error.OutOfMemory,
579 error.LinkFailure => return error.LinkFailure,
580 else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}),
574581 };
575582 }
576583 for (self.lazy_syms.values()) |*metadata| {
......@@ -581,7 +588,10 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id)
581588 if (self.dwarf) |*dwarf| {
582589 const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid);
583590 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
586596 self.debug_abbrev_dirty = false;
587597 self.debug_aranges_dirty = false;
......@@ -616,6 +626,7 @@ pub fn getNavVAddr(
616626 const sym = self.symbols.items[sym_index];
617627 const vaddr = sym.getAddress(.{}, macho_file);
618628 switch (reloc_info.parent) {
629 .none => unreachable,
619630 .atom_index => |atom_index| {
620631 const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?;
621632 try parent_atom.addReloc(macho_file, .{
......@@ -655,6 +666,7 @@ pub fn getUavVAddr(
655666 const sym = self.symbols.items[sym_index];
656667 const vaddr = sym.getAddress(.{}, macho_file);
657668 switch (reloc_info.parent) {
669 .none => unreachable,
658670 .atom_index => |atom_index| {
659671 const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?;
660672 try parent_atom.addReloc(macho_file, .{
......@@ -766,7 +778,7 @@ pub fn updateFunc(
766778 func_index: InternPool.Index,
767779 air: Air,
768780 liveness: Liveness,
769) !void {
781) link.File.UpdateNavError!void {
770782 const tracy = trace(@src());
771783 defer tracy.end();
772784
......@@ -777,13 +789,13 @@ pub fn updateFunc(
777789 const sym_index = try self.getOrCreateMetadataForNav(macho_file, func.owner_nav);
778790 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
779791
780 var code_buffer = std.ArrayList(u8).init(gpa);
781 defer code_buffer.deinit();
792 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
793 defer code_buffer.deinit(gpa);
782794
783795 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;
784796 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
785797
786 const res = try codegen.generateFunction(
798 try codegen.generateFunction(
787799 &macho_file.base,
788800 pt,
789801 zcu.navSrcLoc(func.owner_nav),
......@@ -793,14 +805,7 @@ pub fn updateFunc(
793805 &code_buffer,
794806 if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none,
795807 );
796
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 };
808 const code = code_buffer.items;
804809
805810 const sect_index = try self.getNavOutputSection(macho_file, zcu, func.owner_nav, code);
806811 const old_rva, const old_alignment = blk: {
......@@ -813,7 +818,8 @@ pub fn updateFunc(
813818 break :blk .{ atom.value, atom.alignment };
814819 };
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
818824 // Exports will be updated by `Zcu.processExports` after the update.
819825 if (old_rva != new_rva and old_rva > 0) {
......@@ -850,7 +856,8 @@ pub fn updateFunc(
850856 }
851857 const target_sym = self.symbols.items[sym_index];
852858 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)});
854861 }
855862}
856863
......@@ -883,7 +890,11 @@ pub fn updateNav(
883890 if (self.dwarf) |*dwarf| dwarf: {
884891 var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf;
885892 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 };
887898 }
888899 return;
889900 },
......@@ -894,13 +905,13 @@ pub fn updateNav(
894905 const sym_index = try self.getOrCreateMetadataForNav(macho_file, nav_index);
895906 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
896907
897 var code_buffer = std.ArrayList(u8).init(zcu.gpa);
898 defer code_buffer.deinit();
908 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
909 defer code_buffer.deinit(zcu.gpa);
899910
900911 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;
901912 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
902913
903 const res = try codegen.generateSymbol(
914 try codegen.generateSymbol(
904915 &macho_file.base,
905916 pt,
906917 zcu.navSrcLoc(nav_index),
......@@ -908,21 +919,19 @@ pub fn updateNav(
908919 &code_buffer,
909920 .{ .atom_index = sym_index },
910921 );
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 };
919924 const sect_index = try self.getNavOutputSection(macho_file, zcu, nav_index, code);
920925 if (isThreadlocal(macho_file, nav_index))
921926 try self.updateTlv(macho_file, pt, nav_index, sym_index, sect_index, code)
922927 else
923928 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 };
926935 } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index);
927936
928937 // Exports will be updated by `Zcu.processExports` after the update.
......@@ -936,7 +945,7 @@ fn updateNavCode(
936945 sym_index: Symbol.Index,
937946 sect_index: u8,
938947 code: []const u8,
939) !void {
948) link.File.UpdateNavError!void {
940949 const zcu = pt.zcu;
941950 const gpa = zcu.gpa;
942951 const ip = &zcu.intern_pool;
......@@ -978,7 +987,8 @@ fn updateNavCode(
978987 const need_realloc = code.len > capacity or !required_alignment.check(atom.value);
979988
980989 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)});
982992 log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom.value });
983993 if (old_vaddr != atom.value) {
984994 sym.value = 0;
......@@ -991,7 +1001,8 @@ fn updateNavCode(
9911001 sect.size = needed_size;
9921002 }
9931003 } 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)});
9951006 errdefer self.freeNavMetadata(macho_file, sym_index);
9961007
9971008 sym.value = 0;
......@@ -1000,7 +1011,8 @@ fn updateNavCode(
10001011
10011012 if (!sect.isZerofill()) {
10021013 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)});
10041016 }
10051017}
10061018
......@@ -1198,13 +1210,13 @@ fn lowerConst(
11981210) !LowerConstResult {
11991211 const gpa = macho_file.base.comp.gpa;
12001212
1201 var code_buffer = std.ArrayList(u8).init(gpa);
1202 defer code_buffer.deinit();
1213 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1214 defer code_buffer.deinit(gpa);
12031215
12041216 const name_str = try self.addString(gpa, name);
12051217 const sym_index = try self.newSymbolWithAtom(gpa, name_str, macho_file);
12061218
1207 const res = try codegen.generateSymbol(
1219 try codegen.generateSymbol(
12081220 &macho_file.base,
12091221 pt,
12101222 src_loc,
......@@ -1212,10 +1224,7 @@ fn lowerConst(
12121224 &code_buffer,
12131225 .{ .atom_index = sym_index },
12141226 );
1215 const code = switch (res) {
1216 .ok => code_buffer.items,
1217 .fail => |em| return .{ .fail = em },
1218 };
1227 const code = code_buffer.items;
12191228
12201229 const sym = &self.symbols.items[sym_index];
12211230 sym.out_n_sect = output_section_index;
......@@ -1236,7 +1245,7 @@ fn lowerConst(
12361245
12371246 const sect = macho_file.sections.items(.header)[output_section_index];
12381247 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
12411250 return .{ .ok = sym_index };
12421251}
......@@ -1246,7 +1255,7 @@ pub fn updateExports(
12461255 macho_file: *MachO,
12471256 pt: Zcu.PerThread,
12481257 exported: Zcu.Exported,
1249 export_indices: []const u32,
1258 export_indices: []const Zcu.Export.Index,
12501259) link.File.UpdateExportsError!void {
12511260 const tracy = trace(@src());
12521261 defer tracy.end();
......@@ -1259,7 +1268,7 @@ pub fn updateExports(
12591268 break :blk self.navs.getPtr(nav).?;
12601269 },
12611270 .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);
12631272 const res = try self.lowerUav(macho_file, pt, uav, .none, first_exp.src);
12641273 switch (res) {
12651274 .mcv => {},
......@@ -1279,7 +1288,7 @@ pub fn updateExports(
12791288 const nlist = self.symtab.items(.nlist)[nlist_idx];
12801289
12811290 for (export_indices) |export_idx| {
1282 const exp = zcu.all_exports.items[export_idx];
1291 const exp = export_idx.ptr(zcu);
12831292 if (exp.opts.section.unwrap()) |section_name| {
12841293 if (!section_name.eqlSlice("__text", &zcu.intern_pool)) {
12851294 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
......@@ -1352,8 +1361,8 @@ fn updateLazySymbol(
13521361 const gpa = zcu.gpa;
13531362
13541363 var required_alignment: Atom.Alignment = .none;
1355 var code_buffer = std.ArrayList(u8).init(gpa);
1356 defer code_buffer.deinit();
1364 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1365 defer code_buffer.deinit(gpa);
13571366
13581367 const name_str = blk: {
13591368 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{
......@@ -1365,7 +1374,7 @@ fn updateLazySymbol(
13651374 };
13661375
13671376 const src = Type.fromInterned(lazy_sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;
1368 const res = try codegen.generateLazySymbol(
1377 try codegen.generateLazySymbol(
13691378 &macho_file.base,
13701379 pt,
13711380 src,
......@@ -1375,13 +1384,7 @@ fn updateLazySymbol(
13751384 .none,
13761385 .{ .atom_index = symbol_index },
13771386 );
1378 const code = switch (res) {
1379 .ok => code_buffer.items,
1380 .fail => |em| {
1381 log.err("{s}", .{em.msg});
1382 return error.CodegenFail;
1383 },
1384 };
1387 const code = code_buffer.items;
13851388
13861389 const output_section_index = switch (lazy_sym.kind) {
13871390 .code => macho_file.zig_text_sect_index.?,
......@@ -1412,12 +1415,18 @@ fn updateLazySymbol(
14121415
14131416 const sect = macho_file.sections.items(.header)[output_section_index];
14141417 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);
14161419}
14171420
14181421pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
14191422 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 };
14211430 }
14221431}
14231432
src/link/MachO/relocatable.zig+68-42
......@@ -18,13 +18,15 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
1818 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all
1919 // debug info segments/sections (this is apparently by design by Apple), we copy
2020 // 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.
2321 const path = positionals.items[0].path().?;
24 const in_file = try path.root_dir.handle.openFile(path.sub_path, .{});
25 const stat = try in_file.stat();
26 const amt = try in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size);
27 if (amt != stat.size) return error.InputOutput; // TODO: report an actual user error
22 const in_file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err|
23 return diags.fail("failed to open {}: {s}", .{ path, @errorName(err) });
24 const stat = in_file.stat() catch |err|
25 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});
2830 return;
2931 }
3032
......@@ -33,14 +35,18 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
3335 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
3436 }
3537
36 if (diags.hasErrors()) return error.FlushFailure;
38 if (diags.hasErrors()) return error.LinkFailure;
3739
3840 try macho_file.parseInputFiles();
3941
40 if (diags.hasErrors()) return error.FlushFailure;
42 if (diags.hasErrors()) return error.LinkFailure;
4143
4244 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 };
4450 markExports(macho_file);
4551 claimUnresolved(macho_file);
4652 try initOutputSections(macho_file);
......@@ -49,7 +55,10 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
4955 try calcSectionSizes(macho_file);
5056
5157 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 };
5362 allocateSegment(macho_file);
5463
5564 if (build_options.enable_logging) {
......@@ -93,11 +102,11 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
93102 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
94103 }
95104
96 if (diags.hasErrors()) return error.FlushFailure;
105 if (diags.hasErrors()) return error.LinkFailure;
97106
98107 try parseInputFilesAr(macho_file);
99108
100 if (diags.hasErrors()) return error.FlushFailure;
109 if (diags.hasErrors()) return error.LinkFailure;
101110
102111 // First, we flush relocatable object file generated with our backends.
103112 if (macho_file.getZigObject()) |zo| {
......@@ -108,7 +117,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
108117 try macho_file.addAtomsToSections();
109118 try calcSectionSizes(macho_file);
110119 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)});
112122 allocateSegment(macho_file);
113123
114124 if (build_options.enable_logging) {
......@@ -126,8 +136,6 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
126136 const ncmds, const sizeofcmds = try writeLoadCommands(macho_file);
127137 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.
131139 try zo.readFileContents(macho_file);
132140 }
133141
......@@ -152,7 +160,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
152160
153161 // Update sizes of contributing objects
154162 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)});
156165 }
157166
158167 // Update file offsets of contributing objects
......@@ -171,7 +180,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
171180 state.file_off = pos;
172181 pos += @sizeOf(Archive.ar_hdr);
173182 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);
175184 },
176185 .object => |o| {
177186 const state = &o.output_ar_state;
......@@ -179,7 +188,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
179188 state.file_off = pos;
180189 pos += @sizeOf(Archive.ar_hdr);
181190 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);
183192 },
184193 else => unreachable,
185194 }
......@@ -201,7 +210,10 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
201210 try writer.writeAll(Archive.ARMAG);
202211
203212 // 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
206218 // Write object files
207219 for (files.items) |index| {
......@@ -210,15 +222,16 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
210222 if (padding > 0) {
211223 try writer.writeByteNTimes(0, padding);
212224 }
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)});
214227 }
215228
216229 assert(buffer.items.len == total_size);
217230
218 try macho_file.base.file.?.setEndPos(total_size);
219 try macho_file.base.file.?.pwriteAll(buffer.items, 0);
231 try macho_file.setEndPos(total_size);
232 try macho_file.pwriteAll(buffer.items, 0);
220233
221 if (diags.hasErrors()) return error.FlushFailure;
234 if (diags.hasErrors()) return error.LinkFailure;
222235}
223236
224237fn parseInputFilesAr(macho_file: *MachO) !void {
......@@ -452,11 +465,10 @@ fn allocateSections(macho_file: *MachO) !void {
452465 for (slice.items(.header)) |*header| {
453466 const needed_size = header.size;
454467 header.size = 0;
455 const alignment = try math.powi(u32, 2, header.@"align");
468 const alignment = try macho_file.alignPow(header.@"align");
456469 if (!header.isZerofill()) {
457470 if (needed_size > macho_file.allocatedSize(header.offset)) {
458 header.offset = math.cast(u32, try macho_file.findFreeSpace(needed_size, alignment)) orelse
459 return error.Overflow;
471 header.offset = try macho_file.cast(u32, try macho_file.findFreeSpace(needed_size, alignment));
460472 }
461473 }
462474 if (needed_size > macho_file.allocatedSizeVirtual(header.addr)) {
......@@ -572,7 +584,7 @@ fn sortRelocs(macho_file: *MachO) void {
572584 }
573585}
574586
575fn writeSections(macho_file: *MachO) !void {
587fn writeSections(macho_file: *MachO) link.File.FlushError!void {
576588 const tracy = trace(@src());
577589 defer tracy.end();
578590
......@@ -583,7 +595,7 @@ fn writeSections(macho_file: *MachO) !void {
583595 for (slice.items(.header), slice.items(.out), slice.items(.relocs), 0..) |header, *out, *relocs, n_sect| {
584596 if (header.isZerofill()) continue;
585597 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);
587599 try out.resize(gpa, size);
588600 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
589601 @memset(out.items, padding_byte);
......@@ -662,16 +674,16 @@ fn writeSectionsToFile(macho_file: *MachO) !void {
662674
663675 const slice = macho_file.sections.slice();
664676 for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, out, relocs| {
665 try macho_file.base.file.?.pwriteAll(out.items, header.offset);
666 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
677 try macho_file.pwriteAll(out.items, header.offset);
678 try macho_file.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
667679 }
668680
669681 try macho_file.writeDataInCode();
670 try macho_file.base.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);
682 try macho_file.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff);
683 try macho_file.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff);
672684}
673685
674fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {
686fn writeLoadCommands(macho_file: *MachO) error{ LinkFailure, OutOfMemory }!struct { usize, usize } {
675687 const gpa = macho_file.base.comp.gpa;
676688 const needed_size = load_commands.calcLoadCommandsSizeObject(macho_file);
677689 const buffer = try gpa.alloc(u8, needed_size);
......@@ -686,31 +698,45 @@ fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {
686698 {
687699 assert(macho_file.segments.items.len == 1);
688700 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 };
690704 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 };
692708 }
693709 ncmds += 1;
694710 }
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 };
697715 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 };
699719 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 };
701723 ncmds += 1;
702724
703725 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 };
705729 ncmds += 1;
706730 } 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 };
708734 ncmds += 1;
709735 }
710736
711737 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
715741 return .{ ncmds, buffer.len };
716742}
......@@ -742,7 +768,7 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {
742768 header.ncmds = @intCast(ncmds);
743769 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);
746772}
747773
748774const std = @import("std");
src/link/NvPtx.zig+9-7
......@@ -82,11 +82,17 @@ pub fn deinit(self: *NvPtx) void {
8282 self.llvm_object.deinit();
8383}
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 {
8692 try self.llvm_object.updateFunc(pt, func_index, air, liveness);
8793}
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 {
9096 return self.llvm_object.updateNav(pt, nav);
9197}
9298
......@@ -94,7 +100,7 @@ pub fn updateExports(
94100 self: *NvPtx,
95101 pt: Zcu.PerThread,
96102 exported: Zcu.Exported,
97 export_indices: []const u32,
103 export_indices: []const Zcu.Export.Index,
98104) !void {
99105 if (build_options.skip_non_native and builtin.object_format != .nvptx)
100106 @panic("Attempted to compile for object format that was disabled by build configuration");
......@@ -102,10 +108,6 @@ pub fn updateExports(
102108 return self.llvm_object.updateExports(pt, exported, export_indices);
103109}
104110
105pub fn freeDecl(self: *NvPtx, decl_index: InternPool.DeclIndex) void {
106 return self.llvm_object.freeDecl(decl_index);
107}
108
109111pub fn flush(self: *NvPtx, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
110112 return self.flushModule(arena, tid, prog_node);
111113}
src/link/Plan9.zig+76-110
......@@ -60,7 +60,7 @@ fn_nav_table: std.AutoArrayHashMapUnmanaged(
6060data_nav_table: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u8) = .empty,
6161/// When `updateExports` is called, we store the export indices here, to be used
6262/// during flush.
63nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u32) = .empty,
63nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []Zcu.Export.Index) = .empty,
6464
6565lazy_syms: LazySymbolTable = .{},
6666
......@@ -345,6 +345,7 @@ fn putFn(self: *Plan9, nav_index: InternPool.Nav.Index, out: FnNavOutput) !void
345345 try a.writer().writeInt(u16, 1, .big);
346346
347347 // getting the full file path
348 // TODO don't call getcwd here, that is inappropriate
348349 var buf: [std.fs.max_path_bytes]u8 = undefined;
349350 const full_path = try std.fs.path.join(arena, &.{
350351 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
385386 }
386387}
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 {
389396 if (build_options.skip_non_native and builtin.object_format != .plan9) {
390397 @panic("Attempted to compile for object format that was disabled by build configuration");
391398 }
......@@ -397,8 +404,8 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,
397404
398405 const atom_idx = try self.seeNav(pt, func.owner_nav);
399406
400 var code_buffer = std.ArrayList(u8).init(gpa);
401 defer code_buffer.deinit();
407 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
408 defer code_buffer.deinit(gpa);
402409 var dbg_info_output: DebugInfoOutput = .{
403410 .dbg_line = std.ArrayList(u8).init(gpa),
404411 .start_line = null,
......@@ -409,7 +416,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,
409416 };
410417 defer dbg_info_output.dbg_line.deinit();
411418
412 const res = try codegen.generateFunction(
419 try codegen.generateFunction(
413420 &self.base,
414421 pt,
415422 zcu.navSrcLoc(func.owner_nav),
......@@ -419,10 +426,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,
419426 &code_buffer,
420427 .{ .plan9 = &dbg_info_output },
421428 );
422 const code = switch (res) {
423 .ok => try code_buffer.toOwnedSlice(),
424 .fail => |em| return zcu.failed_codegen.put(gpa, func.owner_nav, em),
425 };
429 const code = try code_buffer.toOwnedSlice(gpa);
426430 self.getAtomPtr(atom_idx).code = .{
427431 .code_ptr = null,
428432 .other = .{ .nav_index = func.owner_nav },
......@@ -433,11 +437,13 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,
433437 .start_line = dbg_info_output.start_line.?,
434438 .end_line = dbg_info_output.end_line,
435439 };
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)});
437443 return self.updateFinish(pt, func.owner_nav);
438444}
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 {
441447 const zcu = pt.zcu;
442448 const gpa = zcu.gpa;
443449 const ip = &zcu.intern_pool;
......@@ -456,10 +462,10 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
456462 if (nav_init.typeOf(zcu).hasRuntimeBits(zcu)) {
457463 const atom_idx = try self.seeNav(pt, nav_index);
458464
459 var code_buffer = std.ArrayList(u8).init(gpa);
460 defer code_buffer.deinit();
465 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
466 defer code_buffer.deinit(gpa);
461467 // 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(
463469 &self.base,
464470 pt,
465471 zcu.navSrcLoc(nav_index),
......@@ -467,10 +473,7 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
467473 &code_buffer,
468474 .{ .atom_index = @intCast(atom_idx) },
469475 );
470 const code = switch (res) {
471 .ok => code_buffer.items,
472 .fail => |em| return zcu.failed_codegen.put(gpa, nav_index, em),
473 };
476 const code = code_buffer.items;
474477 try self.data_nav_table.ensureUnusedCapacity(gpa, 1);
475478 const duped_code = try gpa.dupe(u8, code);
476479 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 {
529532 }
530533}
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 {
533541 const comp = self.base.comp;
542 const diags = &comp.link_diags;
534543 const use_lld = build_options.have_llvm and comp.config.use_lld;
535544 assert(!use_lld);
536545
537546 switch (link.File.effectiveOutputMode(use_lld, comp.config.output_mode)) {
538547 .Exe => {},
539 // plan9 object files are totally different
540 .Obj => return error.TODOImplementPlan9Objs,
541 .Lib => return error.TODOImplementWritingLibFiles,
548 .Obj => return diags.fail("writing plan9 object files unimplemented", .{}),
549 .Lib => return diags.fail("writing plan9 lib files unimplemented", .{}),
542550 }
543551 return self.flushModule(arena, tid, prog_node);
544552}
......@@ -583,7 +591,13 @@ fn atomCount(self: *Plan9) usize {
583591 return data_nav_count + fn_nav_count + lazy_atom_count + extern_atom_count + uav_atom_count;
584592}
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 {
587601 if (build_options.skip_non_native and builtin.object_format != .plan9) {
588602 @panic("Attempted to compile for object format that was disabled by build configuration");
589603 }
......@@ -594,6 +608,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
594608 _ = arena; // Has the same lifetime as the call to Compilation.update.
595609
596610 const comp = self.base.comp;
611 const diags = &comp.link_diags;
597612 const gpa = comp.gpa;
598613 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
605620 defer assert(self.hdr.entry != 0x0);
606621
607622 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", .{}),
609624 tid,
610625 );
611626 defer pt.deactivate();
......@@ -614,22 +629,16 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
614629 if (self.lazy_syms.getPtr(.none)) |metadata| {
615630 // Most lazy symbols can be updated on first use, but
616631 // 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(
618633 pt,
619634 .{ .kind = .code, .ty = .anyerror_type },
620635 metadata.text_atom,
621 ) catch |err| return switch (err) {
622 error.CodegenFail => error.FlushFailure,
623 else => |e| e,
624 };
625 if (metadata.rodata_state != .unused) self.updateLazySymbolAtom(
636 );
637 if (metadata.rodata_state != .unused) try self.updateLazySymbolAtom(
626638 pt,
627639 .{ .kind = .const_data, .ty = .anyerror_type },
628640 metadata.rodata_atom,
629 ) catch |err| return switch (err) {
630 error.CodegenFail => error.FlushFailure,
631 else => |e| e,
632 };
641 );
633642 }
634643 for (self.lazy_syms.values()) |*metadata| {
635644 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
902911 }
903912 }
904913 }
905 // write it all!
906 try file.pwritevAll(iovecs, 0);
914 file.pwritevAll(iovecs, 0) catch |err| return diags.fail("failed to write file: {s}", .{@errorName(err)});
907915}
908916fn addNavExports(
909917 self: *Plan9,
910 mod: *Zcu,
918 zcu: *Zcu,
911919 nav_index: InternPool.Nav.Index,
912 export_indices: []const u32,
920 export_indices: []const Zcu.Export.Index,
913921) !void {
914922 const gpa = self.base.comp.gpa;
915923 const metadata = self.navs.getPtr(nav_index).?;
916924 const atom = self.getAtom(metadata.index);
917925
918926 for (export_indices) |export_idx| {
919 const exp = mod.all_exports.items[export_idx];
920 const exp_name = exp.opts.name.toSlice(&mod.intern_pool);
927 const exp = export_idx.ptr(zcu);
928 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
921929 // plan9 does not support custom sections
922930 if (exp.opts.section.unwrap()) |section_name| {
923 if (!section_name.eqlSlice(".text", &mod.intern_pool) and
924 !section_name.eqlSlice(".data", &mod.intern_pool))
931 if (!section_name.eqlSlice(".text", &zcu.intern_pool) and
932 !section_name.eqlSlice(".data", &zcu.intern_pool))
925933 {
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(
927935 gpa,
928 mod.navSrcLoc(nav_index),
936 zcu.navSrcLoc(nav_index),
929937 "plan9 does not support extra sections",
930938 .{},
931939 ));
......@@ -947,50 +955,6 @@ fn addNavExports(
947955 }
948956}
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}
994958fn createAtom(self: *Plan9) !Atom.Index {
995959 const gpa = self.base.comp.gpa;
996960 const index = @as(Atom.Index, @intCast(self.atoms.items.len));
......@@ -1043,7 +1007,7 @@ pub fn updateExports(
10431007 self: *Plan9,
10441008 pt: Zcu.PerThread,
10451009 exported: Zcu.Exported,
1046 export_indices: []const u32,
1010 export_indices: []const Zcu.Export.Index,
10471011) !void {
10481012 const gpa = self.base.comp.gpa;
10491013 switch (exported) {
......@@ -1054,7 +1018,7 @@ pub fn updateExports(
10541018 gpa.free(kv.value);
10551019 }
10561020 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);
10581022 self.nav_exports.putAssumeCapacityNoClobber(nav, duped_indices);
10591023 },
10601024 }
......@@ -1085,12 +1049,19 @@ pub fn getOrCreateAtomForLazySymbol(self: *Plan9, pt: Zcu.PerThread, lazy_sym: F
10851049 return atom;
10861050}
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 {
10891058 const gpa = pt.zcu.gpa;
1059 const comp = self.base.comp;
1060 const diags = &comp.link_diags;
10901061
10911062 var required_alignment: InternPool.Alignment = .none;
1092 var code_buffer = std.ArrayList(u8).init(gpa);
1093 defer code_buffer.deinit();
1063 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1064 defer code_buffer.deinit(gpa);
10941065
10951066 // create the symbol for the name
10961067 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{
......@@ -1107,7 +1078,7 @@ fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, a
11071078
11081079 // generate the code
11091080 const src = Type.fromInterned(sym.ty).srcLocOrNull(pt.zcu) orelse Zcu.LazySrcLoc.unneeded;
1110 const res = try codegen.generateLazySymbol(
1081 codegen.generateLazySymbol(
11111082 &self.base,
11121083 pt,
11131084 src,
......@@ -1116,14 +1087,12 @@ fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, a
11161087 &code_buffer,
11171088 .none,
11181089 .{ .atom_index = @intCast(atom_index) },
1119 );
1120 const code = switch (res) {
1121 .ok => code_buffer.items,
1122 .fail => |em| {
1123 log.err("{s}", .{em.msg});
1124 return error.CodegenFail;
1125 },
1090 ) catch |err| switch (err) {
1091 error.OutOfMemory => return error.OutOfMemory,
1092 error.CodegenFail => return error.LinkFailure,
1093 error.Overflow => return diags.fail("codegen failure: encountered number too big for compiler", .{}),
11261094 };
1095 const code = code_buffer.items;
11271096 // duped_code is freed when the atom is freed
11281097 const duped_code = try gpa.dupe(u8, code);
11291098 errdefer gpa.free(duped_code);
......@@ -1283,7 +1252,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
12831252 try self.writeSym(writer, sym);
12841253 if (self.nav_exports.get(nav_index)) |export_indices| {
12851254 for (export_indices) |export_idx| {
1286 const exp = zcu.all_exports.items[export_idx];
1255 const exp = export_idx.ptr(zcu);
12871256 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {
12881257 try self.writeSym(writer, self.syms.items[exp_i]);
12891258 }
......@@ -1322,7 +1291,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
13221291 try self.writeSym(writer, sym);
13231292 if (self.nav_exports.get(nav_index)) |export_indices| {
13241293 for (export_indices) |export_idx| {
1325 const exp = zcu.all_exports.items[export_idx];
1294 const exp = export_idx.ptr(zcu);
13261295 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {
13271296 const s = self.syms.items[exp_i];
13281297 if (mem.eql(u8, s.name, "_start"))
......@@ -1432,19 +1401,16 @@ pub fn lowerUav(
14321401 const got_index = self.allocateGotIndex();
14331402 gop.value_ptr.* = index;
14341403 // we need to free name latex
1435 var code_buffer = std.ArrayList(u8).init(gpa);
1436 const res = try codegen.generateSymbol(&self.base, pt, src_loc, val, &code_buffer, .{ .atom_index = index });
1437 const code = switch (res) {
1438 .ok => code_buffer.items,
1439 .fail => |em| return .{ .fail = em },
1440 };
1404 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1405 defer code_buffer.deinit(gpa);
1406 try codegen.generateSymbol(&self.base, pt, src_loc, val, &code_buffer, .{ .atom_index = index });
14411407 const atom_ptr = self.getAtomPtr(index);
14421408 atom_ptr.* = .{
14431409 .type = .d,
14441410 .offset = undefined,
14451411 .sym_index = null,
14461412 .got_index = got_index,
1447 .code = Atom.CodePtr.fromSlice(code),
1413 .code = Atom.CodePtr.fromSlice(try code_buffer.toOwnedSlice(gpa)),
14481414 };
14491415 _ = try atom_ptr.getOrCreateSymbolTableEntry(self);
14501416 self.syms.items[atom_ptr.sym_index.?] = .{
src/link/SpirV.zig+26-18
......@@ -122,7 +122,13 @@ pub fn deinit(self: *SpirV) void {
122122 self.object.deinit();
123123}
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 {
126132 if (build_options.skip_non_native) {
127133 @panic("Attempted to compile for architecture that was disabled by build configuration");
128134 }
......@@ -134,7 +140,7 @@ pub fn updateFunc(self: *SpirV, pt: Zcu.PerThread, func_index: InternPool.Index,
134140 try self.object.updateFunc(pt, func_index, air, liveness);
135141}
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 {
138144 if (build_options.skip_non_native) {
139145 @panic("Attempted to compile for architecture that was disabled by build configuration");
140146 }
......@@ -149,7 +155,7 @@ pub fn updateExports(
149155 self: *SpirV,
150156 pt: Zcu.PerThread,
151157 exported: Zcu.Exported,
152 export_indices: []const u32,
158 export_indices: []const Zcu.Export.Index,
153159) !void {
154160 const zcu = pt.zcu;
155161 const ip = &zcu.intern_pool;
......@@ -184,7 +190,7 @@ pub fn updateExports(
184190 };
185191
186192 for (export_indices) |export_idx| {
187 const exp = zcu.all_exports.items[export_idx];
193 const exp = export_idx.ptr(zcu);
188194 try self.object.spv.declareEntryPoint(
189195 spv_decl_index,
190196 exp.opts.name.toSlice(ip),
......@@ -196,16 +202,21 @@ pub fn updateExports(
196202 // TODO: Export regular functions, variables, etc using Linkage attributes.
197203}
198204
199pub fn freeDecl(self: *SpirV, decl_index: InternPool.DeclIndex) void {
200 _ = self;
201 _ = decl_index;
202}
203
204205pub fn flush(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
205206 return self.flushModule(arena, tid, prog_node);
206207}
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
209220 if (build_options.skip_non_native) {
210221 @panic("Attempted to compile for architecture that was disabled by build configuration");
211222 }
......@@ -216,12 +227,11 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
216227 const sub_prog_node = prog_node.start("Flush Module", 0);
217228 defer sub_prog_node.end();
218229
219 const spv = &self.object.spv;
220
221230 const comp = self.base.comp;
231 const spv = &self.object.spv;
232 const diags = &comp.link_diags;
222233 const gpa = comp.gpa;
223234 const target = comp.getTarget();
224 _ = tid;
225235
226236 try writeCapabilities(spv, target);
227237 try writeMemoryModel(spv, target);
......@@ -264,13 +274,11 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
264274
265275 const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) {
266276 error.OutOfMemory => return error.OutOfMemory,
267 else => |other| {
268 log.err("error while linking: {s}", .{@errorName(other)});
269 return error.FlushFailure;
270 },
277 else => |other| return diags.fail("error while linking: {s}", .{@errorName(other)}),
271278 };
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)});
274282}
275283
276284fn linkModule(self: *SpirV, a: Allocator, module: []Word, progress: std.Progress.Node) ![]Word {
src/link/Wasm.zig+3698-3751
......@@ -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
112const 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
417const builtin = @import("builtin");
518const native_endian = builtin.cpu.arch.endian();
619
20const build_options = @import("build_options");
21
722const std = @import("std");
823const Allocator = std.mem.Allocator;
924const Cache = std.Build.Cache;
1025const Path = Cache.Path;
1126const assert = std.debug.assert;
1227const fs = std.fs;
13const gc_log = std.log.scoped(.gc);
1428const leb = std.leb;
1529const log = std.log.scoped(.link);
1630const mem = std.mem;
1731
1832const Air = @import("../Air.zig");
19const Archive = @import("Wasm/Archive.zig");
33const Mir = @import("../arch/wasm/Mir.zig");
2034const CodeGen = @import("../arch/wasm/CodeGen.zig");
35const abi = @import("../arch/wasm/abi.zig");
2136const Compilation = @import("../Compilation.zig");
2237const Dwarf = @import("Dwarf.zig");
2338const InternPool = @import("../InternPool.zig");
2439const Liveness = @import("../Liveness.zig");
2540const 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");
3041const Zcu = @import("../Zcu.zig");
31const ZigObject = @import("Wasm/ZigObject.zig");
3242const codegen = @import("../codegen.zig");
3343const dev = @import("../dev.zig");
3444const link = @import("../link.zig");
3545const lldMain = @import("../main.zig").lldMain;
3646const trace = @import("../tracy.zig").trace;
3747const wasi_libc = @import("../wasi_libc.zig");
48const Value = @import("../Value.zig");
3849
3950base: link.File,
4051/// Null-terminated strings, indexes have type String and string_table provides
4152/// 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.
4260string_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 = .{},
4365/// Omitted when serializing linker state.
4466string_table: String.Table,
4567/// Symbol name of the entry function to export
......@@ -62,2525 +84,3230 @@ export_table: bool,
6284name: []const u8,
6385/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
6486llvm_object: ?LlvmObject.Ptr = null,
65zig_object: ?*ZigObject,
6687/// List of relocatable files to be linked into the final binary.
6788objects: 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
68162/// 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 Zig
70/// to support existing code.
71/// TODO: Allow setting this through a flag?
72host_name: String,
73/// List of symbols generated by the linker.
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,
163/// LLVM uses "env" by default when none is given.
164/// This value is passed to object files since wasm tooling conventions provides
165/// no way to specify the module name in the symbol table.
166object_host_name: OptionalString,
167
120168/// Memory section
121169memories: std.wasm.Memory = .{ .limits = .{
122170 .min = 0,
123 .max = undefined,
124 .flags = 0,
171 .max = 0,
172 .flags = .{ .has_max = false, .is_shared = false },
125173} },
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
167175/// `--verbose-link` output.
168176/// Initialized on creation, appended to as inputs are added, printed during `flush`.
169177/// String data is allocated into Compilation arena.
170178dump_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,
175180preloaded_strings: PreloadedStrings,
176181
177/// Type reflection is used on the field names to autopopulate each field
178/// during initialization.
179const PreloadedStrings = struct {
180 __heap_base: String,
181 __heap_end: String,
182 __indirect_function_table: String,
183 __linear_memory: String,
184 __stack_pointer: String,
185 __tls_align: String,
186 __tls_base: String,
187 __tls_size: String,
188 __wasm_apply_global_tls_relocs: String,
189 __wasm_call_ctors: String,
190 __wasm_init_memory: String,
191 __wasm_init_memory_flag: String,
192 __wasm_init_tls: String,
193 __zig_err_name_table: String,
194 __zig_err_names: String,
195 __zig_errors_len: String,
196 _initialize: String,
197 _start: String,
198 memory: String,
182/// This field is used when emitting an object; `navs_exe` used otherwise.
183/// Does not include externs since that data lives elsewhere.
184navs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataObj) = .empty,
185/// This field is unused when emitting an object; `navs_obj` used otherwise.
186/// Does not include externs since that data lives elsewhere.
187navs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataExe) = .empty,
188/// Tracks all InternPool values referenced by codegen. Needed for outputting
189/// the data segment. This one does not track ref count because object files
190/// require using max LEB encoding for these references anyway.
191uavs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataObj) = .empty,
192/// Tracks ref count to optimize LEB encodings for UAV references.
193uavs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataExe) = .empty,
194/// Sparse table of uavs that need to be emitted with greater alignment than
195/// the default for the type.
196overaligned_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment) = .empty,
197/// When the key is an enum type, this represents a `@tagName` function.
198zcu_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuFunc) = .empty,
199nav_exports: std.AutoArrayHashMapUnmanaged(NavExport, Zcu.Export.Index) = .empty,
200uav_exports: std.AutoArrayHashMapUnmanaged(UavExport, Zcu.Export.Index) = .empty,
201imports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
202
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,
199310};
200311
201/// Type reflection is used on the field names to autopopulate each inner `name` field.
202const CustomSections = struct {
203 @".debug_info": CustomSection,
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,
312/// Index into `Wasm.zcu_indirect_function_set`.
313pub const ZcuIndirectFunctionSetIndex = enum(u32) {
314 _,
211315};
212316
213const CustomSection = struct {
214 name: String,
215 index: Segment.OptionalIndex,
317pub const UavFixup = extern struct {
318 uavs_exe_index: UavsExeIndex,
319 /// Index into `string_bytes`.
320 offset: u32,
321 addend: u32,
216322};
217323
218/// Index into string_bytes
219pub const String = enum(u32) {
220 _,
324pub const NavFixup = extern struct {
325 navs_exe_index: NavsExeIndex,
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 {
225 bytes: []const u8,
337/// Index into `objects`.
338pub const ObjectIndex = enum(u32) {
339 _,
226340
227 pub fn eql(_: @This(), a: String, b: String) bool {
228 return a == b;
229 }
341 pub fn ptr(index: ObjectIndex, wasm: *const Wasm) *Object {
342 return &wasm.objects.items[@intFromEnum(index)];
343 }
344};
230345
231 pub fn hash(ctx: @This(), key: String) u64 {
232 return std.hash_map.hashString(mem.sliceTo(ctx.bytes[@intFromEnum(key)..], 0));
233 }
234 };
346/// Index into `Wasm.functions`.
347pub const FunctionIndex = enum(u32) {
348 _,
235349
236 const TableIndexAdapter = struct {
237 bytes: []const u8,
350 pub fn ptr(index: FunctionIndex, wasm: *const Wasm) *FunctionImport.Resolution {
351 return &wasm.functions.keys()[@intFromEnum(index)];
352 }
238353
239 pub fn eql(ctx: @This(), a: []const u8, b: String) bool {
240 return mem.eql(u8, a, mem.sliceTo(ctx.bytes[@intFromEnum(b)..], 0));
241 }
354 pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) ?FunctionIndex {
355 return fromResolution(wasm, .fromIpNav(wasm, nav_index));
356 }
242357
243 pub fn hash(_: @This(), adapted_key: []const u8) u64 {
244 assert(mem.indexOfScalar(u8, adapted_key, 0) == null);
245 return std.hash_map.hashString(adapted_key);
358 pub fn fromTagNameType(wasm: *const Wasm, tag_type: InternPool.Index) ?FunctionIndex {
359 const zcu_func: ZcuFunc.Index = @enumFromInt(wasm.zcu_funcs.getIndex(tag_type) orelse return null);
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);
246366 }
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 {
250 const result: OptionalString = @enumFromInt(@intFromEnum(i));
251 assert(result != .none);
252 return result;
372 pub fn fromResolution(wasm: *const Wasm, resolution: FunctionImport.Resolution) ?FunctionIndex {
373 const i = wasm.functions.getIndex(resolution) orelse return null;
374 return @enumFromInt(i);
253375 }
254376};
255377
256pub const OptionalString = enum(u32) {
257 none = std.math.maxInt(u32),
378pub const GlobalExport = extern struct {
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) {
258389 _,
259390
260 pub fn unwrap(i: OptionalString) ?String {
261 if (i == .none) return null;
262 return @enumFromInt(@intFromEnum(i));
391 pub fn fromResolution(wasm: *const Wasm, resolution: FunctionImport.Resolution) ?OutputFunctionIndex {
392 return fromFunctionIndex(wasm, FunctionIndex.fromResolution(wasm, resolution) orelse return null);
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).?);
263440 }
264441};
265442
266/// Index into objects array or the zig object.
267pub const ObjectId = enum(u16) {
268 zig_object = std.math.maxInt(u16) - 1,
443/// Index into `Wasm.globals`.
444pub const GlobalIndex = enum(u32) {
269445 _,
270446
271 pub fn toOptional(i: ObjectId) OptionalObjectId {
272 const result: OptionalObjectId = @enumFromInt(@intFromEnum(i));
273 assert(result != .none);
274 return result;
447 /// This is only accurate when not emitting an object and there is a Zcu.
448 pub const stack_pointer: GlobalIndex = @enumFromInt(0);
449
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).?);
275482 }
276483};
277484
278/// Optional index into objects array or the zig object.
279pub const OptionalObjectId = enum(u16) {
280 zig_object = std.math.maxInt(u16) - 1,
281 none = std.math.maxInt(u16),
485/// Index into `tables`.
486pub const TableIndex = enum(u32) {
282487 _,
283488
284 pub fn unwrap(i: OptionalObjectId) ?ObjectId {
285 if (i == .none) return null;
286 return @enumFromInt(@intFromEnum(i));
489 pub fn ptr(index: TableIndex, f: *const Flush) *Wasm.TableImport.Resolution {
490 return &f.tables.items[@intFromEnum(index)];
287491 }
288};
289492
290/// None of this data is serialized since it can be re-loaded from disk, or if
291/// it has been changed, the data must be discarded.
292const LazyArchive = struct {
293 path: Path,
294 file_contents: []const u8,
295 archive: Archive,
493 pub fn fromObjectTable(wasm: *const Wasm, i: ObjectTableIndex) TableIndex {
494 return @enumFromInt(wasm.tables.getIndex(.fromObjectTable(i)).?);
495 }
296496
297 fn deinit(la: *LazyArchive, gpa: Allocator) void {
298 la.archive.deinit(gpa);
299 gpa.free(la.path.sub_path);
300 gpa.free(la.file_contents);
301 la.* = undefined;
497 pub fn fromSymbolName(wasm: *const Wasm, name: String) TableIndex {
498 const import = wasm.object_table_imports.getPtr(name).?;
499 return @enumFromInt(wasm.tables.getIndex(import.resolution).?);
302500 }
303501};
304502
305pub const Segment = struct {
306 alignment: Alignment,
307 size: u32,
308 offset: u32,
309 flags: u32,
503/// The first N indexes correspond to input objects (`objects`) array.
504/// After that, the indexes correspond to the `source_locations` array,
505/// representing a location in a Zig source file that can be pinpointed
506/// precisely via AST node and token.
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) {
312515 _,
313
314 pub fn toOptional(i: Index) OptionalIndex {
315 const result: OptionalIndex = @enumFromInt(@intFromEnum(i));
316 assert(result != .none);
317 return result;
318 }
319516 };
320517
321 const OptionalIndex = enum(u32) {
322 none = std.math.maxInt(u32),
323 _,
324
325 pub fn unwrap(i: OptionalIndex) ?Index {
326 if (i == .none) return null;
327 return @enumFromInt(@intFromEnum(i));
328 }
518 pub const Unpacked = union(enum) {
519 none,
520 zig_object_nofile,
521 object_index: ObjectIndex,
522 source_location_index: Index,
329523 };
330524
331 pub const Flag = enum(u32) {
332 WASM_DATA_SEGMENT_IS_PASSIVE = 0x01,
333 WASM_DATA_SEGMENT_HAS_MEMINDEX = 0x02,
334 };
525 pub fn pack(unpacked: Unpacked, wasm: *const Wasm) SourceLocation {
526 _ = wasm;
527 return switch (unpacked) {
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 {
337 return segment.flags & @intFromEnum(Flag.WASM_DATA_SEGMENT_IS_PASSIVE) != 0;
549 pub fn fromObject(object_index: ObjectIndex, wasm: *const Wasm) SourceLocation {
550 return pack(.{ .object_index = object_index }, wasm);
338551 }
339552
340 /// For a given segment, determines if it needs passive initialization
341 fn needsPassiveInitialization(segment: Segment, import_mem: bool, name: []const u8) bool {
342 if (import_mem and !std.mem.eql(u8, name, ".bss")) {
343 return true;
553 pub fn addError(sl: SourceLocation, wasm: *Wasm, comptime f: []const u8, args: anytype) void {
554 const diags = &wasm.base.comp.link_diags;
555 switch (sl.unpack(wasm)) {
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"),
344560 }
345 return segment.isPassive();
346561 }
347};
348562
349pub const SymbolLoc = struct {
350 /// The index of the symbol within the specified file
351 index: Symbol.Index,
352 /// The index of the object file where the symbol resides.
353 file: OptionalObjectId,
354};
563 pub fn addNote(
564 sl: SourceLocation,
565 err: *link.Diags.ErrorWithNotes,
566 comptime f: []const u8,
567 args: anytype,
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 binary
357pub fn symbolLocSymbol(wasm: *const Wasm, loc: SymbolLoc) *Symbol {
358 if (wasm.discarded.get(loc)) |new_loc| {
359 return symbolLocSymbol(wasm, new_loc);
574 pub fn fail(sl: SourceLocation, diags: *link.Diags, comptime format: []const u8, args: anytype) error{LinkFailure} {
575 return diags.failSourceLocation(.{ .wasm = sl }, format, args);
360576 }
361 return switch (loc.file) {
362 .none => &wasm.synthetic_symbols.items[@intFromEnum(loc.index)],
363 .zig_object => wasm.zig_object.?.symbol(loc.index),
364 _ => &wasm.objects.items[@intFromEnum(loc.file)].symtable[@intFromEnum(loc.index)],
577
578 pub fn string(
579 sl: SourceLocation,
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,
365662 };
366}
367663
368/// From a given location, returns the name of the symbol.
369pub fn symbolLocName(wasm: *const Wasm, loc: SymbolLoc) [:0]const u8 {
370 return wasm.stringSlice(wasm.symbolLocSymbol(loc).name);
371}
664 pub fn initZigSpecific(flags: *SymbolFlags, must_link: bool, no_strip: bool) void {
665 flags.no_strip = no_strip;
666 flags.alive = false;
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.
374/// e.g. when a symbol was resolved and replaced by the symbol
375/// in a different file, this will return said location.
376/// If the symbol wasn't replaced by another, this will return
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);
674 pub fn isIncluded(flags: SymbolFlags, is_dynamic: bool) bool {
675 return flags.exported or
676 (is_dynamic and !flags.visibility_hidden) or
677 (flags.no_strip and flags.must_link);
381678 }
382 return loc;
383}
384679
385// Contains the location of the function symbol, as well as
386/// the priority itself of the initialization function.
387pub const InitFuncLoc = struct {
388 /// object file index in the list of objects.
389 /// Unlike `SymbolLoc` this cannot be `null` as we never define
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,
680 pub fn isExported(flags: SymbolFlags, is_dynamic: bool) bool {
681 if (flags.undefined or flags.binding == .local) return false;
682 if (is_dynamic and !flags.visibility_hidden) return true;
683 return flags.exported;
684 }
396685
397 /// From a given `InitFuncLoc` returns the corresponding function symbol
398 fn getSymbol(loc: InitFuncLoc, wasm: *const Wasm) *Symbol {
399 return wasm.symbolLocSymbol(getSymbolLoc(loc));
686 /// Returns the name as how it will be output into the final object
687 /// file or binary. When `merge` is true, this will return the
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);
400704 }
705};
401706
402 /// Turns the given `InitFuncLoc` into a `SymbolLoc`
403 fn getSymbolLoc(loc: InitFuncLoc) SymbolLoc {
707pub const GlobalType4 = packed struct(u4) {
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 {
404714 return .{
405 .file = loc.file.toOptional(),
406 .index = loc.index,
715 .valtype = gt.valtype.to(),
716 .mutable = gt.mutable,
407717 };
408718 }
719};
409720
410 /// Returns true when `lhs` has a higher priority (e.i. value closer to 0) than `rhs`.
411 fn lessThan(ctx: void, lhs: InitFuncLoc, rhs: InitFuncLoc) bool {
412 _ = ctx;
413 return lhs.priority < rhs.priority;
721pub const Valtype3 = enum(u3) {
722 i32,
723 i64,
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 };
414746 }
415747};
416748
417pub fn open(
418 arena: Allocator,
419 comp: *Compilation,
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}
749/// Index into `Wasm.navs_obj`.
750pub const NavsObjIndex = enum(u32) {
751 _,
427752
428pub fn createEmpty(
429 arena: Allocator,
430 comp: *Compilation,
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);
753 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index {
754 return &wasm.navs_obj.keys()[@intFromEnum(i)];
755 }
437756
438 const use_lld = build_options.have_llvm and comp.config.use_lld;
439 const use_llvm = comp.config.use_llvm;
440 const output_mode = comp.config.output_mode;
441 const shared_memory = comp.config.shared_memory;
442 const wasi_exec_model = comp.config.wasi_exec_model;
757 pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataObj {
758 return &wasm.navs_obj.values()[@intFromEnum(i)];
759 }
443760
444 // If using LLD to link, this code should produce an object file so that it
445 // can be passed to LLD.
446 // If using LLVM to generate the object file for the zig compilation unit,
447 // we need a place to put the object file so that it can be subsequently
448 // handled.
449 const zcu_object_sub_path = if (!use_lld and !use_llvm)
450 null
451 else
452 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
761 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {
762 const zcu = wasm.base.comp.zcu.?;
763 const ip = &zcu.intern_pool;
764 const nav = ip.getNav(i.key(wasm).*);
765 return nav.fqn.toSlice(ip);
766 }
767};
453768
454 const wasm = try arena.create(Wasm);
455 wasm.* = .{
456 .base = .{
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,
769/// Index into `Wasm.navs_exe`.
770pub const NavsExeIndex = enum(u32) {
771 _,
482772
483 .entry_name = undefined,
484 .zig_object = null,
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);
773 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index {
774 return &wasm.navs_exe.keys()[@intFromEnum(i)];
492775 }
493 errdefer wasm.base.destroy();
494776
495 wasm.host_name = try wasm.internString("env");
496
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 };
777 pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataExe {
778 return &wasm.navs_exe.values()[@intFromEnum(i)];
502779 }
503780
504 inline for (@typeInfo(PreloadedStrings).@"struct".fields) |field| {
505 @field(wasm.preloaded_strings, field.name) = try wasm.internString(field.name);
781 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {
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);
506786 }
787};
507788
508 wasm.entry_name = switch (options.entry) {
509 .disabled => .none,
510 .default => if (output_mode != .Exe) .none else defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(),
511 .enabled => defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(),
512 .named => |name| (try wasm.internString(name)).toOptional(),
513 };
789/// Index into `Wasm.uavs_obj`.
790pub const UavsObjIndex = enum(u32) {
791 _,
514792
515 if (use_lld and (use_llvm or !comp.config.have_zcu)) {
516 // LLVM emits the object file (if any); LLD links it into the final product.
517 return wasm;
793 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index {
794 return &wasm.uavs_obj.keys()[@intFromEnum(i)];
518795 }
519796
520 // What path should this Wasm linker code output to?
521 // If using LLD to link, this code should produce an object file so that it
522 // can be passed to LLD.
523 const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path;
797 pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataObj {
798 return &wasm.uavs_obj.values()[@intFromEnum(i)];
799 }
800};
524801
525 wasm.base.file = try emit.root_dir.handle.createFile(sub_path, .{
526 .truncate = true,
527 .read = true,
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;
802/// Index into `Wasm.uavs_exe`.
803pub const UavsExeIndex = enum(u32) {
804 _,
537805
538 // create stack pointer symbol
539 {
540 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__stack_pointer, .global);
541 const symbol = wasm.symbolLocSymbol(loc);
542 // For object files we will import the stack pointer symbol
543 if (output_mode == .Obj) {
544 symbol.setUndefined(true);
545 symbol.index = @intCast(wasm.imported_globals_count);
546 wasm.imported_globals_count += 1;
547 try wasm.imports.putNoClobber(gpa, loc, .{
548 .module_name = wasm.host_name,
549 .name = symbol.name,
550 .kind = .{ .global = .{ .valtype = .i32, .mutable = true } },
551 });
552 } else {
553 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
554 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
555 const global = try wasm.wasm_globals.addOne(gpa);
556 global.* = .{
557 .global_type = .{
558 .valtype = .i32,
559 .mutable = true,
560 },
561 .init = .{ .i32_const = 0 },
562 };
563 }
806 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index {
807 return &wasm.uavs_exe.keys()[@intFromEnum(i)];
808 }
809
810 pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataExe {
811 return &wasm.uavs_exe.values()[@intFromEnum(i)];
812 }
813};
814
815/// Used when emitting a relocatable object.
816pub const ZcuDataObj = extern struct {
817 code: DataPayload,
818 relocs: OutReloc.Slice,
819};
820
821/// Used when not emitting a relocatable object.
822pub const ZcuDataExe = extern struct {
823 code: DataPayload,
824 /// Tracks how many references there are for the purposes of sorting data segments.
825 count: u32,
826};
827
828/// An abstraction for calling `lowerZcuData` repeatedly until all data entries
829/// are populated.
830const ZcuDataStarts = struct {
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);
564837 }
565838
566 // create indirect function pointer symbol
567 {
568 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__indirect_function_table, .table);
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,
839 fn initObj(wasm: *const Wasm) ZcuDataStarts {
840 return .{
841 .uavs_i = @intCast(wasm.uavs_obj.entries.len),
573842 };
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 }
592843 }
593844
594 // create __wasm_call_ctors
595 {
596 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_call_ctors, .function);
597 const symbol = wasm.symbolLocSymbol(loc);
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`.
845 fn initExe(wasm: *const Wasm) ZcuDataStarts {
846 return .{
847 .uavs_i = @intCast(wasm.uavs_exe.entries.len),
848 };
602849 }
603850
604 // shared-memory symbols for TLS support
605 if (shared_memory) {
606 {
607 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__tls_base, .global);
608 const symbol = wasm.symbolLocSymbol(loc);
609 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
610 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
611 symbol.mark();
612 try wasm.wasm_globals.append(gpa, .{
613 .global_type = .{ .valtype = .i32, .mutable = true },
614 .init = .{ .i32_const = undefined },
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);
851 fn finish(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void {
852 const comp = wasm.base.comp;
853 const is_obj = comp.config.output_mode == .Obj;
854 return if (is_obj) finishObj(zds, wasm, pt) else finishExe(zds, wasm, pt);
855 }
856
857 fn finishObj(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void {
858 var uavs_i = zds.uavs_i;
859 while (uavs_i < wasm.uavs_obj.entries.len) : (uavs_i += 1) {
860 // Call to `lowerZcuData` here possibly creates more entries in these tables.
861 wasm.uavs_obj.values()[uavs_i] = try lowerZcuData(wasm, pt, wasm.uavs_obj.keys()[uavs_i]);
643862 }
644863 }
645864
646 if (comp.zcu) |zcu| {
647 if (!use_llvm) {
648 const zig_object = try arena.create(ZigObject);
649 wasm.zig_object = zig_object;
650 zig_object.* = .{
651 .path = .{
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);
865 fn finishExe(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void {
866 var uavs_i = zds.uavs_i;
867 while (uavs_i < wasm.uavs_exe.entries.len) : (uavs_i += 1) {
868 // Call to `lowerZcuData` here possibly creates more entries in these tables.
869 const zcu_data = try lowerZcuData(wasm, pt, wasm.uavs_exe.keys()[uavs_i]);
870 wasm.uavs_exe.values()[uavs_i].code = zcu_data.code;
658871 }
659872 }
873};
660874
661 return wasm;
662}
875pub const ZcuFunc = union {
876 function: CodeGen.Function,
877 tag_name: TagName,
663878
664pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 {
665 var index: u32 = 0;
666 while (index < wasm.func_types.items.len) : (index += 1) {
667 if (wasm.func_types.items[index].eql(func_type)) return index;
668 }
669 return null;
670}
879 pub const TagName = extern struct {
880 symbol_name: String,
881 type_index: FunctionType.Index,
882 /// Index into `Wasm.tag_name_offs`.
883 table_index: u32,
884 };
671885
672/// Either creates a new import, or updates one if existing.
673/// When `type_index` is non-null, we assume an external function.
674/// In all other cases, a data-symbol will be created instead.
675pub fn addOrUpdateImport(
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}
886 /// Index into `Wasm.zcu_funcs`.
887 /// Note that swapRemove is sometimes performed on `zcu_funcs`.
888 pub const Index = enum(u32) {
889 _,
690890
691/// For a given name, creates a new global synthetic symbol.
692/// Leaves index undefined and the default flags (0).
693fn createSyntheticSymbol(wasm: *Wasm, name: String, tag: Symbol.Tag) !SymbolLoc {
694 return wasm.createSyntheticSymbolOffset(name, tag);
695}
891 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index {
892 return &wasm.zcu_funcs.keys()[@intFromEnum(i)];
893 }
696894
697fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: String, tag: Symbol.Tag) !SymbolLoc {
698 const sym_index: Symbol.Index = @enumFromInt(wasm.synthetic_symbols.items.len);
699 const loc: SymbolLoc = .{ .index = sym_index, .file = .none };
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}
895 pub fn value(i: @This(), wasm: *const Wasm) *ZcuFunc {
896 return &wasm.zcu_funcs.values()[@intFromEnum(i)];
897 }
712898
713fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void {
714 const diags = &wasm.base.comp.link_diags;
715 const obj = link.openObject(path, false, false) catch |err| {
716 switch (diags.failParse(path, "failed to open object: {s}", .{@errorName(err)})) {
717 error.LinkFailure => return,
899 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {
900 const zcu = wasm.base.comp.zcu.?;
901 const ip = &zcu.intern_pool;
902 const ip_index = i.key(wasm).*;
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 }
718913 }
719 };
720 wasm.parseObject(obj) catch |err| {
721 switch (diags.failParse(path, "failed to parse object: {s}", .{@errorName(err)})) {
722 error.LinkFailure => return,
914
915 pub fn typeIndex(i: @This(), wasm: *Wasm) FunctionType.Index {
916 const comp = wasm.base.comp;
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 }
723931 }
724932 };
725}
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;
933};
733934
734 const file_contents = try gpa.alloc(u8, size);
735 defer gpa.free(file_contents);
935pub const NavExport = extern struct {
936 name: String,
937 nav_index: InternPool.Nav.Index,
938};
736939
737 const n = try obj.file.preadAll(file_contents, 0);
738 if (n != file_contents.len) return error.UnexpectedEndOfFile;
940pub const UavExport = extern struct {
941 name: String,
942 uav_index: InternPool.Index,
943};
739944
740 wasm.objects.appendAssumeCapacity(try Object.create(wasm, file_contents, obj.path, null));
741}
945pub const FunctionImport = extern struct {
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`
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);
966 const first_object_function = @intFromEnum(Resolution.__wasm_init_tls) + 1;
753967
754 return index;
755}
968 pub const Unpacked = union(enum) {
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 {
758 return wasm.managed_atoms.items[@intFromEnum(index)];
759}
978 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
979 return switch (r) {
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 {
762 return &wasm.managed_atoms.items[@intFromEnum(index)];
763}
988 const zcu_func_index = if (object_function_index < wasm.object_functions.items.len)
989 return .{ .object_function = @enumFromInt(object_function_index) }
990 else
991 object_function_index - wasm.object_functions.items.len;
764992
765fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {
766 const gpa = wasm.base.comp.gpa;
993 return .{ .zcu_func = @enumFromInt(zcu_func_index) };
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();
771 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
1010 pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) Resolution {
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);
774 var keep_file_contents = false;
775 defer if (!keep_file_contents) gpa.free(file_contents);
1016 pub fn fromZcuFunc(wasm: *const Wasm, i: ZcuFunc.Index) Resolution {
1017 return pack(wasm, .{ .zcu_func = i });
1018 }
7761019
777 const n = try obj.file.preadAll(file_contents, 0);
778 if (n != file_contents.len) return error.UnexpectedEndOfFile;
1020 pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution {
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) {
783 errdefer archive.deinit(gpa);
784 try wasm.lazy_archives.append(gpa, .{
785 .path = .{
786 .root_dir = obj.path.root_dir,
787 .sub_path = try gpa.dupe(u8, obj.path.sub_path),
788 },
789 .file_contents = file_contents,
790 .archive = archive,
791 });
792 keep_file_contents = true;
793 return;
794 }
1028 pub fn isNavOrUnresolved(r: Resolution, wasm: *const Wasm) bool {
1029 return switch (r.unpack(wasm)) {
1030 .unresolved, .zcu_func => true,
1031 else => false,
1032 };
1033 }
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 archive
799 // We loop over all symbols, and then group them by offset as the offset
800 // notates where the object file starts.
801 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
802 defer offsets.deinit();
803 for (archive.toc.values()) |symbol_offsets| {
804 for (symbol_offsets.items) |sym_offset| {
805 try offsets.put(sym_offset, {});
1048 pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 {
1049 return switch (unpack(r, wasm)) {
1050 .unresolved => unreachable,
1051 .__wasm_apply_global_tls_relocs => @tagName(Unpacked.__wasm_apply_global_tls_relocs),
1052 .__wasm_call_ctors => @tagName(Unpacked.__wasm_call_ctors),
1053 .__wasm_init_memory => @tagName(Unpacked.__wasm_init_memory),
1054 .__wasm_init_tls => @tagName(Unpacked.__wasm_init_tls),
1055 .object_function => |i| i.ptr(wasm).name.slice(wasm),
1056 .zcu_func => |i| i.name(wasm),
1057 };
8061058 }
807 }
1059 };
8081060
809 for (offsets.keys()) |file_offset| {
810 const object = try archive.parseObject(wasm, file_contents[file_offset..], obj.path);
811 try wasm.objects.append(gpa, object);
812 }
813}
1061 /// Index into `object_function_imports`.
1062 pub const Index = enum(u32) {
1063 _,
8141064
815fn requiresTLSReloc(wasm: *const Wasm) bool {
816 for (wasm.got_symbols.items) |loc| {
817 if (wasm.symbolLocSymbol(loc).isTLS()) {
818 return true;
1065 pub fn key(index: Index, wasm: *const Wasm) *String {
1066 return &wasm.object_function_imports.keys()[@intFromEnum(index)];
8191067 }
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 {
830 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.symbols.items;
831 return obj.symtable;
832}
1069 pub fn value(index: Index, wasm: *const Wasm) *FunctionImport {
1070 return &wasm.object_function_imports.values()[@intFromEnum(index)];
1071 }
8331072
834fn objectSymbol(wasm: *const Wasm, object_id: ObjectId, index: Symbol.Index) *Symbol {
835 const obj = wasm.objectById(object_id) orelse return &wasm.zig_object.?.symbols.items[@intFromEnum(index)];
836 return &obj.symtable[@intFromEnum(index)];
837}
1073 pub fn symbolName(index: Index, wasm: *const Wasm) String {
1074 return index.key(wasm).*;
1075 }
8381076
839fn objectFunction(wasm: *const Wasm, object_id: ObjectId, sym_index: Symbol.Index) std.wasm.Func {
840 const obj = wasm.objectById(object_id) orelse {
841 const zo = wasm.zig_object.?;
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}
1077 pub fn importName(index: Index, wasm: *const Wasm) String {
1078 return index.value(wasm).name;
1079 }
8481080
849fn objectImportedFunctions(wasm: *const Wasm, object_id: ObjectId) u32 {
850 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imported_functions_count;
851 return obj.imported_functions_count;
852}
1081 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
1082 return index.value(wasm).module_name;
1083 }
8531084
854fn objectGlobals(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Global {
855 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.globals.items;
856 return obj.globals;
857}
1085 pub fn functionType(index: Index, wasm: *const Wasm) FunctionType.Index {
1086 return value(index, wasm).type;
1087 }
1088 };
1089};
8581090
859fn objectFuncTypes(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Type {
860 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.func_types.items;
861 return obj.func_types;
862}
1091pub const ObjectFunction = extern struct {
1092 flags: SymbolFlags,
1093 /// `none` if this function has no symbol describing it.
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 {
865 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.segment_info.items;
866 return obj.segment_info;
867}
1102 pub const Code = DataPayload;
8681103
869/// For a given symbol index, find its corresponding import.
870/// Asserts import exists.
871fn objectImport(wasm: *const Wasm, object_id: ObjectId, symbol_index: Symbol.Index) Import {
872 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imports.get(symbol_index).?;
873 return obj.findImport(obj.symtable[@intFromEnum(symbol_index)]);
874}
1104 pub fn relocations(of: *const ObjectFunction, wasm: *const Wasm) ObjectRelocation.IterableSlice {
1105 const code_section_index = of.object_index.ptr(wasm).code_section_index.?;
1106 const relocs = wasm.object_relocations_table.get(code_section_index) orelse return .empty;
1107 return .init(relocs, of.offset, of.code.len, wasm);
1108 }
1109};
8751110
876/// Returns the object element pointer, or null if it is the ZigObject.
877fn objectById(wasm: *const Wasm, object_id: ObjectId) ?*Object {
878 if (object_id == .zig_object) return null;
879 return &wasm.objects.items[@intFromEnum(object_id)];
880}
1111pub const GlobalImport = extern struct {
1112 flags: SymbolFlags,
1113 module_name: OptionalString,
1114 /// May be different than the key which is a symbol name.
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 {
883 const gpa = wasm.base.comp.gpa;
884 const diags = &wasm.base.comp.link_diags;
885 const obj_path = objectPath(wasm, object_id);
886 log.debug("Resolving symbols in object: '{'}'", .{obj_path});
887 const symbols = objectSymbols(wasm, object_id);
888
889 for (symbols, 0..) |symbol, i| {
890 const sym_index: Symbol.Index = @enumFromInt(i);
891 const location: SymbolLoc = .{
892 .file = object_id.toOptional(),
893 .index = sym_index,
1133 const first_object_global = @intFromEnum(Resolution.__tls_size) + 1;
1134
1135 pub const Unpacked = union(enum) {
1136 unresolved,
1137 __heap_base,
1138 __heap_end,
1139 __stack_pointer,
1140 __tls_align,
1141 __tls_base,
1142 __tls_size,
1143 object_global: ObjectGlobalIndex,
1144 nav_exe: NavsExeIndex,
1145 nav_obj: NavsObjIndex,
8941146 };
895 if (symbol.name == wasm.preloaded_strings.__indirect_function_table) continue;
8961147
897 if (symbol.isLocal()) {
898 if (symbol.isUndefined()) {
899 diags.addParseError(obj_path, "local symbol '{s}' references import", .{
900 wasm.stringSlice(symbol.name),
901 });
902 }
903 try wasm.resolved_symbols.putNoClobber(gpa, location, {});
904 continue;
1148 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
1149 return switch (r) {
1150 .unresolved => .unresolved,
1151 .__heap_base => .__heap_base,
1152 .__heap_end => .__heap_end,
1153 .__stack_pointer => .__stack_pointer,
1154 .__tls_align => .__tls_align,
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 };
9051172 }
9061173
907 const maybe_existing = try wasm.globals.getOrPut(gpa, symbol.name);
908 if (!maybe_existing.found_existing) {
909 maybe_existing.value_ptr.* = location;
910 try wasm.resolved_symbols.putNoClobber(gpa, location, {});
911
912 if (symbol.isUndefined()) {
913 try wasm.undefs.putNoClobber(gpa, symbol.name, location);
914 }
915 continue;
1174 pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution {
1175 return switch (unpacked) {
1176 .unresolved => .unresolved,
1177 .__heap_base => .__heap_base,
1178 .__heap_end => .__heap_end,
1179 .__stack_pointer => .__stack_pointer,
1180 .__tls_align => .__tls_align,
1181 .__tls_base => .__tls_base,
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 };
9161187 }
9171188
918 const existing_loc = maybe_existing.value_ptr.*;
919 const existing_sym: *Symbol = wasm.symbolLocSymbol(existing_loc);
920 const existing_file_path: Path = if (existing_loc.file.unwrap()) |id| objectPath(wasm, id) else .{
921 .root_dir = std.Build.Cache.Directory.cwd(),
922 .sub_path = wasm.name,
923 };
924
925 if (!existing_sym.isUndefined()) outer: {
926 if (!symbol.isUndefined()) inner: {
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 }
1189 pub fn fromIpNav(wasm: *const Wasm, ip_nav: InternPool.Nav.Index) Resolution {
1190 const comp = wasm.base.comp;
1191 const is_obj = comp.config.output_mode == .Obj;
1192 return pack(wasm, if (is_obj) .{
1193 .nav_obj = @enumFromInt(wasm.navs_obj.getIndex(ip_nav).?),
1194 } else .{
1195 .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(ip_nav).?),
1196 });
1197 }
9391198
940 try wasm.discarded.put(gpa, location, existing_loc);
941 continue; // Do not overwrite defined symbols with undefined symbols
1199 pub fn fromObjectGlobal(wasm: *const Wasm, object_global: ObjectGlobalIndex) Resolution {
1200 return pack(wasm, .{ .object_global = object_global });
9421201 }
9431202
944 if (symbol.tag != existing_sym.tag) {
945 var err = try diags.addErrorWithNotes(2);
946 try err.addMsg("symbol '{s}' mismatching types '{s}' and '{s}'", .{
947 wasm.stringSlice(symbol.name), @tagName(symbol.tag), @tagName(existing_sym.tag),
948 });
949 try err.addNote("first definition in '{'}'", .{existing_file_path});
950 try err.addNote("next definition in '{'}'", .{obj_path});
1203 pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 {
1204 return switch (unpack(r, wasm)) {
1205 .unresolved => unreachable,
1206 .__heap_base => @tagName(Unpacked.__heap_base),
1207 .__heap_end => @tagName(Unpacked.__heap_end),
1208 .__stack_pointer => @tagName(Unpacked.__stack_pointer),
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 };
9511216 }
1217 };
9521218
953 if (existing_sym.isUndefined() and symbol.isUndefined()) {
954 // only verify module/import name for function symbols
955 if (symbol.tag == .function) {
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 }
1219 /// Index into `Wasm.object_global_imports`.
1220 pub const Index = enum(u32) {
1221 _,
9731222
974 // both undefined so skip overwriting existing symbol and discard the new symbol
975 try wasm.discarded.put(gpa, location, existing_loc);
976 continue;
1223 pub fn key(index: Index, wasm: *const Wasm) *String {
1224 return &wasm.object_global_imports.keys()[@intFromEnum(index)];
9771225 }
9781226
979 if (existing_sym.tag == .global) {
980 const existing_ty = wasm.getGlobalType(existing_loc);
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 }
1227 pub fn value(index: Index, wasm: *const Wasm) *GlobalImport {
1228 return &wasm.object_global_imports.values()[@intFromEnum(index)];
9881229 }
9891230
990 if (existing_sym.tag == .function) {
991 const existing_ty = wasm.getFunctionSignature(existing_loc);
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 }
1231 pub fn symbolName(index: Index, wasm: *const Wasm) String {
1232 return index.key(wasm).*;
10001233 }
10011234
1002 // when both symbols are weak, we skip overwriting unless the existing
1003 // symbol is weak and the new one isn't, in which case we *do* overwrite it.
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;
1235 pub fn importName(index: Index, wasm: *const Wasm) String {
1236 return index.value(wasm).name;
10081237 }
10091238
1010 // simply overwrite with the new symbol
1011 log.debug("Overwriting symbol '{s}'", .{wasm.stringSlice(symbol.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);
1239 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
1240 return index.value(wasm).module_name;
10211241 }
1022 }
1023}
10241242
1025fn resolveSymbolsInArchives(wasm: *Wasm) !void {
1026 if (wasm.lazy_archives.items.len == 0) return;
1027 const gpa = wasm.base.comp.gpa;
1028 const diags = &wasm.base.comp.link_diags;
1243 pub fn globalType(index: Index, wasm: *const Wasm) ObjectGlobal.Type {
1244 return value(index, wasm).type();
1245 }
1246 };
10291247
1030 log.debug("Resolving symbols in lazy_archives", .{});
1031 var index: u32 = 0;
1032 undef_loop: while (index < wasm.undefs.count()) {
1033 const sym_name_index = wasm.undefs.keys()[index];
1248 pub fn @"type"(gi: *const GlobalImport) ObjectGlobal.Type {
1249 return gi.flags.global_type.to();
1250 }
1251};
10341252
1035 for (wasm.lazy_archives.items) |lazy_archive| {
1036 const sym_name = wasm.stringSlice(sym_name_index);
1037 log.debug("Detected symbol '{s}' in archive '{'}', parsing objects..", .{
1038 sym_name, lazy_archive.path,
1039 });
1040 const offset = lazy_archive.archive.toc.get(sym_name) orelse continue; // symbol does not exist in this archive
1041
1042 // Symbol is found in unparsed object file within current archive.
1043 // Parse object and and resolve symbols again before we check remaining
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));
1253pub const ObjectGlobal = extern struct {
1254 /// `none` if this function has no symbol describing it.
1255 name: OptionalString,
1256 flags: SymbolFlags,
1257 expr: Expr,
1258 /// The object file whose global section contains this global.
1259 object_index: ObjectIndex,
1260 offset: u32,
1261 size: u32,
10521262
1053 // continue loop for any remaining undefined symbols that still exist
1054 // after resolving last object file
1055 continue :undef_loop;
1056 }
1057 index += 1;
1263 pub fn @"type"(og: *const ObjectGlobal) Type {
1264 return og.flags.global_type.to();
10581265 }
1059}
10601266
1061/// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value.
1062fn writeI32Const(writer: anytype, val: u32) !void {
1063 try writer.writeByte(std.wasm.opcode(.i32_const));
1064 try leb.writeIleb128(writer, @as(i32, @bitCast(val)));
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;
1267 pub const Type = struct {
1268 valtype: std.wasm.Valtype,
1269 mutable: bool,
1270 };
10721271
1073 // Passive segments are used to avoid memory being reinitialized on each
1074 // thread's instantiation. These passive segments are initialized and
1075 // dropped in __wasm_init_memory, which is registered as the start function
1076 // We also initialize bss segments (using memory.fill) as part of this
1077 // function.
1078 if (!wasm.hasPassiveInitializationSegments()) {
1079 return;
1272 pub fn relocations(og: *const ObjectGlobal, wasm: *const Wasm) ObjectRelocation.IterableSlice {
1273 const global_section_index = og.object_index.ptr(wasm).global_section_index.?;
1274 const relocs = wasm.object_relocations_table.get(global_section_index) orelse return .empty;
1275 return .init(relocs, og.offset, og.size, wasm);
10801276 }
1081 const sym_loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_memory, .function);
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 }
1277};
11471278
1148 try writeI32Const(writer, 0);
1149 try writeI32Const(writer, segment.size);
1150 try writer.writeByte(std.wasm.opcode(.misc_prefix));
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 }
1279pub const RefType1 = enum(u1) {
1280 funcref,
1281 externref,
12121282
1213 try writer.writeByte(std.wasm.opcode(.misc_prefix));
1214 try leb.writeUleb128(writer, std.wasm.miscOpcode(.data_drop));
1215 try leb.writeUleb128(writer, segment_index);
1216 }
1283 pub fn from(rt: std.wasm.RefType) RefType1 {
1284 return switch (rt) {
1285 .funcref => .funcref,
1286 .externref => .externref,
1287 };
12171288 }
12181289
1219 // End of the function body
1220 try writer.writeByte(std.wasm.opcode(.end));
1290 pub fn to(rt: RefType1) std.wasm.RefType {
1291 return switch (rt) {
1292 .funcref => .funcref,
1293 .externref => .externref,
1294 };
1295 }
1296};
12211297
1222 try wasm.createSyntheticFunction(
1223 wasm.preloaded_strings.__wasm_init_memory,
1224 std.wasm.Type{ .params = &.{}, .returns = &.{} },
1225 &function_body,
1226 );
1227}
1298pub const TableImport = extern struct {
1299 flags: SymbolFlags,
1300 module_name: String,
1301 /// May be different than the key which is a symbol name.
1302 name: String,
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 for
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;
1315 const first_object_table = @intFromEnum(Resolution.__indirect_function_table) + 1;
12351316
1236 // When we have TLS GOT entries and shared memory is enabled,
1237 // we must perform runtime relocations or else we don't create the function.
1238 if (!shared_memory or !wasm.requiresTLSReloc()) {
1239 return;
1240 }
1317 pub const Unpacked = union(enum) {
1318 unresolved,
1319 __indirect_function_table,
1320 object_table: ObjectTableIndex,
1321 };
12411322
1242 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_apply_global_tls_relocs, .function);
1243 wasm.symbolLocSymbol(loc).mark();
1244 var function_body = std.ArrayList(u8).init(gpa);
1245 defer function_body.deinit();
1246 const writer = function_body.writer();
1247
1248 // locals (we have none)
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}
1323 pub fn unpack(r: Resolution) Unpacked {
1324 return switch (r) {
1325 .unresolved => .unresolved,
1326 .__indirect_function_table => .__indirect_function_table,
1327 _ => .{ .object_table = @enumFromInt(@intFromEnum(r) - first_object_table) },
1328 };
1329 }
12771330
1278fn validateFeatures(
1279 wasm: *const Wasm,
1280 to_emit: *[@typeInfo(Feature.Tag).@"enum".fields.len]bool,
1281 emit_features_count: *u32,
1282) !void {
1283 const comp = wasm.base.comp;
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 }
1331 fn pack(unpacked: Unpacked) Resolution {
1332 return switch (unpacked) {
1333 .unresolved => .unresolved,
1334 .__indirect_function_table => .__indirect_function_table,
1335 .object_table => |i| @enumFromInt(first_object_table + @intFromEnum(i)),
1336 };
13091337 }
1310 }
13111338
1312 // extract all the used, disallowed and required features from each
1313 // linked object file so we can test them.
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 }
1339 fn fromObjectTable(object_table: ObjectTableIndex) Resolution {
1340 return pack(.{ .object_table = object_table });
13291341 }
13301342
1331 for (object.segment_info) |segment| {
1332 if (segment.isTLS()) {
1333 has_tls = true;
1334 }
1343 pub fn refType(r: Resolution, wasm: *const Wasm) std.wasm.RefType {
1344 return switch (unpack(r)) {
1345 .unresolved => unreachable,
1346 .__indirect_function_table => .funcref,
1347 .object_table => |i| i.ptr(wasm).flags.ref_type.to(),
1348 };
13351349 }
1336 }
13371350
1338 // when we infer the features, we allow each feature found in the 'used' set
1339 // and insert it into the 'allowed' set. When features are not inferred,
1340 // we validate that a used feature is allowed.
1341 for (used, 0..) |used_set, used_index| {
1342 const is_enabled = @as(u1, @truncate(used_set)) != 0;
1343 if (infer) {
1344 allowed[used_index] = is_enabled;
1345 emit_features_count.* += @intFromBool(is_enabled);
1346 } else if (is_enabled and !allowed[used_index]) {
1347 diags.addParseError(
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;
1351 pub fn limits(r: Resolution, wasm: *const Wasm) std.wasm.Limits {
1352 return switch (unpack(r)) {
1353 .unresolved => unreachable,
1354 .__indirect_function_table => .{
1355 .flags = .{ .has_max = true, .is_shared = false },
1356 .min = @intCast(wasm.flush_buffer.indirect_function_table.entries.len + 1),
1357 .max = @intCast(wasm.flush_buffer.indirect_function_table.entries.len + 1),
1358 },
1359 .object_table => |i| i.ptr(wasm).limits(),
1360 };
13531361 }
1354 }
1362 };
13551363
1356 if (!valid_feature_set) {
1357 return error.FlushFailure;
1358 }
1364 /// Index into `object_table_imports`.
1365 pub const Index = enum(u32) {
1366 _,
13591367
1360 if (shared_memory) {
1361 const disallowed_feature = disallowed[@intFromEnum(Feature.Tag.shared_mem)];
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;
1368 pub fn key(index: Index, wasm: *const Wasm) *String {
1369 return &wasm.object_table_imports.keys()[@intFromEnum(index)];
13691370 }
13701371
1371 for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| {
1372 if (!allowed[@intFromEnum(feature)]) {
1373 var err = try diags.addErrorWithNotes(0);
1374 try err.addMsg("feature '{}' is not used but is required for shared-memory", .{feature});
1375 }
1372 pub fn value(index: Index, wasm: *const Wasm) *TableImport {
1373 return &wasm.object_table_imports.values()[@intFromEnum(index)];
13761374 }
1377 }
13781375
1379 if (has_tls) {
1380 for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| {
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 }
1376 pub fn name(index: Index, wasm: *const Wasm) String {
1377 return index.key(wasm).*;
13851378 }
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;
14031382 }
1383 };
14041384
1405 // validate the linked object file has each required feature
1406 for (required, 0..) |required_feature, feature_index| {
1407 const is_required = @as(u1, @truncate(required_feature)) != 0;
1408 if (is_required and !object_used_features[feature_index]) {
1409 var err = try diags.addErrorWithNotes(2);
1410 try err.addMsg("feature '{}' is required but not used in linked object", .{@as(Feature.Tag, @enumFromInt(feature_index))});
1411 try err.addNote("required by '{'}'", .{wasm.objects.items[required_feature >> 1].path});
1412 try err.addNote("missing in '{'}'", .{object.path});
1413 valid_feature_set = false;
1414 }
1415 }
1385 pub fn limits(ti: *const TableImport) std.wasm.Limits {
1386 return .{
1387 .flags = .{
1388 .has_max = ti.flags.limits_has_max,
1389 .is_shared = ti.flags.limits_is_shared,
1390 },
1391 .min = ti.limits_min,
1392 .max = ti.limits_max,
1393 };
14161394 }
1395};
14171396
1418 if (!valid_feature_set) {
1419 return error.FlushFailure;
1420 }
1397pub const Table = extern struct {
1398 module_name: OptionalString,
1399 name: OptionalString,
1400 flags: SymbolFlags,
1401 limits_min: u32,
1402 limits_max: u32,
14211403
1422 to_emit.* = allowed;
1423}
1404 pub fn limits(t: *const Table) std.wasm.Limits {
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 from
1426/// any object file. For instance, the `__heap_base` symbol will only be created,
1427/// if one or multiple undefined references exist. When none exist, the symbol will
1428/// not be created, ensuring we don't unnecessarily emit unreferenced symbols.
1429fn resolveLazySymbols(wasm: *Wasm) !void {
1430 const comp = wasm.base.comp;
1431 const gpa = comp.gpa;
1432 const shared_memory = comp.config.shared_memory;
1416/// Uniquely identifies a section across all objects. By subtracting
1417/// `Object.local_section_index_base` from this one, the Object section index
1418/// is obtained.
1419pub const ObjectSectionIndex = enum(u32) {
1420 _,
1421};
14331422
1434 if (wasm.getExistingString("__heap_base")) |name_offset| {
1435 if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {
1436 const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data);
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 }
1423/// Index into `object_tables`.
1424pub const ObjectTableIndex = enum(u32) {
1425 _,
14411426
1442 if (wasm.getExistingString("__heap_end")) |name_offset| {
1443 if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {
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 }
1427 pub fn ptr(index: ObjectTableIndex, wasm: *const Wasm) *Table {
1428 return &wasm.object_tables.items[@intFromEnum(index)];
14481429 }
14491430
1450 if (!shared_memory) {
1451 if (wasm.getExistingString("__tls_base")) |name_offset| {
1452 if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {
1453 const loc = try wasm.createSyntheticSymbolOffset(name_offset, .global);
1454 try wasm.discarded.putNoClobber(gpa, kv.value, loc);
1455 _ = wasm.resolved_symbols.swapRemove(kv.value);
1456 const symbol = wasm.symbolLocSymbol(loc);
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 }
1431 pub fn chaseWeak(i: ObjectTableIndex, wasm: *const Wasm) ObjectTableIndex {
1432 const table = ptr(i, wasm);
1433 if (table.flags.binding != .weak) return i;
1434 const name = table.name.unwrap().?;
1435 const import = wasm.object_table_imports.getPtr(name).?;
1436 assert(import.resolution != .unresolved); // otherwise it should resolve to this one.
1437 return import.resolution.unpack().object_table;
14651438 }
1466}
1439};
14671440
1468pub fn findGlobalSymbol(wasm: *const Wasm, name: []const u8) ?SymbolLoc {
1469 const name_index = wasm.getExistingString(name) orelse return null;
1470 return wasm.globals.get(name_index);
1471}
1441/// Index into `Wasm.object_globals`.
1442pub const ObjectGlobalIndex = enum(u32) {
1443 _,
14721444
1473fn checkUndefinedSymbols(wasm: *const Wasm) !void {
1474 const comp = wasm.base.comp;
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;
1445 pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *ObjectGlobal {
1446 return &wasm.object_globals.items[@intFromEnum(index)];
15031447 }
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| {
1511 func_type.deinit(gpa);
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);
1449 pub fn name(index: ObjectGlobalIndex, wasm: *const Wasm) OptionalString {
1450 return index.ptr(wasm).name;
15211451 }
15221452
1523 for (wasm.lazy_archives.items) |*lazy_archive| lazy_archive.deinit(gpa);
1524 wasm.lazy_archives.deinit(gpa);
1525
1526 if (wasm.globals.get(wasm.preloaded_strings.__wasm_init_tls)) |loc| {
1527 const atom = wasm.symbol_atom.get(loc).?;
1528 wasm.getAtomPtr(atom).deinit(gpa);
1453 pub fn chaseWeak(i: ObjectGlobalIndex, wasm: *const Wasm) ObjectGlobalIndex {
1454 const global = ptr(i, wasm);
1455 if (global.flags.binding != .weak) return i;
1456 const import_name = global.name.unwrap().?;
1457 const import = wasm.object_global_imports.getPtr(import_name).?;
1458 assert(import.resolution != .unresolved); // otherwise it should resolve to this one.
1459 return import.resolution.unpack(wasm).object_global;
15291460 }
1461};
15301462
1531 wasm.synthetic_symbols.deinit(gpa);
1532 wasm.globals.deinit(gpa);
1533 wasm.resolved_symbols.deinit(gpa);
1534 wasm.undefs.deinit(gpa);
1535 wasm.discarded.deinit(gpa);
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);
1463pub const ObjectMemory = extern struct {
1464 flags: SymbolFlags,
1465 name: OptionalString,
1466 limits_min: u32,
1467 limits_max: u32,
15431468
1544 // free output sections
1545 wasm.imports.deinit(gpa);
1546 wasm.func_types.deinit(gpa);
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);
1469 /// Index into `Wasm.object_memories`.
1470 pub const Index = enum(u32) {
1471 _,
15531472
1554 wasm.string_bytes.deinit(gpa);
1555 wasm.string_table.deinit(gpa);
1556 wasm.dump_argv_list.deinit(gpa);
1557}
1473 pub fn ptr(index: Index, wasm: *const Wasm) *ObjectMemory {
1474 return &wasm.object_memories.items[@intFromEnum(index)];
1475 }
1476 };
15581477
1559pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
1560 if (build_options.skip_non_native and builtin.object_format != .wasm) {
1561 @panic("Attempted to compile for object format that was disabled by build configuration");
1478 pub fn limits(om: *const ObjectMemory) std.wasm.Limits {
1479 return .{
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 };
15621487 }
1563 if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness);
1564 try wasm.zig_object.?.updateFunc(wasm, pt, func_index, air, liveness);
1565}
1488};
15661489
1567// Generate code for the "Nav", storing it in memory to be later written to
1568// the file on flush().
1569pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {
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}
1490/// Index into `Wasm.object_functions`.
1491pub const ObjectFunctionIndex = enum(u32) {
1492 _,
15761493
1577pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
1578 if (wasm.llvm_object) |_| return;
1579 try wasm.zig_object.?.updateLineNumber(pt, ti_id);
1580}
1494 pub fn ptr(index: ObjectFunctionIndex, wasm: *const Wasm) *ObjectFunction {
1495 return &wasm.object_functions.items[@intFromEnum(index)];
1496 }
15811497
1582/// From a given symbol location, returns its `wasm.GlobalType`.
1583/// Asserts the Symbol represents a global.
1584fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType {
1585 const symbol = wasm.symbolLocSymbol(loc);
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 },
1498 pub fn toOptional(i: ObjectFunctionIndex) OptionalObjectFunctionIndex {
1499 const result: OptionalObjectFunctionIndex = @enumFromInt(@intFromEnum(i));
1500 assert(result != .none);
1501 return result;
16091502 }
1610}
16111503
1612/// From a given symbol location, returns its `wasm.Type`.
1613/// Asserts the Symbol represents a function.
1614fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {
1615 const symbol = wasm.symbolLocSymbol(loc);
1616 assert(symbol.tag == .function);
1617 const is_undefined = symbol.isUndefined();
1618 switch (loc.file) {
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 },
1504 pub fn chaseWeak(i: ObjectFunctionIndex, wasm: *const Wasm) ObjectFunctionIndex {
1505 const func = ptr(i, wasm);
1506 if (func.flags.binding != .weak) return i;
1507 const name = func.name.unwrap().?;
1508 const import = wasm.object_function_imports.getPtr(name).?;
1509 assert(import.resolution != .unresolved); // otherwise it should resolve to this one.
1510 return import.resolution.unpack(wasm).object_function;
16511511 }
1652}
1512};
16531513
1654/// Returns the symbol index from a symbol of which its flag is set global,
1655/// such as an exported or imported symbol.
1656/// If the symbol does not yet exist, creates a new one symbol instead
1657/// and then returns the index to it.
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}
1514/// Index into `object_functions`, or null.
1515pub const OptionalObjectFunctionIndex = enum(u32) {
1516 none = std.math.maxInt(u32),
1517 _,
16631518
1664/// For a given `Nav`, find the given symbol index's atom, and create a relocation for the type.
1665/// Returns the given pointer address
1666pub fn getNavVAddr(
1667 wasm: *Wasm,
1668 pt: Zcu.PerThread,
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}
1519 pub fn unwrap(i: OptionalObjectFunctionIndex) ?ObjectFunctionIndex {
1520 if (i == .none) return null;
1521 return @enumFromInt(@intFromEnum(i));
1522 }
1523};
16741524
1675pub fn lowerUav(
1676 wasm: *Wasm,
1677 pt: Zcu.PerThread,
1678 uav: InternPool.Index,
1679 explicit_alignment: Alignment,
1680 src_loc: Zcu.LazySrcLoc,
1681) !codegen.GenResult {
1682 return wasm.zig_object.?.lowerUav(wasm, pt, uav, explicit_alignment, src_loc);
1683}
1525pub const ObjectDataSegment = extern struct {
1526 /// `none` if segment info custom subsection is missing.
1527 name: OptionalString,
1528 flags: Flags,
1529 payload: DataPayload,
1530 offset: u32,
1531 object_index: ObjectIndex,
1532
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 {
1686 return wasm.zig_object.?.getUavVAddr(wasm, uav, reloc_info);
1687}
1551 /// Index into `Wasm.object_data_segments`.
1552 pub const Index = enum(u32) {
1553 _,
16881554
1689pub fn deleteExport(
1690 wasm: *Wasm,
1691 exported: Zcu.Exported,
1692 name: InternPool.NullTerminatedString,
1693) void {
1694 if (wasm.llvm_object) |_| return;
1695 return wasm.zig_object.?.deleteExport(wasm, exported, name);
1696}
1555 pub fn ptr(i: Index, wasm: *const Wasm) *ObjectDataSegment {
1556 return &wasm.object_data_segments.items[@intFromEnum(i)];
1557 }
1558 };
16971559
1698pub fn updateExports(
1699 wasm: *Wasm,
1700 pt: Zcu.PerThread,
1701 exported: Zcu.Exported,
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");
1560 pub fn relocations(ods: *const ObjectDataSegment, wasm: *const Wasm) ObjectRelocation.IterableSlice {
1561 const data_section_index = ods.object_index.ptr(wasm).data_section_index.?;
1562 const relocs = wasm.object_relocations_table.get(data_section_index) orelse return .empty;
1563 return .init(relocs, ods.offset, ods.payload.len, wasm);
17061564 }
1707 if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices);
1708 return wasm.zig_object.?.updateExports(wasm, pt, exported, export_indices);
1709}
1565};
17101566
1711pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {
1712 if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
1713 return wasm.zig_object.?.freeDecl(wasm, decl_index);
1714}
1567/// A local or exported global const from an object file.
1568pub const ObjectData = extern struct {
1569 segment: ObjectDataSegment.Index,
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.
1717/// Starts at offset 1, where the value `0` represents an unresolved function pointer
1718/// or null-pointer
1719fn mapFunctionTable(wasm: *Wasm) void {
1720 var it = wasm.function_table.iterator();
1721 var index: u32 = 1;
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);
1577 /// Index into `Wasm.object_datas`.
1578 pub const Index = enum(u32) {
1579 _,
1580
1581 pub fn ptr(i: Index, wasm: *const Wasm) *ObjectData {
1582 return &wasm.object_datas.items[@intFromEnum(i)];
17291583 }
1730 }
1584 };
1585};
17311586
1732 if (wasm.import_table or wasm.base.comp.config.output_mode == .Obj) {
1733 const sym_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?;
1734 const import = wasm.imports.getPtr(sym_loc).?;
1735 import.kind.table.limits.min = index - 1; // we start at index 1.
1736 } else if (index > 1) {
1737 log.debug("Appending indirect function table", .{});
1738 const sym_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?;
1739 const symbol = wasm.symbolLocSymbol(sym_loc);
1740 const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count];
1741 table.limits = .{ .min = index, .max = index, .flags = 0x1 };
1742 }
1743}
1587pub const ObjectDataImport = extern struct {
1588 resolution: Resolution,
1589 flags: SymbolFlags,
1590 source_location: SourceLocation,
1591
1592 pub const Resolution = enum(u32) {
1593 unresolved,
1594 __zig_error_names,
1595 __zig_error_name_table,
1596 __heap_base,
1597 __heap_end,
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.
1746/// Simply inserts it into the map of atoms when it doesn't exist yet.
1747pub fn appendAtomAtIndex(wasm: *Wasm, index: Segment.Index, atom_index: Atom.Index) !void {
1748 const gpa = wasm.base.comp.gpa;
1749 const atom = wasm.getAtomPtr(atom_index);
1750 if (wasm.atoms.getPtr(index)) |last_index_ptr| {
1751 atom.prev = last_index_ptr.*;
1752 last_index_ptr.* = atom_index;
1753 } else {
1754 try wasm.atoms.putNoClobber(gpa, index, atom_index);
1755 }
1756}
1603 const first_object = @intFromEnum(Resolution.__heap_end) + 1;
1604
1605 pub const Unpacked = union(enum) {
1606 unresolved,
1607 __zig_error_names,
1608 __zig_error_name_table,
1609 __heap_base,
1610 __heap_end,
1611 object: ObjectData.Index,
1612 uav_exe: UavsExeIndex,
1613 uav_obj: UavsObjIndex,
1614 nav_exe: NavsExeIndex,
1615 nav_obj: NavsObjIndex,
1616 };
17571617
1758fn allocateAtoms(wasm: *Wasm) !void {
1759 // first sort the data segments
1760 try sortDataSegments(wasm);
1761
1762 var it = wasm.atoms.iterator();
1763 while (it.next()) |entry| {
1764 const segment = wasm.segmentPtr(entry.key_ptr.*);
1765 var atom_index = entry.value_ptr.*;
1766 if (entry.key_ptr.toOptional() == wasm.code_section_index) {
1767 // Code section is allocated upon writing as they are required to be ordered
1768 // to synchronise with the function section.
1769 continue;
1770 }
1771 var offset: u32 = 0;
1772 while (true) {
1773 const atom = wasm.getAtomPtr(atom_index);
1774 const symbol_loc = atom.symbolLoc();
1775 // Ensure we get the original symbol, so we verify the correct symbol on whether
1776 // it is dead or not and ensure an atom is removed when dead.
1777 // This is required as we may have parsed aliases into atoms.
1778 const sym = switch (symbol_loc.file) {
1779 .zig_object => wasm.zig_object.?.symbols.items[@intFromEnum(symbol_loc.index)],
1780 .none => wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)],
1781 _ => wasm.objects.items[@intFromEnum(symbol_loc.file)].symtable[@intFromEnum(symbol_loc.index)],
1618 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
1619 return switch (r) {
1620 .unresolved => .unresolved,
1621 .__zig_error_names => .__zig_error_names,
1622 .__zig_error_name_table => .__zig_error_name_table,
1623 .__heap_base => .__heap_base,
1624 .__heap_end => .__heap_end,
1625 _ => {
1626 const object_index = @intFromEnum(r) - first_object;
1627
1628 const uav_index = if (object_index < wasm.object_datas.items.len)
1629 return .{ .object = @enumFromInt(object_index) }
1630 else
1631 object_index - wasm.object_datas.items.len;
1632
1633 const comp = wasm.base.comp;
1634 const is_obj = comp.config.output_mode == .Obj;
1635 if (is_obj) {
1636 const nav_index = if (uav_index < wasm.uavs_obj.entries.len)
1637 return .{ .uav_obj = @enumFromInt(uav_index) }
1638 else
1639 uav_index - wasm.uavs_obj.entries.len;
1640
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 },
17821651 };
1652 }
17831653
1784 // Dead symbols must be unlinked from the linked-list to prevent them
1785 // from being emit into the binary.
1786 if (sym.isDead()) {
1787 if (entry.value_ptr.* == atom_index and atom.prev != .null) {
1788 // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update
1789 // the entry to point it to the previous atom to ensure we do not start with a dead symbol that
1790 // was removed and therefore do not emit any code at all.
1791 entry.value_ptr.* = atom.prev;
1792 }
1793 if (atom.prev == .null) break;
1794 atom_index = atom.prev;
1795 atom.prev = .null;
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;
1654 pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution {
1655 return switch (unpacked) {
1656 .unresolved => .unresolved,
1657 .__zig_error_names => .__zig_error_names,
1658 .__zig_error_name_table => .__zig_error_name_table,
1659 .__heap_base => .__heap_base,
1660 .__heap_end => .__heap_end,
1661 .object => |i| @enumFromInt(first_object + @intFromEnum(i)),
1662 inline .uav_exe, .uav_obj => |i| @enumFromInt(first_object + wasm.object_datas.items.len + @intFromEnum(i)),
1663 .nav_exe => |i| @enumFromInt(first_object + wasm.object_datas.items.len + wasm.uavs_exe.entries.len + @intFromEnum(i)),
1664 .nav_obj => |i| @enumFromInt(first_object + wasm.object_datas.items.len + wasm.uavs_obj.entries.len + @intFromEnum(i)),
1665 };
18091666 }
1810 segment.size = @intCast(segment.alignment.forward(offset));
1811 }
1812}
18131667
1814/// For each data symbol, sets the virtual address.
1815fn allocateVirtualAddresses(wasm: *Wasm) void {
1816 for (wasm.resolved_symbols.keys()) |loc| {
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 };
1668 pub fn fromObjectDataIndex(wasm: *const Wasm, object_data_index: ObjectData.Index) Resolution {
1669 return pack(wasm, .{ .object = object_data_index });
1670 }
18271671
1828 const atom = wasm.getAtom(atom_index);
1829 const merge_segment = wasm.base.comp.config.output_mode != .Obj;
1830 const segment_info = switch (atom.file) {
1831 .zig_object => wasm.zig_object.?.segment_info.items,
1832 .none => wasm.segment_info.values(),
1833 _ => wasm.objects.items[@intFromEnum(atom.file)].segment_info,
1834 };
1835 const segment_name = segment_info[symbol.index].outputName(merge_segment);
1836 const segment_index = wasm.data_segments.get(segment_name).?;
1837 const segment = wasm.segmentPtr(segment_index);
1838
1839 // TLS symbols have their virtual address set relative to their own TLS segment,
1840 // rather than the entire Data section.
1841 if (symbol.hasFlag(.WASM_SYM_TLS)) {
1842 symbol.virtual_address = atom.offset;
1843 } else {
1844 symbol.virtual_address = atom.offset + segment.offset;
1672 pub fn objectDataSegment(r: Resolution, wasm: *const Wasm) ?ObjectDataSegment.Index {
1673 return switch (unpack(r, wasm)) {
1674 .unresolved => unreachable,
1675 .object => |i| i.ptr(wasm).segment,
1676 .__zig_error_names,
1677 .__zig_error_name_table,
1678 .__heap_base,
1679 .__heap_end,
1680 .uav_exe,
1681 .uav_obj,
1682 .nav_exe,
1683 .nav_obj,
1684 => null,
1685 };
18451686 }
1846 }
1847}
18481687
1849fn sortDataSegments(wasm: *Wasm) !void {
1850 const gpa = wasm.base.comp.gpa;
1851 var new_mapping: std.StringArrayHashMapUnmanaged(Segment.Index) = .empty;
1852 try new_mapping.ensureUnusedCapacity(gpa, wasm.data_segments.count());
1853 errdefer new_mapping.deinit(gpa);
1688 pub fn dataLoc(r: Resolution, wasm: *const Wasm) DataLoc {
1689 return switch (unpack(r, wasm)) {
1690 .unresolved => unreachable,
1691 .object => |i| {
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());
1856 defer gpa.free(keys);
1710 /// Points into `Wasm.object_data_imports`.
1711 pub const Index = enum(u32) {
1712 _,
18571713
1858 const SortContext = struct {
1859 fn sort(_: void, lhs: []const u8, rhs: []const u8) bool {
1860 return order(lhs) < order(rhs);
1714 pub fn value(i: @This(), wasm: *const Wasm) *ObjectDataImport {
1715 return &wasm.object_data_imports.values()[@intFromEnum(i)];
18611716 }
18621717
1863 fn order(name: []const u8) u8 {
1864 if (mem.startsWith(u8, name, ".rodata")) return 0;
1865 if (mem.startsWith(u8, name, ".data")) return 1;
1866 if (mem.startsWith(u8, name, ".text")) return 2;
1867 return 3;
1718 pub fn fromSymbolName(wasm: *const Wasm, name: String) ?Index {
1719 return @enumFromInt(wasm.object_data_imports.getIndex(name) orelse return null);
18681720 }
18691721 };
1722};
18701723
1871 mem.sort([]const u8, keys, {}, SortContext.sort);
1872 for (keys) |key| {
1873 const segment_index = wasm.data_segments.get(key).?;
1874 new_mapping.putAssumeCapacity(key, segment_index);
1875 }
1876 wasm.data_segments.deinit(gpa);
1877 wasm.data_segments = new_mapping;
1878}
1724pub const DataPayload = extern struct {
1725 off: Off,
1726 /// The size in bytes of the data representing the segment within the section.
1727 len: u32,
18791728
1880/// Obtains all initfuncs from each object file, verifies its function signature,
1881/// and then appends it to our final `init_funcs` list.
1882/// After all functions have been inserted, the functions will be ordered based
1883/// on their priority.
1884/// NOTE: This function must be called before we merged any other section.
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 }
1729 pub const Off = enum(u32) {
1730 /// The payload is all zeroes (bss section).
1731 none = std.math.maxInt(u32),
1732 /// Points into string_bytes. No corresponding string_table entry.
1733 _,
19201734
1921 // sort the initfunctions based on their priority
1922 mem.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan);
1735 pub fn unwrap(off: Off) ?u32 {
1736 return if (off == .none) null else @intFromEnum(off);
1737 }
1738 };
19231739
1924 if (wasm.init_funcs.items.len > 0) {
1925 const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_call_ctors).?;
1926 try wasm.mark(loc);
1740 pub fn slice(p: DataPayload, wasm: *const Wasm) []const u8 {
1741 return wasm.string_bytes.items[p.off.unwrap().?..][0..p.len];
19271742 }
1928}
1743};
19291744
1930/// Creates a function body for the `__wasm_call_ctors` symbol.
1931/// Loops over all constructors found in `init_funcs` and calls them
1932/// respectively based on their priority which was sorted by `setupInitFunctions`.
1933/// NOTE: This function must be called after we merged all sections to ensure the
1934/// references to the function stored in the symbol have been finalized so we end
1935/// up calling the resolved function.
1936fn initializeCallCtorsFunction(wasm: *Wasm) !void {
1937 const gpa = wasm.base.comp.gpa;
1938 // No code to emit, so also no ctors to call
1939 if (wasm.code_section_index == .none) {
1940 // Make sure to remove it from the resolved symbols so we do not emit
1941 // it within any section. TODO: Remove this once we implement garbage collection.
1942 const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_call_ctors).?;
1943 assert(wasm.resolved_symbols.swapRemove(loc));
1944 return;
1945 }
1745/// A reference to a local or exported global const.
1746pub const DataSegmentId = enum(u32) {
1747 __zig_error_names,
1748 __zig_error_name_table,
1749 /// All name string bytes for all `@tagName` implementations, concatenated together.
1750 __zig_tag_names,
1751 /// All tag name slices for all `@tagName` implementations, concatenated together.
1752 __zig_tag_name_table,
1753 /// This and `__heap_end` are better retrieved via a global, but there is
1754 /// some suboptimal code out there (wasi libc) that additionally needs them
1755 /// as data symbols.
1756 __heap_base,
1757 __heap_end,
1758 /// First, an `ObjectDataSegment.Index`.
1759 /// Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object.
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);
1948 defer function_body.deinit();
1949 const writer = function_body.writer();
1763 const first_object = @intFromEnum(DataSegmentId.__heap_end) + 1;
19501764
1951 // Create the function body
1952 {
1953 // Write locals count (we have none)
1954 try leb.writeUleb128(writer, @as(u32, 0));
1765 pub const Category = enum {
1766 /// Thread-local variables.
1767 tls,
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 constructors
1957 for (wasm.init_funcs.items) |init_func_loc| {
1958 const symbol = init_func_loc.getSymbol(wasm);
1959 const func = wasm.functions.values()[symbol.index - wasm.imported_functions_count].func;
1960 const ty = wasm.func_types.items[func.type_index];
1775 pub const Unpacked = union(enum) {
1776 __zig_error_names,
1777 __zig_error_name_table,
1778 __zig_tag_names,
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 index
1963 try writer.writeByte(std.wasm.opcode(.call));
1964 try leb.writeUleb128(writer, symbol.index);
1789 pub fn pack(wasm: *const Wasm, unpacked: Unpacked) DataSegmentId {
1790 return switch (unpacked) {
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 value
1967 for (ty.returns) |_| {
1968 try writer.writeByte(std.wasm.opcode(.drop));
1969 }
1970 }
1804 pub fn unpack(id: DataSegmentId, wasm: *const Wasm) Unpacked {
1805 return switch (id) {
1806 .__zig_error_names => .__zig_error_names,
1807 .__zig_error_name_table => .__zig_error_name_table,
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 body
1973 try writer.writeByte(std.wasm.opcode(.end));
1974 }
1815 const uav_index = if (object_index < wasm.object_data_segments.items.len)
1816 return .{ .object = @enumFromInt(object_index) }
1817 else
1818 object_index - wasm.object_data_segments.items.len;
19751819
1976 try wasm.createSyntheticFunction(
1977 wasm.preloaded_strings.__wasm_call_ctors,
1978 std.wasm.Type{ .params = &.{}, .returns = &.{} },
1979 &function_body,
1980 );
1981}
1820 const comp = wasm.base.comp;
1821 const is_obj = comp.config.output_mode == .Obj;
1822 if (is_obj) {
1823 const nav_index = if (uav_index < wasm.uavs_obj.entries.len)
1824 return .{ .uav_obj = @enumFromInt(uav_index) }
1825 else
1826 uav_index - wasm.uavs_obj.entries.len;
19821827
1983fn createSyntheticFunction(
1984 wasm: *Wasm,
1985 symbol_name: String,
1986 func_ty: std.wasm.Type,
1987 function_body: *std.ArrayList(u8),
1988) !void {
1989 const gpa = wasm.base.comp.gpa;
1990 const loc = wasm.globals.get(symbol_name).?;
1991 const symbol = wasm.symbolLocSymbol(loc);
1992 if (symbol.isDead()) {
1993 return;
1828 return .{ .nav_obj = @enumFromInt(nav_index) };
1829 } else {
1830 const nav_index = if (uav_index < wasm.uavs_exe.entries.len)
1831 return .{ .uav_exe = @enumFromInt(uav_index) }
1832 else
1833 uav_index - wasm.uavs_exe.entries.len;
1834
1835 return .{ .nav_exe = @enumFromInt(nav_index) };
1836 }
1837 },
1838 };
19941839 }
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 by
2014/// the codegeneration backend. This will not allocate the created Atom yet.
2015/// Returns the index of the symbol.
2016pub fn createFunction(
2017 wasm: *Wasm,
2018 symbol_name: []const u8,
2019 func_ty: std.wasm.Type,
2020 function_body: *std.ArrayList(u8),
2021 relocations: *std.ArrayList(Relocation),
2022) !Symbol.Index {
2023 return wasm.zig_object.?.createFunction(wasm, symbol_name, func_ty, function_body, relocations);
2024}
1841 pub fn fromNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) DataSegmentId {
1842 const comp = wasm.base.comp;
1843 const is_obj = comp.config.output_mode == .Obj;
1844 return pack(wasm, if (is_obj) .{
1845 .nav_obj = @enumFromInt(wasm.navs_obj.getIndex(nav_index).?),
1846 } else .{
1847 .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(nav_index).?),
1848 });
1849 }
20251850
2026/// If required, sets the function index in the `start` section.
2027fn setupStartSection(wasm: *Wasm) !void {
2028 if (wasm.globals.get(wasm.preloaded_strings.__wasm_init_memory)) |loc| {
2029 wasm.entry = wasm.symbolLocSymbol(loc).index;
1851 pub fn fromObjectDataSegment(wasm: *const Wasm, object_data_segment: ObjectDataSegment.Index) DataSegmentId {
1852 return pack(wasm, .{ .object = object_data_segment });
20301853 }
2031}
20321854
2033fn initializeTLSFunction(wasm: *Wasm) !void {
2034 const comp = wasm.base.comp;
2035 const gpa = comp.gpa;
2036 const shared_memory = comp.config.shared_memory;
1855 pub fn category(id: DataSegmentId, wasm: *const Wasm) Category {
1856 return switch (unpack(id, wasm)) {
1857 .__zig_error_names,
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 it
2041 wasm.symbolLocSymbol(wasm.globals.get(wasm.preloaded_strings.__wasm_init_tls).?).mark();
1883 pub fn isTls(id: DataSegmentId, wasm: *const Wasm) bool {
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);
2044 defer function_body.deinit();
2045 const writer = function_body.writer();
1904 pub fn isBss(id: DataSegmentId, wasm: *const Wasm) bool {
1905 return id.category(wasm) == .zero;
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 // locals
2048 try writer.writeByte(0);
1934 pub fn alignment(id: DataSegmentId, wasm: *const Wasm) Alignment {
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 feature
2051 if (wasm.data_segments.getIndex(".tdata")) |data_index| {
2052 const segment_index = wasm.data_segments.entries.items(.value)[data_index];
2053 const segment = wasm.segmentPtr(segment_index);
1963 pub fn refCount(id: DataSegmentId, wasm: *const Wasm) u32 {
1964 return switch (unpack(id, wasm)) {
1965 .__zig_error_names => @intCast(wasm.error_name_offs.items.len),
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));
2058 try leb.writeUleb128(writer, param_local);
1991 pub fn isEmpty(id: DataSegmentId, wasm: *const Wasm) bool {
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).?;
2061 try writer.writeByte(std.wasm.opcode(.global_set));
2062 try leb.writeUleb128(writer, wasm.symbolLocSymbol(tls_base_loc).index);
2001 .object => |i| i.ptr(wasm).payload.off == .none,
2002 inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.off == .none,
2003 };
2004 }
20632005
2064 // load stack values for the bulk-memory operation
2065 {
2066 try writer.writeByte(std.wasm.opcode(.local_get));
2067 try leb.writeUleb128(writer, param_local);
2006 pub fn size(id: DataSegmentId, wasm: *const Wasm) u32 {
2007 return switch (unpack(id, wasm)) {
2008 .__zig_error_names => @intCast(wasm.error_name_bytes.items.len),
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));
2070 try leb.writeUleb128(writer, @as(u32, 0)); //segment offset
2031pub const DataLoc = struct {
2032 segment: Wasm.DataSegmentId,
2033 offset: u32,
20712034
2072 try writer.writeByte(std.wasm.opcode(.i32_const));
2073 try leb.writeUleb128(writer, @as(u32, segment.size)); //segment offset
2074 }
2035 pub fn fromObjectDataIndex(wasm: *const Wasm, i: Wasm.ObjectData.Index) DataLoc {
2036 const ptr = i.ptr(wasm);
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 segment
2077 try writer.writeByte(std.wasm.opcode(.misc_prefix));
2078 try leb.writeUleb128(writer, std.wasm.miscOpcode(.memory_init));
2079 // segment immediate
2080 try leb.writeUleb128(writer, @as(u32, @intCast(data_index)));
2081 // memory index immediate (always 0)
2082 try leb.writeUleb128(writer, @as(u32, 0));
2043 pub fn fromDataImportId(wasm: *const Wasm, id: Wasm.DataImportId) DataLoc {
2044 return switch (id.unpack(wasm)) {
2045 .object_data_import => |i| .fromObjectDataImportIndex(wasm, i),
2046 .zcu_import => |i| .fromZcuImport(wasm, i),
2047 };
20832048 }
20842049
2085 // If we have to perform any TLS relocations, call the corresponding function
2086 // which performs all runtime TLS relocations. This is a synthetic function,
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();
2050 pub fn fromObjectDataImportIndex(wasm: *const Wasm, i: Wasm.ObjectDataImport.Index) DataLoc {
2051 return i.value(wasm).resolution.dataLoc(wasm);
20922052 }
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(
2097 wasm.preloaded_strings.__wasm_init_tls,
2098 std.wasm.Type{ .params = &.{.i32}, .returns = &.{} },
2099 &function_body,
2100 );
2101}
2063/// Index into `Wasm.uavs`.
2064pub const UavIndex = enum(u32) {
2065 _,
2066};
21022067
2103fn setupImports(wasm: *Wasm) !void {
2104 const gpa = wasm.base.comp.gpa;
2105 log.debug("Merging imports", .{});
2106 for (wasm.resolved_symbols.keys()) |symbol_loc| {
2107 const object_id = symbol_loc.file.unwrap() orelse {
2108 // Synthetic symbols will already exist in the `import` section
2109 continue;
2110 };
2068pub const CustomSegment = extern struct {
2069 payload: Payload,
2070 flags: SymbolFlags,
2071 section_name: String,
21112072
2112 const symbol = wasm.symbolLocSymbol(symbol_loc);
2113 if (symbol.isDead()) continue;
2114 if (!symbol.requiresImport()) continue;
2115 if (symbol.name == wasm.preloaded_strings.__indirect_function_table) continue;
2073 pub const Payload = DataPayload;
2074};
2075
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)});
2118 const import = objectImport(wasm, object_id, symbol_loc.index);
2080 pub const end = @intFromEnum(std.wasm.Opcode.end);
21192081
2120 // We copy the import to a new import to ensure the names contain references
2121 // to the internal string table, rather than of the object file.
2122 const new_imp: Import = .{
2123 .module_name = import.module_name,
2124 .name = import.name,
2125 .kind = import.kind,
2082 pub fn slice(index: Expr, wasm: *const Wasm) [:end]const u8 {
2083 const start_slice = wasm.string_bytes.items[@intFromEnum(index)..];
2084 const end_pos = Object.exprEndPos(start_slice, 0) catch |err| switch (err) {
2085 error.InvalidInitOpcode => unreachable,
21262086 };
2127 // TODO: De-duplicate imports when they contain the same names and type
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 }
2087 return start_slice[0..end_pos :end];
21542088 }
2155 wasm.imported_functions_count = function_index;
2156 wasm.imported_globals_count = global_index;
2157 wasm.imported_tables_count = table_index;
2089};
21582090
2159 log.debug("Merged ({d}) functions, ({d}) globals, and ({d}) tables into import section", .{
2160 function_index,
2161 global_index,
2162 table_index,
2163 });
2164}
2091pub const FunctionType = extern struct {
2092 params: ValtypeList,
2093 returns: ValtypeList,
21652094
2166/// Takes the global, function and table section from each linked object file
2167/// and merges it into a single section for each.
2168fn mergeSections(wasm: *Wasm) !void {
2169 const gpa = wasm.base.comp.gpa;
2095 /// Index into func_types
2096 pub const Index = enum(u32) {
2097 _,
21702098
2171 var removed_duplicates = std.ArrayList(SymbolLoc).init(gpa);
2172 defer removed_duplicates.deinit();
2099 pub fn ptr(i: Index, wasm: *const Wasm) *FunctionType {
2100 return &wasm.func_types.keys()[@intFromEnum(i)];
2101 }
21732102
2174 for (wasm.resolved_symbols.keys()) |sym_loc| {
2175 const object_id = sym_loc.file.unwrap() orelse {
2176 // Synthetic symbols already live in the corresponding sections.
2177 continue;
2178 };
2103 pub fn fmt(i: Index, wasm: *const Wasm) Formatter {
2104 return i.ptr(wasm).fmt(wasm);
2105 }
2106 };
21792107
2180 const symbol = objectSymbol(wasm, object_id, sym_loc.index);
2181 if (symbol.isDead() or symbol.isUndefined()) {
2182 // Skip undefined symbols as they go in the `import` section
2183 continue;
2184 }
2185
2186 switch (symbol.tag) {
2187 .function => {
2188 const gop = try wasm.functions.getOrPut(
2189 gpa,
2190 .{ .file = sym_loc.file, .index = symbol.index },
2191 );
2192 if (gop.found_existing) {
2193 // We found an alias to the same function, discard this symbol in favor of
2194 // the original symbol and point the discard function to it. This ensures
2195 // we only emit a single function, instead of duplicates.
2196 // we favor keeping the global over a local.
2197 const original_loc: SymbolLoc = .{ .file = gop.key_ptr.file, .index = gop.value_ptr.sym_index };
2198 const original_sym = wasm.symbolLocSymbol(original_loc);
2199 if (original_sym.isLocal() and symbol.isGlobal()) {
2200 original_sym.unmark();
2201 try wasm.discarded.put(gpa, original_loc, sym_loc);
2202 try removed_duplicates.append(original_loc);
2203 } else {
2204 symbol.unmark();
2205 try wasm.discarded.putNoClobber(gpa, sym_loc, original_loc);
2206 try removed_duplicates.append(sym_loc);
2207 continue;
2108 pub const format = @compileError("can't format without *Wasm reference");
2109
2110 pub fn eql(a: FunctionType, b: FunctionType) bool {
2111 return a.params == b.params and a.returns == b.returns;
2112 }
2113
2114 pub fn fmt(ft: FunctionType, wasm: *const Wasm) Formatter {
2115 return .{ .wasm = wasm, .ft = ft };
2116 }
2117
2118 const Formatter = struct {
2119 wasm: *const Wasm,
2120 ft: FunctionType,
2121
2122 pub fn format(
2123 self: Formatter,
2124 comptime format_string: []const u8,
2125 options: std.fmt.FormatOptions,
2126 writer: anytype,
2127 ) !void {
2128 if (format_string.len != 0) std.fmt.invalidFmtError(format_string, self);
2129 _ = options;
2130 const params = self.ft.params.slice(self.wasm);
2131 const returns = self.ft.returns.slice(self.wasm);
2132
2133 try writer.writeByte('(');
2134 for (params, 0..) |param, i| {
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(", ");
22082148 }
22092149 }
2210 gop.value_ptr.* = .{
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 => {},
2150 }
22322151 }
2233 }
2152 };
2153};
22342154
2235 // For any removed duplicates, remove them from the resolved symbols list
2236 for (removed_duplicates.items) |sym_loc| {
2237 assert(wasm.resolved_symbols.swapRemove(sym_loc));
2238 gc_log.debug("Removed duplicate for function '{s}'", .{wasm.symbolLocName(sym_loc)});
2239 }
2155/// Represents a function entry, holding the index to its type
2156pub const Func = extern struct {
2157 type_index: FunctionType.Index,
2158};
22402159
2241 log.debug("Merged ({d}) functions", .{wasm.functions.count()});
2242 log.debug("Merged ({d}) globals", .{wasm.wasm_globals.items.len});
2243 log.debug("Merged ({d}) tables", .{wasm.tables.items.len});
2244}
2160/// Type reflection is used on the field names to autopopulate each field
2161/// during initialization.
2162const PreloadedStrings = struct {
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 final
2247/// 'types' section, while assigning the type index to the representing
2248/// section (import, export, function).
2249fn mergeTypes(wasm: *Wasm) !void {
2250 const gpa = wasm.base.comp.gpa;
2251 // A map to track which functions have already had their
2252 // type inserted. If we do this for the same function multiple times,
2253 // it will be overwritten with the incorrect type.
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 };
2184/// Index into string_bytes
2185pub const String = enum(u32) {
2186 _,
2187
2188 const Table = std.HashMapUnmanaged(String, void, TableContext, std.hash_map.default_max_load_percentage);
2189
2190 const TableContext = struct {
2191 bytes: []const u8,
22632192
2264 const symbol = objectSymbol(wasm, object_id, sym_loc.index);
2265 if (symbol.tag != .function or symbol.isDead()) {
2266 // Only functions have types. Only retrieve the type of referenced functions.
2267 continue;
2193 pub fn eql(_: @This(), a: String, b: String) bool {
2194 return a == b;
22682195 }
22692196
2270 if (symbol.isUndefined()) {
2271 log.debug("Adding type from extern function '{s}'", .{wasm.symbolLocName(sym_loc)});
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, {});
2197 pub fn hash(ctx: @This(), key: String) u64 {
2198 return std.hash_map.hashString(mem.sliceTo(ctx.bytes[@intFromEnum(key)..], 0));
22802199 }
2281 }
2282 log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len});
2283}
2200 };
22842201
2285fn checkExportNames(wasm: *Wasm) !void {
2286 const force_exp_names = wasm.export_symbol_names;
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 };
2202 const TableIndexAdapter = struct {
2203 bytes: []const u8,
22992204
2300 const symbol = wasm.symbolLocSymbol(loc);
2301 symbol.setFlag(.WASM_SYM_EXPORTED);
2205 pub fn eql(ctx: @This(), a: []const u8, b: String) bool {
2206 return mem.eql(u8, a, mem.sliceTo(ctx.bytes[@intFromEnum(b)..], 0));
23022207 }
23032208
2304 if (failed_exports) {
2305 return error.FlushFailure;
2209 pub fn hash(_: @This(), adapted_key: []const u8) u64 {
2210 assert(mem.indexOfScalar(u8, adapted_key, 0) == null);
2211 return std.hash_map.hashString(adapted_key);
23062212 }
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];
23072218 }
2308}
23092219
2310fn setupExports(wasm: *Wasm) !void {
2311 const comp = wasm.base.comp;
2312 const gpa = comp.gpa;
2313 if (comp.config.output_mode == .Obj) return;
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);
2220 pub fn toOptional(i: String) OptionalString {
2221 const result: OptionalString = @enumFromInt(@intFromEnum(i));
2222 assert(result != .none);
2223 return result;
23422224 }
2225};
23432226
2344 log.debug("Completed building exports. Total count: ({d})", .{wasm.exports.items.len});
2345}
2227pub const OptionalString = enum(u32) {
2228 none = std.math.maxInt(u32),
2229 _,
23462230
2347fn setupStart(wasm: *Wasm) !void {
2348 const comp = wasm.base.comp;
2349 const diags = &wasm.base.comp.link_diags;
2350 // do not export entry point if user set none or no default was set.
2351 const entry_name = wasm.entry_name.unwrap() orelse return;
2352
2353 const symbol_loc = wasm.globals.get(entry_name) orelse {
2354 var err = try diags.addErrorWithNotes(1);
2355 try err.addMsg("entry symbol '{s}' missing", .{wasm.stringSlice(entry_name)});
2356 try err.addNote("'-fno-entry' suppresses this error", .{});
2357 return error.LinkFailure;
2358 };
2231 pub fn unwrap(i: OptionalString) ?String {
2232 if (i == .none) return null;
2233 return @enumFromInt(@intFromEnum(i));
2234 }
2235
2236 pub fn slice(index: OptionalString, wasm: *const Wasm) ?[:0]const u8 {
2237 return (index.unwrap() orelse return null).slice(wasm);
2238 }
2239};
23592240
2360 const symbol = wasm.symbolLocSymbol(symbol_loc);
2361 if (symbol.tag != .function)
2362 return diags.fail("entry symbol '{s}' is not a function", .{wasm.stringSlice(entry_name)});
2241/// Stored identically to `String`. The bytes are reinterpreted as
2242/// `std.wasm.Valtype` elements.
2243pub const ValtypeList = enum(u32) {
2244 _,
23632245
2364 // Ensure the symbol is exported so host environment can access it
2365 if (comp.config.output_mode != .Obj) {
2366 symbol.setFlag(.WASM_SYM_EXPORTED);
2246 pub fn fromString(s: String) ValtypeList {
2247 return @enumFromInt(@intFromEnum(s));
23672248 }
2368}
23692249
2370/// Sets up the memory section of the wasm module, as well as the stack.
2371fn setupMemory(wasm: *Wasm) !void {
2372 const comp = wasm.base.comp;
2373 const diags = &wasm.base.comp.link_diags;
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;
2250 pub fn slice(index: ValtypeList, wasm: *const Wasm) []const std.wasm.Valtype {
2251 return @ptrCast(String.slice(@enumFromInt(@intFromEnum(index)), wasm));
2252 }
2253};
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: {
2391 const sym = wasm.symbolLocSymbol(loc);
2392 break :index sym.index - wasm.imported_globals_count;
2393 } else null;
2259 pub fn ptr(index: ZcuImportIndex, wasm: *const Wasm) *InternPool.Nav.Index {
2260 return &wasm.imports.keys()[@intFromEnum(index)];
2261 }
23942262
2395 if (place_stack_first and !is_obj) {
2396 memory_ptr = stack_alignment.forward(memory_ptr);
2397 memory_ptr += wasm.base.stack_size;
2398 // We always put the stack pointer global at index 0
2399 if (stack_ptr) |index| {
2400 wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
2401 }
2263 pub fn importName(index: ZcuImportIndex, wasm: *const Wasm) String {
2264 const zcu = wasm.base.comp.zcu.?;
2265 const ip = &zcu.intern_pool;
2266 const nav_index = index.ptr(wasm).*;
2267 const ext = ip.getNav(nav_index).getResolvedExtern(ip).?;
2268 const name_slice = ext.name.toSlice(ip);
2269 return wasm.getExistingString(name_slice).?;
24022270 }
24032271
2404 var offset: u32 = @as(u32, @intCast(memory_ptr));
2405 var data_seg_it = wasm.data_segments.iterator();
2406 while (data_seg_it.next()) |entry| {
2407 const segment = wasm.segmentPtr(entry.value_ptr.*);
2408 memory_ptr = segment.alignment.forward(memory_ptr);
2272 pub fn moduleName(index: ZcuImportIndex, wasm: *const Wasm) OptionalString {
2273 const zcu = wasm.base.comp.zcu.?;
2274 const ip = &zcu.intern_pool;
2275 const nav_index = index.ptr(wasm).*;
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 symbols
2411 if (mem.eql(u8, entry.key_ptr.*, ".tdata")) {
2412 if (wasm.globals.get(wasm.preloaded_strings.__tls_size)) |loc| {
2413 const sym = wasm.symbolLocSymbol(loc);
2414 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.size);
2415 }
2416 if (wasm.globals.get(wasm.preloaded_strings.__tls_align)) |loc| {
2417 const sym = wasm.symbolLocSymbol(loc);
2418 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.alignment.toByteUnits().?);
2419 }
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 }
2281 pub fn functionType(index: ZcuImportIndex, wasm: *Wasm) FunctionType.Index {
2282 const comp = wasm.base.comp;
2283 const target = &comp.root_mod.resolved_target.result;
2284 const zcu = comp.zcu.?;
2285 const ip = &zcu.intern_pool;
2286 const nav_index = index.ptr(wasm).*;
2287 const ext = ip.getNav(nav_index).getResolvedExtern(ip).?;
2288 const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?;
2289 return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?;
2290 }
24282291
2429 memory_ptr += segment.size;
2430 segment.offset = offset;
2431 offset += segment.size;
2292 pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) ObjectGlobal.Type {
2293 _ = index;
2294 _ = wasm;
2295 unreachable; // Zig has no way to create Wasm globals yet.
24322296 }
2297};
24332298
2434 // create the memory init flag which is used by the init memory function
2435 if (shared_memory and wasm.hasPassiveInitializationSegments()) {
2436 // align to pointer size
2437 memory_ptr = mem.alignForward(u64, memory_ptr, 4);
2438 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_memory_flag, .data);
2439 const sym = wasm.symbolLocSymbol(loc);
2440 sym.mark();
2441 sym.virtual_address = @as(u32, @intCast(memory_ptr));
2442 memory_ptr += 4;
2299/// 0. Index into `Wasm.object_function_imports`.
2300/// 1. Index into `Wasm.imports`.
2301pub const FunctionImportId = enum(u32) {
2302 _,
2303
2304 pub const Unpacked = union(enum) {
2305 object_function_import: FunctionImport.Index,
2306 zcu_import: ZcuImportIndex,
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 };
24432314 }
24442315
2445 if (!place_stack_first and !is_obj) {
2446 memory_ptr = stack_alignment.forward(memory_ptr);
2447 memory_ptr += wasm.base.stack_size;
2448 if (stack_ptr) |index| {
2449 wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
2450 }
2316 pub fn unpack(id: FunctionImportId, wasm: *const Wasm) Unpacked {
2317 const i = @intFromEnum(id);
2318 if (i < wasm.object_function_imports.entries.len) return .{ .object_function_import = @enumFromInt(i) };
2319 const zcu_import_i = i - wasm.object_function_imports.entries.len;
2320 return .{ .zcu_import = @enumFromInt(zcu_import_i) };
24512321 }
24522322
2453 // One of the linked object files has a reference to the __heap_base symbol.
2454 // We must set its virtual address so it can be used in relocations.
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));
2323 pub fn fromObject(function_import_index: FunctionImport.Index, wasm: *const Wasm) FunctionImportId {
2324 return pack(.{ .object_function_import = function_import_index }, wasm);
24582325 }
24592326
2460 // Setup the max amount of pages
2461 // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1
2462 const max_memory_allowed: u64 = (1 << 32) - 1;
2327 pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) FunctionImportId {
2328 return pack(.{ .zcu_import = zcu_import }, wasm);
2329 }
24632330
2464 if (wasm.initial_memory) |initial_memory| {
2465 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
2466 var err = try diags.addErrorWithNotes(0);
2467 try err.addMsg("Initial memory must be {d}-byte aligned", .{page_size});
2468 }
2469 if (memory_ptr > initial_memory) {
2470 var err = try diags.addErrorWithNotes(0);
2471 try err.addMsg("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
2472 }
2473 if (initial_memory > max_memory_allowed) {
2474 var err = try diags.addErrorWithNotes(0);
2475 try err.addMsg("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
2331 /// This function is allowed O(N) lookup because it is only called during
2332 /// diagnostic generation.
2333 pub fn sourceLocation(id: FunctionImportId, wasm: *const Wasm) SourceLocation {
2334 switch (id.unpack(wasm)) {
2335 .object_function_import => |obj_func_index| {
2336 // TODO binary search
2337 for (wasm.objects.items, 0..) |o, i| {
2338 if (o.function_imports.off <= @intFromEnum(obj_func_index) and
2339 o.function_imports.off + o.function_imports.len > @intFromEnum(obj_func_index))
2340 {
2341 return .pack(.{ .object_index = @enumFromInt(i) }, wasm);
2342 }
2343 } else unreachable;
2344 },
2345 .zcu_import => return .zig_object_nofile, // TODO give a better source location
24762346 }
2477 memory_ptr = initial_memory;
24782347 }
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| {
2486 const symbol = wasm.symbolLocSymbol(loc);
2487 symbol.virtual_address = @as(u32, @intCast(memory_ptr));
2349 pub fn importName(id: FunctionImportId, wasm: *const Wasm) String {
2350 return switch (unpack(id, wasm)) {
2351 inline .object_function_import, .zcu_import => |i| i.importName(wasm),
2352 };
24882353 }
24892354
2490 if (wasm.max_memory) |max_memory| {
2491 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
2492 var err = try diags.addErrorWithNotes(0);
2493 try err.addMsg("Maximum memory must be {d}-byte aligned", .{page_size});
2494 }
2495 if (memory_ptr > max_memory) {
2496 var err = try diags.addErrorWithNotes(0);
2497 try err.addMsg("Maximum memory too small, must be at least {d} bytes", .{memory_ptr});
2498 }
2499 if (max_memory > max_memory_allowed) {
2500 var err = try diags.addErrorWithNotes(0);
2501 try err.addMsg("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed});
2502 }
2503 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
2504 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);
2505 if (shared_memory) {
2506 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_IS_SHARED);
2355 pub fn moduleName(id: FunctionImportId, wasm: *const Wasm) OptionalString {
2356 return switch (unpack(id, wasm)) {
2357 inline .object_function_import, .zcu_import => |i| i.moduleName(wasm),
2358 };
2359 }
2360
2361 pub fn functionType(id: FunctionImportId, wasm: *Wasm) FunctionType.Index {
2362 return switch (unpack(id, wasm)) {
2363 inline .object_function_import, .zcu_import => |i| i.functionType(wasm),
2364 };
2365 }
2366
2367 /// Asserts not emitting an object, and `Wasm.import_symbols` is false.
2368 pub fn undefinedAllowed(id: FunctionImportId, wasm: *const Wasm) bool {
2369 assert(!wasm.import_symbols);
2370 assert(wasm.base.comp.config.output_mode != .Obj);
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
25072429 }
2508 log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max});
25092430 }
2510}
25112431
2512/// From a given object's index and the index of the segment, returns the corresponding
2513/// index of the segment within the final data section. When the segment does not yet
2514/// exist, a new one will be initialized and appended. The new index will be returned in that case.
2515pub fn getMatchingSegment(wasm: *Wasm, object_id: ObjectId, symbol_index: Symbol.Index) !Segment.Index {
2516 const comp = wasm.base.comp;
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;
2432 pub fn importName(id: GlobalImportId, wasm: *const Wasm) String {
2433 return switch (unpack(id, wasm)) {
2434 inline .object_global_import, .zcu_import => |i| i.importName(wasm),
2435 };
2436 }
25222437
2523 switch (symbol.tag) {
2524 .data => {
2525 const segment_info = objectSegmentInfo(wasm, object_id)[symbol.index];
2526 const merge_segment = comp.config.output_mode != .Obj;
2527 const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment));
2528 if (!result.found_existing) {
2529 result.value_ptr.* = index;
2530 var flags: u32 = 0;
2531 if (shared_memory) {
2532 flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
2533 }
2534 try wasm.segments.append(gpa, .{
2535 .alignment = .@"1",
2536 .size = 0,
2537 .offset = 0,
2538 .flags = flags,
2539 });
2540 try wasm.segment_info.putNoClobber(gpa, index, .{
2541 .name = try gpa.dupe(u8, segment_info.name),
2542 .alignment = segment_info.alignment,
2543 .flags = segment_info.flags,
2544 });
2545 return index;
2546 } else return result.value_ptr.*;
2547 },
2548 .function => return wasm.code_section_index.unwrap() orelse blk: {
2549 wasm.code_section_index = index.toOptional();
2550 try wasm.appendDummySegment();
2551 break :blk index;
2552 },
2553 .section => {
2554 const section_name = wasm.objectSymbol(object_id, symbol_index).name;
2555
2556 inline for (@typeInfo(CustomSections).@"struct".fields) |field| {
2557 if (@field(wasm.custom_sections, field.name).name == section_name) {
2558 const field_ptr = &@field(wasm.custom_sections, field.name).index;
2559 return field_ptr.unwrap() orelse {
2560 field_ptr.* = index.toOptional();
2561 try wasm.appendDummySegment();
2562 return index;
2563 };
2564 }
2565 } else {
2566 return diags.failParse(objectPath(wasm, object_id), "unknown section: {s}", .{
2567 wasm.stringSlice(section_name),
2568 });
2569 }
2438 pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) OptionalString {
2439 return switch (unpack(id, wasm)) {
2440 inline .object_global_import, .zcu_import => |i| i.moduleName(wasm),
2441 };
2442 }
2443
2444 pub fn globalType(id: GlobalImportId, wasm: *Wasm) ObjectGlobal.Type {
2445 return switch (unpack(id, wasm)) {
2446 inline .object_global_import, .zcu_import => |i| i.globalType(wasm),
2447 };
2448 }
2449};
2450
2451/// 0. Index into `Wasm.object_data_imports`.
2452/// 1. Index into `Wasm.imports`.
2453pub const DataImportId = enum(u32) {
2454 _,
2455
2456 pub const Unpacked = union(enum) {
2457 object_data_import: ObjectDataImport.Index,
2458 zcu_import: ZcuImportIndex,
2459 };
2460
2461 pub fn pack(unpacked: Unpacked, wasm: *const Wasm) DataImportId {
2462 return switch (unpacked) {
2463 .object_data_import => |i| @enumFromInt(@intFromEnum(i)),
2464 .zcu_import => |i| @enumFromInt(@intFromEnum(i) + wasm.object_data_imports.entries.len),
2465 };
2466 }
2467
2468 pub fn unpack(id: DataImportId, wasm: *const Wasm) Unpacked {
2469 const i = @intFromEnum(id);
2470 if (i < wasm.object_data_imports.entries.len) return .{ .object_data_import = @enumFromInt(i) };
2471 const zcu_import_i = i - wasm.object_data_imports.entries.len;
2472 return .{ .zcu_import = @enumFromInt(zcu_import_i) };
2473 }
2474
2475 pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) DataImportId {
2476 return pack(.{ .zcu_import = zcu_import }, wasm);
2477 }
2478
2479 pub fn fromObject(object_data_import: ObjectDataImport.Index, wasm: *const Wasm) DataImportId {
2480 return pack(.{ .object_data_import = object_data_import }, wasm);
2481 }
2482
2483 pub fn sourceLocation(id: DataImportId, wasm: *const Wasm) SourceLocation {
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 }));
25703280 },
2571 else => unreachable,
3281 .uav => |uav_index| assert(wasm.uav_exports.swapRemove(.{ .uav_index = uav_index, .name = export_name })),
25723282 }
25733283}
25743284
2575/// Appends a new segment with default field values
2576fn appendDummySegment(wasm: *Wasm) !void {
2577 const gpa = wasm.base.comp.gpa;
2578 try wasm.segments.append(gpa, .{
2579 .alignment = .@"1",
2580 .size = 0,
2581 .offset = 0,
2582 .flags = 0,
2583 });
3285pub fn updateExports(
3286 wasm: *Wasm,
3287 pt: Zcu.PerThread,
3288 exported: Zcu.Exported,
3289 export_indices: []const Zcu.Export.Index,
3290) !void {
3291 if (build_options.skip_non_native and builtin.object_format != .wasm) {
3292 @panic("Attempted to compile for object format that was disabled by build configuration");
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 }
25843311}
25853312
25863313pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
......@@ -2596,7 +3323,9 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
25963323 .res => unreachable,
25973324 .dso_exact => unreachable,
25983325 .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 },
26003329 }
26013330 }
26023331
......@@ -2612,791 +3341,472 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
26123341pub fn flush(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
26133342 const comp = wasm.base.comp;
26143343 const use_lld = build_options.have_llvm and comp.config.use_lld;
3344 const diags = &comp.link_diags;
26153345
26163346 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 };
26183352 }
26193353 return wasm.flushModule(arena, tid, prog_node);
26203354}
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 {
26233357 const tracy = trace(@src());
26243358 defer tracy.end();
26253359
2626 const comp = wasm.base.comp;
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);
3360 const sub_prog_node = prog_node.start("Wasm Prelink", 0);
26373361 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 {
26993363 const comp = wasm.base.comp;
2700 const diags = &comp.link_diags;
27013364 const gpa = comp.gpa;
2702 const use_llvm = comp.config.use_llvm;
2703 const use_lld = build_options.have_llvm and comp.config.use_lld;
2704 const shared_memory = comp.config.shared_memory;
2705 const import_memory = comp.config.import_memory;
2706 const export_memory = comp.config.export_memory;
3365 const rdynamic = comp.config.rdynamic;
3366 const is_obj = comp.config.output_mode == .Obj;
27073367
2708 // Size of each section header
2709 const header_size = 5 + 1;
2710 // The amount of sections that will be written
2711 var section_count: u32 = 0;
2712 // Index of the code section. Used to tell relocation table where the section lives.
2713 var code_section_index: ?u32 = null;
2714 // Index of the data section. Used to tell relocation table where the section lives.
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));
3368 assert(wasm.missing_exports.entries.len == 0);
3369 for (wasm.export_symbol_names) |exp_name| {
3370 const exp_name_interned = try wasm.internString(exp_name);
3371 if (wasm.object_function_imports.getPtr(exp_name_interned)) |import| {
3372 if (import.resolution != .unresolved) {
3373 import.flags.exported = true;
3374 continue;
27383375 }
2739 try leb.writeUleb128(binary_writer, @as(u32, @intCast(func_type.returns.len)));
2740 for (func_type.returns) |ret_ty| {
2741 try leb.writeUleb128(binary_writer, std.wasm.valtype(ret_ty));
3376 }
3377 if (wasm.object_global_imports.getPtr(exp_name_interned)) |import| {
3378 if (import.resolution != .unresolved) {
3379 import.flags.exported = true;
3380 continue;
27423381 }
27433382 }
2744
2745 try writeVecSectionHeader(
2746 binary_bytes.items,
2747 header_offset,
2748 .type,
2749 @intCast(binary_bytes.items.len - header_offset - header_size),
2750 @intCast(wasm.func_types.items.len),
2751 );
2752 section_count += 1;
3383 if (wasm.object_table_imports.getPtr(exp_name_interned)) |import| {
3384 if (import.resolution != .unresolved) {
3385 import.flags.exported = true;
3386 continue;
3387 }
3388 }
3389 try wasm.missing_exports.put(gpa, exp_name_interned, {});
27533390 }
27543391
2755 // Import section
2756 if (wasm.imports.count() != 0 or import_memory) {
2757 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2758
2759 var it = wasm.imports.iterator();
2760 while (it.next()) |entry| {
2761 assert(wasm.symbolLocSymbol(entry.key_ptr.*).isUndefined());
2762 const import = entry.value_ptr.*;
2763 try wasm.emitImport(binary_writer, import);
3392 if (wasm.entry_name.unwrap()) |entry_name| {
3393 if (wasm.object_function_imports.getPtr(entry_name)) |import| {
3394 if (import.resolution != .unresolved) {
3395 import.flags.exported = true;
3396 wasm.entry_resolution = import.resolution;
3397 }
27643398 }
3399 }
27653400
2766 if (import_memory) {
2767 const mem_imp: Import = .{
2768 .module_name = wasm.host_name,
2769 .name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory,
2770 .kind = .{ .memory = wasm.memories.limits },
2771 };
2772 try wasm.emitImport(binary_writer, mem_imp);
3401 if (comp.zcu != null) {
3402 // Zig always depends on a stack pointer global.
3403 // If emitting an object, it's an import. Otherwise, the linker synthesizes it.
3404 if (is_obj) {
3405 @panic("TODO");
3406 } else {
3407 try wasm.globals.put(gpa, .__stack_pointer, {});
3408 assert(wasm.globals.entries.len - 1 == @intFromEnum(GlobalIndex.stack_pointer));
27733409 }
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;
27833410 }
27843411
2785 // Function section
2786 if (wasm.functions.count() != 0) {
2787 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2788 for (wasm.functions.values()) |function| {
2789 try leb.writeUleb128(binary_writer, function.func.type_index);
3412 // These loops do both recursive marking of alive symbols well as checking for undefined symbols.
3413 // At the end, output functions and globals will be populated.
3414 for (wasm.object_function_imports.keys(), wasm.object_function_imports.values(), 0..) |name, *import, i| {
3415 if (import.flags.isIncluded(rdynamic)) {
3416 try markFunctionImport(wasm, name, import, @enumFromInt(i));
27903417 }
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;
28003418 }
2801
2802 // Table section
2803 if (wasm.tables.items.len > 0) {
2804 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2805
2806 for (wasm.tables.items) |table| {
2807 try leb.writeUleb128(binary_writer, std.wasm.reftype(table.reftype));
2808 try emitLimits(binary_writer, table.limits);
3419 // Also treat init functions as roots.
3420 for (wasm.object_init_funcs.items) |init_func| {
3421 const func = init_func.function_index.ptr(wasm);
3422 if (func.object_index.ptr(wasm).is_included) {
3423 try markFunction(wasm, init_func.function_index, false);
28093424 }
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;
28193425 }
3426 wasm.functions_end_prelink = @intCast(wasm.functions.entries.len);
28203427
2821 // Memory section
2822 if (!import_memory) {
2823 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2824
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;
3428 for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| {
3429 if (import.flags.isIncluded(rdynamic)) {
3430 try markGlobalImport(wasm, name, import, @enumFromInt(i));
3431 }
28343432 }
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)
2837 if (wasm.wasm_globals.items.len > 0) {
2838 const header_offset = try reserveVecSectionHeader(&binary_bytes);
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);
3436 for (wasm.object_table_imports.keys(), wasm.object_table_imports.values(), 0..) |name, *import, i| {
3437 if (import.flags.isIncluded(rdynamic)) {
3438 try markTableImport(wasm, name, import, @enumFromInt(i));
28443439 }
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;
28543440 }
28553441
2856 // Export section
2857 if (wasm.exports.items.len != 0 or export_memory) {
2858 const header_offset = try reserveVecSectionHeader(&binary_bytes);
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);
3442 for (wasm.object_data_imports.keys(), wasm.object_data_imports.values(), 0..) |name, *import, i| {
3443 if (import.flags.isIncluded(rdynamic)) {
3444 try markDataImport(wasm, name, import, @enumFromInt(i));
28663445 }
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;
28833446 }
28843447
2885 if (wasm.entry) |entry_index| {
2886 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2887 try writeVecSectionHeader(
2888 binary_bytes.items,
2889 header_offset,
2890 .start,
2891 @intCast(binary_bytes.items.len - header_offset - header_size),
2892 entry_index,
2893 );
3448 // This is a wild ass guess at how to merge memories, haven't checked yet
3449 // what the proper way to do this is.
3450 for (wasm.object_memory_imports.values()) |*memory_import| {
3451 wasm.memories.limits.min = @min(wasm.memories.limits.min, memory_import.limits_min);
3452 wasm.memories.limits.max = @max(wasm.memories.limits.max, memory_import.limits_max);
3453 wasm.memories.limits.flags.has_max = wasm.memories.limits.flags.has_max or memory_import.limits_has_max;
28943454 }
28953455
2896 // element section (function table)
2897 if (wasm.function_table.count() > 0) {
2898 const header_offset = try reserveVecSectionHeader(&binary_bytes);
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 }
3456 wasm.function_imports_len_prelink = @intCast(wasm.function_imports.entries.len);
3457 wasm.data_imports_len_prelink = @intCast(wasm.data_imports.entries.len);
3458}
29433459
2944 // Code section
2945 if (wasm.code_section_index != .none) {
2946 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2947 const start_offset = binary_bytes.items.len - 5; // minus 5 so start offset is 5 to include entry count
3460pub fn markFunctionImport(
3461 wasm: *Wasm,
3462 name: String,
3463 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();
2950 while (func_it.next()) |entry| {
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);
3469 const comp = wasm.base.comp;
3470 const gpa = comp.gpa;
29543471
2955 if (!is_obj) {
2956 atom.resolveRelocs(wasm);
2957 }
2958 atom.offset = @intCast(binary_bytes.items.len - start_offset);
2959 try leb.writeUleb128(binary_writer, atom.size);
2960 try binary_writer.writeAll(atom.code.items);
3472 try wasm.functions.ensureUnusedCapacity(gpa, 1);
3473
3474 if (import.resolution == .unresolved) {
3475 if (name == wasm.preloaded_strings.__wasm_init_memory) {
3476 try wasm.resolveFunctionSynthetic(import, .__wasm_init_memory, &.{}, &.{});
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));
29613485 }
3486 } else {
3487 try markFunction(wasm, import.resolution.unpack(wasm).object_function, import.flags.exported);
3488 }
3489}
29623490
2963 try writeVecSectionHeader(
2964 binary_bytes.items,
2965 header_offset,
2966 .code,
2967 @intCast(binary_bytes.items.len - header_offset - header_size),
2968 @intCast(wasm.functions.count()),
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 }
3491/// Recursively mark alive everything referenced by the function.
3492fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex, override_export: bool) link.File.FlushError!void {
3493 const comp = wasm.base.comp;
3494 const gpa = comp.gpa;
3495 const gop = try wasm.functions.getOrPut(gpa, .fromObjectFunction(wasm, i));
3496 if (gop.found_existing) return;
30083497
3009 // Pad with zeroes to ensure all segments are aligned
3010 if (current_offset != atom.offset) {
3011 const diff = atom.offset - current_offset;
3012 try binary_writer.writeByteNTimes(0, diff);
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);
3498 const rdynamic = comp.config.rdynamic;
3499 const is_obj = comp.config.output_mode == .Obj;
3500 const function = i.ptr(wasm);
3501 markObject(wasm, function.object_index);
30183502
3019 current_offset += atom.size;
3020 if (atom.prev != .null) {
3021 atom_index = atom.prev;
3022 } else {
3023 // also pad with zeroes when last atom to ensure
3024 // segments are aligned.
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);
3503 if (!is_obj and (override_export or function.flags.isExported(rdynamic))) {
3504 const symbol_name = function.name.unwrap().?;
3505 if (!override_export and function.flags.visibility_hidden) {
3506 try wasm.hidden_function_exports.put(gpa, symbol_name, @enumFromInt(gop.index));
3507 } else {
3508 try wasm.function_exports.put(gpa, symbol_name, @enumFromInt(gop.index));
30333509 }
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;
30443510 }
30453511
3046 if (is_obj) {
3047 // relocations need to point to the index of a symbol in the final symbol table. To save memory,
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 }
3512 try wasm.markRelocations(function.relocations(wasm));
3513}
30923514
3093 var debug_bytes = std.ArrayList(u8).init(gpa);
3094 defer debug_bytes.deinit();
3515fn markObject(wasm: *Wasm, i: ObjectIndex) void {
3516 i.ptr(wasm).is_included = true;
3517}
30953518
3096 inline for (@typeInfo(CustomSections).@"struct".fields) |field| {
3097 if (@field(wasm.custom_sections, field.name).index.unwrap()) |index| {
3098 var atom = wasm.getAtomPtr(wasm.atoms.get(index).?);
3099 while (true) {
3100 atom.resolveRelocs(wasm);
3101 try debug_bytes.appendSlice(atom.code.items);
3102 if (atom.prev == .null) break;
3103 atom = wasm.getAtomPtr(atom.prev);
3104 }
3105 try emitDebugSection(&binary_bytes, debug_bytes.items, field.name);
3106 debug_bytes.clearRetainingCapacity();
3107 }
3108 }
3519/// Recursively mark alive everything referenced by the global.
3520fn markGlobalImport(
3521 wasm: *Wasm,
3522 name: String,
3523 import: *GlobalImport,
3524 global_index: GlobalImport.Index,
3525) link.File.FlushError!void {
3526 if (import.flags.alive) return;
3527 import.flags.alive = true;
31093528
3110 try emitProducerSection(&binary_bytes);
3111 if (feature_count > 0) {
3112 try emitFeaturesSection(&binary_bytes, &enabled_features, feature_count);
3113 }
3114 }
3529 const comp = wasm.base.comp;
3530 const gpa = comp.gpa;
31153531
3116 // Only when writing all sections executed properly we write the magic
3117 // bytes. This allows us to easily detect what went wrong while generating
3118 // the final binary.
3119 {
3120 const src = std.wasm.magic ++ std.wasm.version;
3121 binary_bytes.items[0..src.len].* = src;
3532 try wasm.globals.ensureUnusedCapacity(gpa, 1);
3533
3534 if (import.resolution == .unresolved) {
3535 if (name == wasm.preloaded_strings.__heap_base) {
3536 import.resolution = .__heap_base;
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);
31223558 }
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 );
31483559}
31493560
3150fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void {
3151 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3561fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex, override_export: bool) link.File.FlushError!void {
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();
3154 const producers = "producers";
3155 try leb.writeUleb128(writer, @as(u32, @intCast(producers.len)));
3156 try writer.writeAll(producers);
3567 const rdynamic = comp.config.rdynamic;
3568 const is_obj = comp.config.output_mode == .Obj;
3569 const global = i.ptr(wasm);
31573570
3158 try leb.writeUleb128(writer, @as(u32, 2)); // 2 fields: Language + processed-by
3571 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 version
3161 var version_buf: [100]u8 = undefined;
3162 const version = try std.fmt.bufPrint(&version_buf, "{}", .{build_options.semver});
3576 try wasm.markRelocations(global.relocations(wasm));
3577}
31633578
3164 // language field
3165 {
3166 const language = "language";
3167 try leb.writeUleb128(writer, @as(u32, @intCast(language.len)));
3168 try writer.writeAll(language);
3579fn markTableImport(
3580 wasm: *Wasm,
3581 name: String,
3582 import: *TableImport,
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)
3171 try leb.writeUleb128(writer, @as(u32, 1));
3588 const comp = wasm.base.comp;
3589 const gpa = comp.gpa;
31723590
3173 // versioned name
3174 {
3175 try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig"
3176 try writer.writeAll("Zig");
3591 try wasm.tables.ensureUnusedCapacity(gpa, 1);
31773592
3178 try leb.writeUleb128(writer, @as(u32, @intCast(version.len)));
3179 try writer.writeAll(version);
3593 if (import.resolution == .unresolved) {
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);
31803599 }
3600 } else {
3601 wasm.tables.putAssumeCapacity(import.resolution, {});
3602 // Tables have no relocations.
31813603 }
3604}
31823605
3183 // processed-by field
3184 {
3185 const processed_by = "processed-by";
3186 try leb.writeUleb128(writer, @as(u32, @intCast(processed_by.len)));
3187 try writer.writeAll(processed_by);
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");
3606fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.File.FlushError!void {
3607 const comp = wasm.base.comp;
3608 const segment = segment_index.ptr(wasm);
3609 if (segment.flags.alive) return;
3610 segment.flags.alive = true;
31963611
3197 try leb.writeUleb128(writer, @as(u32, @intCast(version.len)));
3198 try writer.writeAll(version);
3199 }
3200 }
3612 wasm.any_passive_inits = wasm.any_passive_inits or segment.flags.is_passive or
3613 (comp.config.import_memory and !wasm.isBss(segment.name));
32013614
3202 try writeCustomSectionHeader(
3203 binary_bytes.items,
3204 header_offset,
3205 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3206 );
3615 try wasm.data_segments.put(comp.gpa, .pack(wasm, .{ .object = segment_index }), {});
3616 try wasm.markRelocations(segment.relocations(wasm));
32073617}
32083618
3209fn emitBuildIdSection(binary_bytes: *std.ArrayList(u8), build_id: []const u8) !void {
3210 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3211
3212 const writer = binary_bytes.writer();
3213 const hdr_build_id = "build_id";
3214 try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len)));
3215 try writer.writeAll(hdr_build_id);
3619pub fn markDataImport(
3620 wasm: *Wasm,
3621 name: String,
3622 import: *ObjectDataImport,
3623 data_index: ObjectDataImport.Index,
3624) link.File.FlushError!void {
3625 if (import.flags.alive) return;
3626 import.flags.alive = true;
32163627
3217 try leb.writeUleb128(writer, @as(u32, 1));
3218 try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len)));
3219 try writer.writeAll(build_id);
3628 const comp = wasm.base.comp;
3629 const gpa = comp.gpa;
32203630
3221 try writeCustomSectionHeader(
3222 binary_bytes.items,
3223 header_offset,
3224 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3225 );
3631 if (import.resolution == .unresolved) {
3632 if (name == wasm.preloaded_strings.__heap_base) {
3633 import.resolution = .__heap_base;
3634 wasm.data_segments.putAssumeCapacity(.__heap_base, {});
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 }
32263644}
32273645
3228fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void {
3229 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3646fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.File.FlushError!void {
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();
3232 const target_features = "target_features";
3233 try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len)));
3234 try writer.writeAll(target_features);
3725 .memory_addr_leb,
3726 .memory_addr_sleb,
3727 .memory_addr_i32,
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);
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);
3738 .type_index_leb => continue,
32453739 }
32463740 }
3741}
32473742
3248 try writeCustomSectionHeader(
3249 binary_bytes.items,
3250 header_offset,
3251 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3252 );
3743fn markTable(wasm: *Wasm, i: ObjectTableIndex) link.File.FlushError!void {
3744 try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(i), {});
32533745}
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;
32563757 const comp = wasm.base.comp;
3257 const import_memory = comp.config.import_memory;
3258 const Name = struct {
3259 index: u32,
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 };
3758 const use_lld = build_options.have_llvm and comp.config.use_lld;
3759 const diags = &comp.link_diags;
3760 const gpa = comp.gpa;
32673761
3268 // we must de-duplicate symbols that point to the same function
3269 var funcs = std.AutoArrayHashMap(u32, Name).init(arena);
3270 try funcs.ensureUnusedCapacity(wasm.functions.count() + wasm.imported_functions_count);
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;
3762 if (wasm.llvm_object) |llvm_object| {
3763 try wasm.base.emitLlvmObject(arena, llvm_object, prog_node);
3764 if (use_lld) return;
32993765 }
33003766
3301 mem.sort(Name, funcs.values(), {}, Name.lessThan);
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");
3767 if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items);
33083768
3309 try wasm.emitNameSubsection(.function, funcs.values(), writer);
3310 try wasm.emitNameSubsection(.global, globals.items, writer);
3311 try wasm.emitNameSubsection(.data_segment, segments.items, writer);
3769 if (wasm.base.zcu_object_sub_path) |path| {
3770 const module_obj_path: Path = .{
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(
3314 binary_bytes.items,
3315 header_offset,
3316 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3317 );
3318}
3781 const tracy = trace(@src());
3782 defer tracy.end();
33193783
3320fn emitNameSubsection(wasm: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void {
3321 const gpa = wasm.base.comp.gpa;
3784 const sub_prog_node = prog_node.start("Wasm Flush", 0);
3785 defer sub_prog_node.end();
33223786
3323 // We must emit subsection size, so first write to a temporary list
3324 var section_list = std.ArrayList(u8).init(gpa);
3325 defer section_list.deinit();
3326 const sub_writer = section_list.writer();
3787 const functions_end_zcu: u32 = @intCast(wasm.functions.entries.len);
3788 defer wasm.functions.shrinkRetainingCapacity(functions_end_zcu);
33273789
3328 try leb.writeUleb128(sub_writer, @as(u32, @intCast(names.len)));
3329 for (names) |name| {
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 }
3790 const globals_end_zcu: u32 = @intCast(wasm.globals.entries.len);
3791 defer wasm.globals.shrinkRetainingCapacity(globals_end_zcu);
33353792
3336 // From now, write to the actual writer
3337 try leb.writeUleb128(writer, @intFromEnum(section_id));
3338 try leb.writeUleb128(writer, @as(u32, @intCast(section_list.items.len)));
3339 try writer.writeAll(section_list.items);
3340}
3793 const function_exports_end_zcu: u32 = @intCast(wasm.function_exports.entries.len);
3794 defer wasm.function_exports.shrinkRetainingCapacity(function_exports_end_zcu);
33413795
3342fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void {
3343 try writer.writeByte(limits.flags);
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}
3796 const hidden_function_exports_end_zcu: u32 = @intCast(wasm.hidden_function_exports.entries.len);
3797 defer wasm.hidden_function_exports.shrinkRetainingCapacity(hidden_function_exports_end_zcu);
33493798
3350fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {
3351 switch (init_expr) {
3352 .i32_const => |val| {
3353 try writer.writeByte(std.wasm.opcode(.i32_const));
3354 try leb.writeIleb128(writer, val);
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}
3799 wasm.flush_buffer.clear();
3800 try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{});
3801 try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values());
3802 try wasm.flush_buffer.global_imports.reinit(gpa, wasm.global_imports.keys(), wasm.global_imports.values());
3803 try wasm.flush_buffer.data_imports.reinit(gpa, wasm.data_imports.keys(), wasm.data_imports.values());
33753804
3376fn emitImport(wasm: *Wasm, writer: anytype, import: Import) !void {
3377 const module_name = wasm.stringSlice(import.module_name);
3378 try leb.writeUleb128(writer, @as(u32, @intCast(module_name.len)));
3379 try writer.writeAll(module_name);
3380
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 }
3805 return wasm.flush_buffer.finish(wasm) catch |err| switch (err) {
3806 error.OutOfMemory => return error.OutOfMemory,
3807 error.LinkFailure => return error.LinkFailure,
3808 else => |e| return diags.fail("failed to flush wasm: {s}", .{@errorName(e)}),
3809 };
34003810}
34013811
34023812fn 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:
34063816 defer tracy.end();
34073817
34083818 const comp = wasm.base.comp;
3819 const diags = &comp.link_diags;
34093820 const shared_memory = comp.config.shared_memory;
34103821 const export_memory = comp.config.export_memory;
34113822 const import_memory = comp.config.import_memory;
......@@ -3459,7 +3870,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
34593870 }
34603871 try man.addOptionalFile(module_obj_path);
34613872 try man.addOptionalFilePath(compiler_rt_path);
3462 man.hash.addOptionalBytes(wasm.optionalStringSlice(wasm.entry_name));
3873 man.hash.addOptionalBytes(wasm.entry_name.slice(wasm));
34633874 man.hash.add(wasm.base.stack_size);
34643875 man.hash.add(wasm.base.build_id);
34653876 man.hash.add(import_memory);
......@@ -3608,7 +4019,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
36084019 try argv.append("--export-dynamic");
36094020 }
36104021
3611 if (wasm.optionalStringSlice(wasm.entry_name)) |entry_name| {
4022 if (wasm.entry_name.slice(wasm)) |entry_name| {
36124023 try argv.appendSlice(&.{ "--entry", entry_name });
36134024 } else {
36144025 try argv.append("--no-entry");
......@@ -3750,14 +4161,12 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
37504161 switch (term) {
37514162 .Exited => |code| {
37524163 if (code != 0) {
3753 const diags = &comp.link_diags;
37544164 diags.lockAndParseLldStderr(linker_command, stderr);
3755 return error.LLDReportedFailure;
4165 return error.LinkFailure;
37564166 }
37574167 },
37584168 else => {
3759 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
3760 return error.LLDCrashed;
4169 return diags.fail("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
37614170 },
37624171 }
37634172
......@@ -3771,7 +4180,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
37714180 if (comp.clang_passthrough_mode) {
37724181 std.process.exit(exit_code);
37734182 } else {
3774 return error.LLDReportedFailure;
4183 return diags.fail("{s} returned exit code {d}:\n{s}", .{ argv.items[0], exit_code });
37754184 }
37764185 }
37774186 }
......@@ -3811,969 +4220,507 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
38114220 }
38124221}
38134222
3814fn reserveVecSectionHeader(bytes: *std.ArrayList(u8)) !u32 {
3815 // section id + fixed leb contents size + fixed leb vector length
3816 const header_size = 1 + 5 + 5;
3817 const offset = @as(u32, @intCast(bytes.items.len));
3818 try bytes.appendSlice(&[_]u8{0} ** header_size);
3819 return offset;
4223fn defaultEntrySymbolName(
4224 preloaded_strings: *const PreloadedStrings,
4225 wasi_exec_model: std.builtin.WasiExecModel,
4226) String {
4227 return switch (wasi_exec_model) {
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;
38204261}
38214262
3822fn reserveCustomSectionHeader(bytes: *std.ArrayList(u8)) !u32 {
3823 // unlike regular section, we don't emit the count
3824 const header_size = 1 + 5;
3825 const offset = @as(u32, @intCast(bytes.items.len));
3826 try bytes.appendSlice(&[_]u8{0} ** header_size);
3827 return offset;
4263// TODO implement instead by appending to string_bytes
4264pub fn internStringFmt(wasm: *Wasm, comptime format: []const u8, args: anytype) Allocator.Error!String {
4265 var buffer: [32]u8 = undefined;
4266 const slice = std.fmt.bufPrint(&buffer, format, args) catch unreachable;
4267 return internString(wasm, slice);
38284268}
38294269
3830fn writeVecSectionHeader(buffer: []u8, offset: u32, section: std.wasm.Section, size: u32, items: u32) !void {
3831 var buf: [1 + 5 + 5]u8 = undefined;
3832 buf[0] = @intFromEnum(section);
3833 leb.writeUnsignedFixed(5, buf[1..6], size);
3834 leb.writeUnsignedFixed(5, buf[6..], items);
3835 buffer[offset..][0..buf.len].* = buf;
4270pub fn getExistingString(wasm: *const Wasm, bytes: []const u8) ?String {
4271 assert(mem.indexOfScalar(u8, bytes, 0) == null);
4272 return wasm.string_table.getKeyAdapted(bytes, @as(String.TableIndexAdapter, .{
4273 .bytes = wasm.string_bytes.items,
4274 }));
38364275}
38374276
3838fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void {
3839 var buf: [1 + 5]u8 = undefined;
3840 buf[0] = 0; // 0 = 'custom' section
3841 leb.writeUnsignedFixed(5, buf[1..6], size);
3842 buffer[offset..][0..buf.len].* = buf;
4277pub fn internValtypeList(wasm: *Wasm, valtype_list: []const std.wasm.Valtype) Allocator.Error!ValtypeList {
4278 return .fromString(try internString(wasm, @ptrCast(valtype_list)));
38434279}
38444280
3845fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
3846 const offset = try reserveCustomSectionHeader(binary_bytes);
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);
4281pub fn getExistingValtypeList(wasm: *const Wasm, valtype_list: []const std.wasm.Valtype) ?ValtypeList {
4282 return .fromString(getExistingString(wasm, @ptrCast(valtype_list)) orelse return null);
38634283}
38644284
3865fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
3866 const writer = binary_bytes.writer();
3867
3868 try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SYMBOL_TABLE));
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);
4285pub fn addFuncType(wasm: *Wasm, ft: FunctionType) Allocator.Error!FunctionType.Index {
4286 const gpa = wasm.base.comp.gpa;
4287 const gop = try wasm.func_types.getOrPut(gpa, ft);
4288 return @enumFromInt(gop.index);
39124289}
39134290
3914fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
3915 const writer = binary_bytes.writer();
3916 try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SEGMENT_INFO));
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);
4291pub fn getExistingFuncType(wasm: *const Wasm, ft: FunctionType) ?FunctionType.Index {
4292 const index = wasm.func_types.getIndex(ft) orelse return null;
4293 return @enumFromInt(index);
39354294}
39364295
3937pub fn getUleb128Size(uint_value: anytype) u32 {
3938 const T = @TypeOf(uint_value);
3939 const U = if (@typeInfo(T).int.bits < 8) u8 else T;
3940 var value = @as(U, @intCast(uint_value));
3941
3942 var size: u32 = 0;
3943 while (value != 0) : (size += 1) {
3944 value >>= 7;
3945 }
3946 return size;
4296pub fn getExistingFuncType2(wasm: *const Wasm, params: []const std.wasm.Valtype, returns: []const std.wasm.Valtype) FunctionType.Index {
4297 return getExistingFuncType(wasm, .{
4298 .params = getExistingValtypeList(wasm, params).?,
4299 .returns = getExistingValtypeList(wasm, returns).?,
4300 }).?;
39474301}
39484302
3949/// For each relocatable section, emits a custom "relocation.<section_name>" section
3950fn emitCodeRelocations(
4303pub fn internFunctionType(
39514304 wasm: *Wasm,
3952 binary_bytes: *std.ArrayList(u8),
3953 section_index: u32,
3954 symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),
3955) !void {
3956 const code_index = wasm.code_section_index.unwrap() orelse return;
3957 const writer = binary_bytes.writer();
3958 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3959
3960 // write custom section information
3961 const name = "reloc.CODE";
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);
4305 cc: std.builtin.CallingConvention,
4306 params: []const InternPool.Index,
4307 return_type: Zcu.Type,
4308 target: *const std.Target,
4309) Allocator.Error!FunctionType.Index {
4310 try convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch);
4311 return wasm.addFuncType(.{
4312 .params = try wasm.internValtypeList(wasm.params_scratch.items),
4313 .returns = try wasm.internValtypeList(wasm.returns_scratch.items),
4314 });
39954315}
39964316
3997fn emitDataRelocations(
4317pub fn getExistingFunctionType(
39984318 wasm: *Wasm,
3999 binary_bytes: *std.ArrayList(u8),
4000 section_index: u32,
4001 symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),
4002) !void {
4003 if (wasm.data_segments.count() == 0) return;
4004 const writer = binary_bytes.writer();
4005 const header_offset = try reserveCustomSectionHeader(binary_bytes);
4006
4007 // write custom section information
4008 const name = "reloc.DATA";
4009 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
4010 try writer.writeAll(name);
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);
4319 cc: std.builtin.CallingConvention,
4320 params: []const InternPool.Index,
4321 return_type: Zcu.Type,
4322 target: *const std.Target,
4323) ?FunctionType.Index {
4324 convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch) catch |err| switch (err) {
4325 error.OutOfMemory => return null,
4326 };
4327 return wasm.getExistingFuncType(.{
4328 .params = wasm.getExistingValtypeList(wasm.params_scratch.items) orelse return null,
4329 .returns = wasm.getExistingValtypeList(wasm.returns_scratch.items) orelse return null,
4330 });
40454331}
40464332
4047fn hasPassiveInitializationSegments(wasm: *const Wasm) bool {
4048 const comp = wasm.base.comp;
4049 const import_memory = comp.config.import_memory;
4050
4051 var it = wasm.data_segments.iterator();
4052 while (it.next()) |entry| {
4053 const segment = wasm.segmentPtr(entry.value_ptr.*);
4054 if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) {
4055 return true;
4056 }
4057 }
4058 return false;
4333pub fn addExpr(wasm: *Wasm, bytes: []const u8) Allocator.Error!Expr {
4334 const gpa = wasm.base.comp.gpa;
4335 // We can't use string table deduplication here since these expressions can
4336 // have null bytes in them however it may be interesting to explore since
4337 // it is likely for globals to share initialization values. Then again
4338 // there may not be very many globals in total.
4339 try wasm.string_bytes.appendSlice(gpa, bytes);
4340 return @enumFromInt(wasm.string_bytes.items.len - bytes.len);
40594341}
40604342
4061/// Searches for a matching function signature. When no matching signature is found,
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.
4343pub fn addRelocatableDataPayload(wasm: *Wasm, bytes: []const u8) Allocator.Error!DataPayload {
40694344 const gpa = wasm.base.comp.gpa;
4070 const index: u32 = @intCast(wasm.func_types.items.len);
4071 const params = try gpa.dupe(std.wasm.Valtype, func_type.params);
4072 errdefer gpa.free(params);
4073 const returns = try gpa.dupe(std.wasm.Valtype, func_type.returns);
4074 errdefer gpa.free(returns);
4075 try wasm.func_types.append(gpa, .{
4076 .params = params,
4077 .returns = returns,
4078 });
4079 return index;
4345 try wasm.string_bytes.appendSlice(gpa, bytes);
4346 return .{
4347 .off = @enumFromInt(wasm.string_bytes.items.len - bytes.len),
4348 .len = @intCast(bytes.len),
4349 };
40804350}
40814351
4082/// For the given `nav`, stores the corresponding type representing the function signature.
4083/// Asserts declaration has an associated `Atom`.
4084/// Returns the index into the list of types.
4085pub fn storeNavType(wasm: *Wasm, nav: InternPool.Nav.Index, func_type: std.wasm.Type) !u32 {
4086 return wasm.zig_object.?.storeDeclType(wasm.base.comp.gpa, nav, func_type);
4352pub fn uavSymbolIndex(wasm: *Wasm, ip_index: InternPool.Index) Allocator.Error!SymbolTableIndex {
4353 const comp = wasm.base.comp;
4354 assert(comp.config.output_mode == .Obj);
4355 const gpa = comp.gpa;
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);
40874360}
40884361
4089/// Returns the symbol index of the error name table.
4090///
4091/// When the symbol does not yet exist, it will create a new one instead.
4092pub fn getErrorTableSymbol(wasm: *Wasm, pt: Zcu.PerThread) !u32 {
4093 const sym_index = try wasm.zig_object.?.getErrorTableSymbol(wasm, pt);
4094 return @intFromEnum(sym_index);
4362pub fn navSymbolIndex(wasm: *Wasm, nav_index: InternPool.Nav.Index) Allocator.Error!SymbolTableIndex {
4363 const comp = wasm.base.comp;
4364 assert(comp.config.output_mode == .Obj);
4365 const zcu = comp.zcu.?;
4366 const ip = &zcu.intern_pool;
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);
40954373}
40964374
4097/// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`.
4098/// When the index was not found, a new `Atom` will be created, and its index will be returned.
4099/// The newly created Atom is empty with default fields as specified by `Atom.empty`.
4100pub fn getOrCreateAtomForNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !Atom.Index {
4101 return wasm.zig_object.?.getOrCreateAtomForNav(wasm, pt, nav);
4375pub fn errorNameTableSymbolIndex(wasm: *Wasm) Allocator.Error!SymbolTableIndex {
4376 const comp = wasm.base.comp;
4377 assert(comp.config.output_mode == .Obj);
4378 const gpa = comp.gpa;
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);
41024382}
41034383
4104/// Verifies all resolved symbols and checks whether itself needs to be marked alive,
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;
4384pub fn stackPointerSymbolIndex(wasm: *Wasm) Allocator.Error!SymbolTableIndex {
41114385 const comp = wasm.base.comp;
4112
4113 for (wasm.resolved_symbols.keys()) |sym_loc| {
4114 const sym = wasm.symbolLocSymbol(sym_loc);
4115 if (sym.isExported(comp.config.rdynamic) or sym.isNoStrip() or !do_garbage_collect) {
4116 try wasm.mark(sym_loc);
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 }
4386 assert(comp.config.output_mode == .Obj);
4387 const gpa = comp.gpa;
4388 const gop = try wasm.symbol_table.getOrPut(gpa, wasm.preloaded_strings.__stack_pointer);
4389 gop.value_ptr.* = {};
4390 return @enumFromInt(gop.index);
41284391}
41294392
4130/// Marks a symbol as 'alive' recursively so itself and any references it contains to
4131/// other symbols will not be omit from the binary.
4132fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
4133 const symbol = wasm.symbolLocSymbol(loc);
4134 if (symbol.isAlive()) {
4135 // Symbol is already marked alive, including its references.
4136 // This means we can skip it so we don't end up marking the same symbols
4137 // multiple times.
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 }
4393pub fn tagNameSymbolIndex(wasm: *Wasm, ip_index: InternPool.Index) Allocator.Error!SymbolTableIndex {
4394 const comp = wasm.base.comp;
4395 assert(comp.config.output_mode == .Obj);
4396 const gpa = comp.gpa;
4397 const name = try wasm.internStringFmt("__zig_tag_name_{d}", .{@intFromEnum(ip_index)});
4398 const gop = try wasm.symbol_table.getOrPut(gpa, name);
4399 gop.value_ptr.* = {};
4400 return @enumFromInt(gop.index);
41584401}
41594402
4160fn defaultEntrySymbolName(
4161 preloaded_strings: *const PreloadedStrings,
4162 wasi_exec_model: std.builtin.WasiExecModel,
4163) String {
4164 return switch (wasi_exec_model) {
4165 .reactor => preloaded_strings._initialize,
4166 .command => preloaded_strings._start,
4167 };
4403pub fn symbolNameIndex(wasm: *Wasm, name: String) Allocator.Error!SymbolTableIndex {
4404 const comp = wasm.base.comp;
4405 assert(comp.config.output_mode == .Obj);
4406 const gpa = comp.gpa;
4407 const gop = try wasm.symbol_table.getOrPut(gpa, name);
4408 gop.value_ptr.* = {};
4409 return @enumFromInt(gop.index);
41684410}
41694411
4170pub const Atom = struct {
4171 /// Represents the index of the file this atom was generated from.
4172 /// This is `none` when the atom was generated by a synthetic linker symbol.
4173 file: OptionalObjectId,
4174 /// symbol index of the symbol representing this atom
4175 sym_index: Symbol.Index,
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 }
4412pub fn refUavObj(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsObjIndex {
4413 const comp = wasm.base.comp;
4414 const zcu = comp.zcu.?;
4415 const ip = &zcu.intern_pool;
4416 const gpa = comp.gpa;
4417 assert(comp.config.output_mode == .Obj);
42294418
4230 /// Returns the location of the symbol that represents this `Atom`
4231 pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {
4232 return .{
4233 .file = atom.file,
4234 .index = atom.sym_index,
4235 };
4419 if (orig_ptr_ty != .none) {
4420 const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu);
4421 const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment;
4422 if (explicit_alignment.compare(.gt, abi_alignment)) {
4423 const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index);
4424 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment;
4425 }
42364426 }
42374427
4238 /// Resolves the relocations within the atom, writing the new value
4239 /// at the calculated offset.
4240 pub fn resolveRelocs(atom: *Atom, wasm: *const Wasm) void {
4241 if (atom.relocs.items.len == 0) return;
4242 const symbol_name = wasm.symbolLocName(atom.symbolLoc());
4243 log.debug("Resolving relocs in atom '{s}' count({d})", .{
4244 symbol_name,
4245 atom.relocs.items.len,
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 });
4428 const gop = try wasm.uavs_obj.getOrPut(gpa, ip_index);
4429 if (!gop.found_existing) gop.value_ptr.* = .{
4430 // Lowering the value is delayed to avoid recursion.
4431 .code = undefined,
4432 .relocs = undefined,
4433 };
4434 return @enumFromInt(gop.index);
4435}
42594436
4260 switch (reloc.relocation_type) {
4261 .R_WASM_TABLE_INDEX_I32,
4262 .R_WASM_FUNCTION_OFFSET_I32,
4263 .R_WASM_GLOBAL_INDEX_I32,
4264 .R_WASM_MEMORY_ADDR_I32,
4265 .R_WASM_SECTION_OFFSET_I32,
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 }
4437pub fn refUavExe(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsExeIndex {
4438 const comp = wasm.base.comp;
4439 const zcu = comp.zcu.?;
4440 const ip = &zcu.intern_pool;
4441 const gpa = comp.gpa;
4442 assert(comp.config.output_mode != .Obj);
42884443
4289 /// From a given `relocation` will return the new value to be written.
4290 /// All values will be represented as a `u64` as all values can fit within it.
4291 /// The final value must be casted to the correct size.
4292 fn relocationValue(atom: Atom, relocation: Relocation, wasm: *const Wasm) u64 {
4293 const target_loc = wasm.symbolLocFinalLoc(.{
4294 .file = atom.file,
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 },
4444 if (orig_ptr_ty != .none) {
4445 const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu);
4446 const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment;
4447 if (explicit_alignment.compare(.gt, abi_alignment)) {
4448 const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index);
4449 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment;
43584450 }
43594451 }
43604452
4361 // For a given `Atom` returns whether it has a tombstone value or not.
4362 /// This defines whether we want a specific value when a section is dead.
4363 fn tombstone(atom: Atom, wasm: *const Wasm) ?i64 {
4364 const atom_name = wasm.symbolLocSymbol(atom.symbolLoc()).name;
4365 if (atom_name == wasm.custom_sections.@".debug_ranges".name or
4366 atom_name == wasm.custom_sections.@".debug_loc".name)
4367 {
4368 return -2;
4369 } else if (std.mem.startsWith(u8, wasm.stringSlice(atom_name), ".debug_")) {
4370 return -1;
4371 } else {
4372 return null;
4373 }
4453 const gop = try wasm.uavs_exe.getOrPut(gpa, ip_index);
4454 if (gop.found_existing) {
4455 gop.value_ptr.count += 1;
4456 } else {
4457 gop.value_ptr.* = .{
4458 // Lowering the value is delayed to avoid recursion.
4459 .code = undefined,
4460 .count = 1,
4461 };
43744462 }
4375};
4463 return @enumFromInt(gop.index);
4464}
43764465
4377pub const Relocation = struct {
4378 /// Represents the type of the `Relocation`
4379 relocation_type: RelocationType,
4380 /// Offset of the value to rewrite relative to the relevant section's contents.
4381 /// When `offset` is zero, its position is immediately after the id and size of the section.
4382 offset: u32,
4383 /// The index of the symbol used.
4384 /// When the type is `R_WASM_TYPE_INDEX_LEB`, it represents the index of the type.
4385 index: u32,
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 }
4466pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {
4467 const comp = wasm.base.comp;
4468 const gpa = comp.gpa;
4469 assert(comp.config.output_mode != .Obj);
4470 const gop = try wasm.navs_obj.getOrPut(gpa, nav_index);
4471 if (!gop.found_existing) gop.value_ptr.* = .{
4472 // Lowering the value is delayed to avoid recursion.
4473 .code = undefined,
4474 .relocs = undefined,
44334475 };
4476 return @enumFromInt(gop.index);
4477}
44344478
4435 /// Verifies the relocation type of a given `Relocation` and returns
4436 /// true when the relocation references a function call or address to a function.
4437 pub fn isFunction(self: Relocation) bool {
4438 return switch (self.relocation_type) {
4439 .R_WASM_FUNCTION_INDEX_LEB,
4440 .R_WASM_TABLE_INDEX_SLEB,
4441 => true,
4442 else => false,
4479pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {
4480 const comp = wasm.base.comp;
4481 const gpa = comp.gpa;
4482 assert(comp.config.output_mode != .Obj);
4483 const gop = try wasm.navs_exe.getOrPut(gpa, nav_index);
4484 if (gop.found_existing) {
4485 gop.value_ptr.count += 1;
4486 } else {
4487 gop.value_ptr.* = .{
4488 // Lowering the value is delayed to avoid recursion.
4489 .code = undefined,
4490 .count = 0,
44434491 };
44444492 }
4493 return @enumFromInt(gop.index);
4494}
44454495
4446 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
4447 _ = fmt;
4448 _ = options;
4449 try writer.print("{s} offset=0x{x:0>6} symbol={d}", .{
4450 @tagName(self.relocation_type),
4451 self.offset,
4452 self.index,
4453 });
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};
4496/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.
4497pub fn uavAddr(wasm: *Wasm, uav_index: UavsExeIndex) u32 {
4498 assert(wasm.flush_buffer.memory_layout_finished);
4499 const comp = wasm.base.comp;
4500 assert(comp.config.output_mode != .Obj);
4501 const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index });
4502 return wasm.flush_buffer.data_segments.get(ds_id).?;
4503}
46224504
4623pub const known_features = std.StaticStringMap(Feature.Tag).initComptime(.{
4624 .{ "atomics", .atomics },
4625 .{ "bulk-memory", .bulk_memory },
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);
4505/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.
4506pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) u32 {
4507 assert(wasm.flush_buffer.memory_layout_finished);
46454508 const comp = wasm.base.comp;
4646 const gpa = comp.gpa;
4647 const symbol = &object.symtable[@intFromEnum(symbol_index)];
4648 const relocatable_data: Object.RelocatableData = switch (symbol.tag) {
4649 .function => object.relocatable_data.get(.code).?[symbol.index - object.imported_functions_count],
4650 .data => object.relocatable_data.get(.data).?[symbol.index],
4651 .section => blk: {
4652 const data = object.relocatable_data.get(.custom).?;
4653 for (data) |dat| {
4654 if (dat.section_index == symbol.index) {
4655 break :blk dat;
4509 assert(comp.config.output_mode != .Obj);
4510 if (wasm.navs_exe.getIndex(nav_index)) |i| {
4511 const navs_exe_index: NavsExeIndex = @enumFromInt(i);
4512 log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index });
4513 const ds_id: DataSegmentId = .pack(wasm, .{ .nav_exe = navs_exe_index });
4514 return wasm.flush_buffer.data_segments.get(ds_id).?;
4515 }
4516 const zcu = comp.zcu.?;
4517 const ip = &zcu.intern_pool;
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"),
46564538 }
46574539 }
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 }
47064540 }
47074541 }
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).?;
47104552}
47114553
4712fn searchRelocStart(relocs: []const Wasm.Relocation, address: u32) usize {
4713 var min: usize = 0;
4714 var max: usize = relocs.len;
4715 while (min < max) {
4716 const index = (min + max) / 2;
4717 const curr = relocs[index];
4718 if (curr.offset < address) {
4719 min = index + 1;
4554fn convertZcuFnType(
4555 comp: *Compilation,
4556 cc: std.builtin.CallingConvention,
4557 params: []const InternPool.Index,
4558 return_type: Zcu.Type,
4559 target: *const std.Target,
4560 params_buffer: *std.ArrayListUnmanaged(std.wasm.Valtype),
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));
47204577 } else {
4721 max = index;
4578 try returns_buffer.append(gpa, CodeGen.typeToValtype(return_type, zcu, target));
47224579 }
4580 } else if (return_type.isError(zcu)) {
4581 try returns_buffer.append(gpa, .i32);
47234582 }
4724 return min;
4725}
47264583
4727fn searchRelocEnd(relocs: []const Wasm.Relocation, address: u32) usize {
4728 for (relocs, 0..relocs.len) |reloc, index| {
4729 if (reloc.offset > address) {
4730 return index;
4584 // param types
4585 for (params) |param_type_ip| {
4586 const param_type = Zcu.Type.fromInterned(param_type_ip);
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)),
47314606 }
47324607 }
4733 return relocs.len;
47344608}
47354609
4736pub fn internString(wasm: *Wasm, bytes: []const u8) error{OutOfMemory}!String {
4737 const gpa = wasm.base.comp.gpa;
4738 const gop = try wasm.string_table.getOrPutContextAdapted(
4739 gpa,
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);
4610pub fn isBss(wasm: *const Wasm, optional_name: OptionalString) bool {
4611 const s = optional_name.slice(wasm) orelse return false;
4612 return mem.eql(u8, s, ".bss") or mem.startsWith(u8, s, ".bss.");
4613}
47484614
4749 wasm.string_bytes.appendSliceAssumeCapacity(bytes);
4750 wasm.string_bytes.appendAssumeCapacity(0);
4615/// After this function is called, there may be additional entries in
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 };
47554666}
47564667
4757pub fn getExistingString(wasm: *const Wasm, bytes: []const u8) ?String {
4758 return wasm.string_table.getKeyAdapted(bytes, @as(String.TableIndexAdapter, .{
4759 .bytes = wasm.string_bytes.items,
4760 }));
4668fn pointerAlignment(wasm: *const Wasm) Alignment {
4669 const target = &wasm.base.comp.root_mod.resolved_target.result;
4670 return switch (target.cpu.arch) {
4671 .wasm32 => .@"4",
4672 .wasm64 => .@"8",
4673 else => unreachable,
4674 };
47614675}
47624676
4763pub fn stringSlice(wasm: *const Wasm, index: String) [:0]const u8 {
4764 const slice = wasm.string_bytes.items[@intFromEnum(index)..];
4765 return slice[0..mem.indexOfScalar(u8, slice, 0).? :0];
4677fn pointerSize(wasm: *const Wasm) u32 {
4678 const target = &wasm.base.comp.root_mod.resolved_target.result;
4679 return switch (target.cpu.arch) {
4680 .wasm32 => 4,
4681 .wasm64 => 8,
4682 else => unreachable,
4683 };
47664684}
47674685
4768pub fn optionalStringSlice(wasm: *const Wasm, index: OptionalString) ?[:0]const u8 {
4769 return stringSlice(wasm, index.unwrap() orelse return null);
4686fn addZcuImportReserved(wasm: *Wasm, nav_index: InternPool.Nav.Index) ZcuImportIndex {
4687 const gop = wasm.imports.getOrPutAssumeCapacity(nav_index);
4688 gop.value_ptr.* = {};
4689 return @enumFromInt(gop.index);
47704690}
47714691
4772pub fn castToString(wasm: *const Wasm, index: u32) String {
4773 assert(index == 0 or wasm.string_bytes.items[index - 1] == 0);
4774 return @enumFromInt(index);
4692fn resolveFunctionSynthetic(
4693 wasm: *Wasm,
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 }
47754713}
47764714
4777fn segmentPtr(wasm: *const Wasm, index: Segment.Index) *Segment {
4778 return &wasm.segments.items[@intFromEnum(index)];
4715pub fn addFunction(
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 });
47794726}
src/link/Wasm/Archive.zig+14-3
......@@ -142,8 +142,18 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive {
142142
143143/// From a given file offset, starts reading for a file header.
144144/// 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 {
146 const header = mem.bytesAsValue(Header, file_contents[0..@sizeOf(Header)]);
145pub fn parseObject(
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)]);
147157 if (!mem.eql(u8, &header.fmag, ARFMAG)) return error.BadHeaderDelimiter;
148158
149159 const name_or_index = try header.nameOrIndex();
......@@ -157,8 +167,9 @@ pub fn parseObject(archive: Archive, wasm: *Wasm, file_contents: []const u8, pat
157167 };
158168
159169 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);
162173}
163174
164175const 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//! 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.
41const Object = @This();
52
63const Wasm = @import("../Wasm.zig");
7const Atom = Wasm.Atom;
84const Alignment = Wasm.Alignment;
9const Symbol = @import("Symbol.zig");
105
116const std = @import("std");
127const Allocator = std.mem.Allocator;
13const leb = std.leb;
14const meta = std.meta;
158const Path = std.Build.Cache.Path;
16
179const log = std.log.scoped(.object);
10const assert = std.debug.assert;
1811
1912/// Wasm spec version used for this `Object`
20version: u32 = 0,
13version: u32,
2114/// For error reporting purposes only.
2215/// Name (read path) of the object or archive file.
2316path: Path,
2417/// For error reporting purposes only.
2518/// If this represents an object in an archive, it's the basename of the
2619/// object, and path refers to the archive.
27archive_member_name: ?[]const u8,
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 = &.{},
20archive_member_name: Wasm.OptionalString,
4421/// Represents the function ID that must be called on startup.
4522/// This is `null` by default as runtimes may determine the startup
4623/// function themselves. This is essentially legacy.
47start: ?u32 = null,
48/// A slice of features that tell the linker what features are mandatory,
49/// used (or therefore missing) and must generate an error when another
50/// object uses features that are not supported by the other.
51features: []const Wasm.Feature = &.{},
52/// A table that maps the relocations we must perform where the key represents
53/// the section that the list of relocations applies to.
54relocations: std.AutoArrayHashMapUnmanaged(u32, []Wasm.Relocation) = .empty,
55/// Table of symbols belonging to this Object file
56symtable: []Symbol = &.{},
57/// Extra metadata about the linking section, such as alignment of segments and their name
58segment_info: []const Wasm.NamedSegment = &.{},
59/// A sequence of function initializers that must be called on startup
60init_funcs: []const Wasm.InitFunc = &.{},
61/// Comdat information
62comdat_info: []const Wasm.Comdat = &.{},
63/// Represents non-synthetic sections that can essentially be mem-cpy'd into place
64/// after performing relocations.
65relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableData) = .empty,
66/// Amount of functions in the `import` sections.
67imported_functions_count: u32 = 0,
68/// Amount of globals in the `import` section.
69imported_globals_count: u32 = 0,
70/// Amount of tables in the `import` section.
71imported_tables_count: u32 = 0,
72
73/// Represents a single item within a section (depending on its `type`)
74pub const RelocatableData = struct {
75 /// The type of the relocatable data
76 type: Tag,
77 /// Pointer to the data of the segment, where its length is written to `size`
78 data: [*]u8,
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 }
24start_function: Wasm.OptionalObjectFunctionIndex,
25/// A slice of features that tell the linker what features are mandatory, used
26/// (or therefore missing) and must generate an error when another object uses
27/// features that are not supported by the other.
28features: Wasm.Feature.Set,
29/// Points into `Wasm.object_functions`
30functions: RelativeSlice,
31/// Points into `Wasm.object_function_imports`
32function_imports: RelativeSlice,
33/// Points into `Wasm.object_global_imports`
34global_imports: RelativeSlice,
35/// Points into `Wasm.object_table_imports`
36table_imports: RelativeSlice,
37// Points into `Wasm.object_data_imports`
38data_imports: RelativeSlice,
39/// Points into Wasm object_custom_segments
40custom_segments: RelativeSlice,
41/// Points into Wasm object_init_funcs
42init_funcs: RelativeSlice,
43/// Points into Wasm object_comdats
44comdats: RelativeSlice,
45/// Guaranteed to be non-null when functions has nonzero length.
46code_section_index: ?Wasm.ObjectSectionIndex,
47/// Guaranteed to be non-null when globals has nonzero length.
48global_section_index: ?Wasm.ObjectSectionIndex,
49/// Guaranteed to be non-null when data segments has nonzero length.
50data_section_index: ?Wasm.ObjectSectionIndex,
51is_included: bool,
52
53pub const RelativeSlice = struct {
54 off: u32,
55 len: u32,
11856};
11957
120/// Initializes a new `Object` from a wasm object file.
121/// This also parses and verifies the object file.
122/// When a max size is given, will only parse up to the given size,
123/// else will read until the end of the file.
124pub fn create(
125 wasm: *Wasm,
126 file_contents: []const u8,
127 path: Path,
128 archive_member_name: ?[]const u8,
129) !Object {
130 const gpa = wasm.base.comp.gpa;
131 var object: Object = .{
132 .path = path,
133 .archive_member_name = archive_member_name,
58pub const SegmentInfo = struct {
59 name: Wasm.String,
60 flags: Flags,
61
62 /// Matches the ABI.
63 pub const Flags = packed struct(u32) {
64 /// Signals that the segment contains only null terminated strings allowing
65 /// the linker to perform merging.
66 strings: bool,
67 /// The segment contains thread-local data. This means that a unique copy
68 /// of this segment will be created for each thread.
69 tls: bool,
70 /// If the object file is included in the final link, the segment should be
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,
13477 };
78};
13579
136 var parser: Parser = .{
137 .object = &object,
138 .wasm = wasm,
139 .reader = std.io.fixedBufferStream(file_contents),
140 };
141 try parser.parseObject(gpa);
80pub const FunctionImport = struct {
81 module_name: Wasm.String,
82 name: Wasm.String,
83 function_index: ScratchSpace.FuncTypeIndex,
84};
14285
143 return object;
144}
86pub const GlobalImport = struct {
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 be
147/// the same allocator that was used when `init` was called.
148pub fn deinit(object: *Object, gpa: Allocator) void {
149 for (object.func_types) |func_ty| {
150 gpa.free(func_ty.params);
151 gpa.free(func_ty.returns);
152 }
153 gpa.free(object.func_types);
154 gpa.free(object.functions);
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}
93pub const TableImport = struct {
94 module_name: Wasm.String,
95 name: Wasm.String,
96 limits_min: u32,
97 limits_max: u32,
98 limits_has_max: bool,
99 limits_is_shared: bool,
100 ref_type: std.wasm.RefType,
101};
188102
189/// Finds the import within the list of imports from a given kind and index of that kind.
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}
103pub const DataSegmentFlags = enum(u32) { active, passive, active_memidx };
200104
201/// Checks if the object file is an MVP version.
202/// When that's the case, we check if there's an import table definition with its name
203/// set to '__indirect_function_table". When that's also the case,
204/// we initialize a new table symbol that corresponds to that import and return that symbol.
205///
206/// When the object file is *NOT* MVP, we return `null`.
207fn checkLegacyIndirectFunctionTable(object: *Object, wasm: *const Wasm) !?Symbol {
208 const diags = &wasm.base.comp.link_diags;
105pub const SubsectionType = enum(u8) {
106 segment_info = 5,
107 init_funcs = 6,
108 comdat_info = 7,
109 symbol_table = 8,
110};
209111
210 var table_count: usize = 0;
211 for (object.symtable) |sym| {
212 if (sym.tag == .table) table_count += 1;
213 }
112/// Specified by https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md
113pub const RelocationType = enum(u8) {
114 function_index_leb = 0,
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 file
216 if (object.imported_tables_count == table_count) return null;
143pub const Symbol = struct {
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) {
219 return diags.failParse(object.path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
220 object.imported_tables_count,
221 table_count,
222 });
223 }
158 const Pointee = union(enum) {
159 function: Wasm.ObjectFunctionIndex,
160 function_import: ScratchSpace.FuncImportIndex,
161 data: Wasm.ObjectData.Index,
162 data_import: void,
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).
226 if (object.tables.len > 0) {
227 return diags.failParse(object.path, "unexpected table definition without representing table symbols.", .{});
228 }
171pub const ScratchSpace = struct {
172 func_types: std.ArrayListUnmanaged(Wasm.FunctionType.Index) = .empty,
173 func_type_indexes: std.ArrayListUnmanaged(FuncTypeIndex) = .empty,
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) {
231 return diags.failParse(object.path, "found more than one table import, but no representing table symbols", .{});
232 }
193 /// Index into `func_imports`.
194 const FuncImportIndex = enum(u32) {
195 _,
233196
234 const table_import: Wasm.Import = for (object.imports) |imp| {
235 if (imp.kind == .table) {
236 break imp;
197 fn ptr(index: FuncImportIndex, ss: *const ScratchSpace) *FunctionImport {
198 return &ss.func_imports.items[@intFromEnum(index)];
237199 }
238 } else unreachable;
200 };
239201
240 if (table_import.name != wasm.preloaded_strings.__indirect_function_table) {
241 return diags.failParse(object.path, "non-indirect function table import '{s}' is missing a corresponding symbol", .{
242 wasm.stringSlice(table_import.name),
243 });
244 }
202 /// Index into `global_imports`.
203 const GlobalImportIndex = enum(u32) {
204 _,
245205
246 var table_symbol: Symbol = .{
247 .flags = 0,
248 .name = table_import.name,
249 .tag = .table,
250 .index = 0,
251 .virtual_address = undefined,
206 fn ptr(index: GlobalImportIndex, ss: *const ScratchSpace) *GlobalImport {
207 return &ss.global_imports.items[@intFromEnum(index)];
208 }
252209 };
253 table_symbol.setFlag(.WASM_SYM_UNDEFINED);
254 table_symbol.setFlag(.WASM_SYM_NO_STRIP);
255 return table_symbol;
256}
257210
258const Parser = struct {
259 reader: std.io.FixedBufferStream([]const u8),
260 /// Object file we're building
261 object: *Object,
262 /// Mutable so that the string table can be modified.
263 wasm: *Wasm,
211 /// Index into `table_imports`.
212 const TableImportIndex = enum(u32) {
213 _,
214
215 fn ptr(index: TableImportIndex, ss: *const ScratchSpace) *TableImport {
216 return &ss.table_imports.items[@intFromEnum(index)];
217 }
218 };
264219
265 fn parseObject(parser: *Parser, gpa: Allocator) anyerror!void {
266 const wasm = parser.wasm;
220 /// Index into `func_types`.
221 const FuncTypeIndex = enum(u32) {
222 _,
267223
268 {
269 var magic_bytes: [4]u8 = undefined;
270 try parser.reader.reader().readNoEof(&magic_bytes);
271 if (!std.mem.eql(u8, &magic_bytes, &std.wasm.magic)) return error.BadObjectMagic;
224 fn ptr(index: FuncTypeIndex, ss: *const ScratchSpace) *Wasm.FunctionType.Index {
225 return &ss.func_types.items[@intFromEnum(index)];
272226 }
227 };
273228
274 const version = try parser.reader.reader().readInt(u32, .little);
275 parser.object.version = version;
276
277 var saw_linking_section = false;
278
279 var section_index: u32 = 0;
280 while (parser.reader.reader().readByte()) |byte| : (section_index += 1) {
281 const len = try readLeb(u32, parser.reader.reader());
282 var limited_reader = std.io.limitedReader(parser.reader.reader(), len);
283 const reader = limited_reader.reader();
284 switch (@as(std.wasm.Section, @enumFromInt(byte))) {
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;
229 pub fn deinit(ss: *ScratchSpace, gpa: Allocator) void {
230 ss.exports.deinit(gpa);
231 ss.func_types.deinit(gpa);
232 ss.func_type_indexes.deinit(gpa);
233 ss.func_imports.deinit(gpa);
234 ss.global_imports.deinit(gpa);
235 ss.table_imports.deinit(gpa);
236 ss.symbol_table.deinit(gpa);
237 ss.segment_info.deinit(gpa);
238 ss.* = undefined;
239 }
328240
329 for (try readVec(&type_val.params, reader, gpa)) |*param| {
330 param.* = try readEnum(std.wasm.Valtype, reader);
331 }
241 fn clear(ss: *ScratchSpace) void {
242 ss.exports.clearRetainingCapacity();
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| {
334 result.* = try readEnum(std.wasm.Valtype, reader);
253pub fn parse(
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 },
335497 }
336498 }
337 try assertEnd(reader);
338 },
339 .import => {
340 for (try readVec(&parser.object.imports, reader, gpa)) |*import| {
341 const module_len = try readLeb(u32, reader);
342 const module_name = try gpa.alloc(u8, module_len);
343 defer gpa.free(module_name);
344 try reader.readNoEof(module_name);
345
346 const name_len = try readLeb(u32, reader);
347 const name = try gpa.alloc(u8, name_len);
348 defer gpa.free(name);
349 try reader.readNoEof(name);
350
351 const kind = try readEnum(std.wasm.ExternalKind, reader);
352 const kind_value: std.wasm.Import.Kind = switch (kind) {
353 .function => val: {
354 parser.object.imported_functions_count += 1;
355 break :val .{ .function = try readLeb(u32, reader) };
499 } else if (std.mem.startsWith(u8, section_name, "reloc.")) {
500 // 'The "reloc." custom sections must come after the "linking" custom section'
501 if (!saw_linking_section) return error.RelocBeforeLinkingSection;
502
503 // "Relocation sections start with an identifier specifying
504 // which section they apply to, and must be sequenced in
505 // the module after that section."
506 // "Relocation sections can only target code, data and custom sections."
507 const local_section, pos = readLeb(u32, bytes, pos);
508 const count, pos = readLeb(u32, bytes, pos);
509 const section: Wasm.ObjectSectionIndex = @enumFromInt(local_section_index_base + local_section);
510
511 log.debug("found {d} relocations for section={d}", .{ count, section });
512
513 var prev_offset: u32 = 0;
514 try wasm.object_relocations.ensureUnusedCapacity(gpa, count);
515 for (0..count) |_| {
516 const tag: RelocationType = @enumFromInt(bytes[pos]);
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 });
356574 },
357 .memory => .{ .memory = try readLimits(reader) },
358 .global => val: {
359 parser.object.imported_globals_count += 1;
360 break :val .{ .global = .{
361 .valtype = try readEnum(std.wasm.Valtype, reader),
362 .mutable = (try reader.readByte()) == 0x01,
363 } };
575 .section_offset_i32 => {
576 const addend: i32, pos = readLeb(i32, bytes, pos);
577 wasm.object_relocations.appendAssumeCapacity(.{
578 .tag = .section_offset_i32,
579 .offset = offset,
580 .pointee = .{ .section = sym.pointee.section },
581 .addend = addend,
582 });
364583 },
365 .table => val: {
366 parser.object.imported_tables_count += 1;
367 break :val .{ .table = .{
368 .reftype = try readEnum(std.wasm.RefType, reader),
369 .limits = try readLimits(reader),
370 } };
584 .type_index_leb => {
585 wasm.object_relocations.appendAssumeCapacity(.{
586 .tag = .type_index_leb,
587 .offset = offset,
588 .pointee = .{ .type_index = ss.func_types.items[index] },
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 });
371633 },
372 };
373634
374 import.* = .{
375 .module_name = try wasm.internString(module_name),
376 .name = try wasm.internString(name),
377 .kind = kind_value,
378 };
379 }
380 try assertEnd(reader);
381 },
382 .function => {
383 for (try readVec(&parser.object.functions, reader, gpa)) |*func| {
384 func.* = .{ .type_index = try readLeb(u32, reader) };
385 }
386 try assertEnd(reader);
387 },
388 .table => {
389 for (try readVec(&parser.object.tables, reader, gpa)) |*table| {
390 table.* = .{
391 .reftype = try readEnum(std.wasm.RefType, reader),
392 .limits = try readLimits(reader),
393 };
635 .table_number_leb => {
636 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
637 .table => .{
638 .tag = .fromType(tag),
639 .offset = offset,
640 .pointee = .{ .table = sym.pointee.table },
641 .addend = undefined,
642 },
643 .table_import => .{
644 .tag = .fromTypeImport(tag),
645 .offset = offset,
646 .pointee = .{ .symbol_name = sym.name.unwrap().? },
647 .addend = undefined,
648 },
649 else => unreachable,
650 });
651 },
652 .event_index_leb => return diags.failParse(path, "unsupported relocation: R_WASM_EVENT_INDEX_LEB", .{}),
653 }
394654 }
395 try assertEnd(reader);
396 },
397 .memory => {
398 for (try readVec(&parser.object.memories, reader, gpa)) |*memory| {
399 memory.* = .{ .limits = try readLimits(reader) };
655
656 try wasm.object_relocations_table.putNoClobber(gpa, section, .{
657 .off = @intCast(wasm.object_relocations.len - count),
658 .len = count,
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 },
400762 }
401 try assertEnd(reader);
402 },
403 .global => {
404 for (try readVec(&parser.object.globals, reader, gpa)) |*global| {
405 global.* = .{
763 }
764 },
765 .function => {
766 const functions_len, pos = readLeb(u32, bytes, pos);
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 = .{
406821 .global_type = .{
407 .valtype = try readEnum(std.wasm.Valtype, reader),
408 .mutable = (try reader.readByte()) == 0x01,
822 .valtype = .from(valtype),
823 .mutable = mutable,
409824 },
410 .init = try readInit(reader),
411 };
412 }
413 try assertEnd(reader);
414 },
415 .@"export" => {
416 for (try readVec(&parser.object.exports, reader, gpa)) |*exp| {
417 const name_len = try readLeb(u32, reader);
418 const name = try gpa.alloc(u8, name_len);
419 defer gpa.free(name);
420 try reader.readNoEof(name);
421 exp.* = .{
422 .name = try wasm.internString(name),
423 .kind = try readEnum(std.wasm.ExternalKind, reader),
424 .index = try readLeb(u32, reader),
425 };
825 },
826 .expr = expr,
827 .object_index = object_index,
828 .offset = @intCast(init_start - section_start),
829 .size = @intCast(pos - init_start),
830 };
831 }
832 },
833 .@"export" => {
834 const exports_len, pos = readLeb(u32, bytes, pos);
835 // Read into scratch space, and then later add this data as if
836 // it were extra symbol table entries, but allow merging with
837 // existing symbol table data if the name matches.
838 for (try ss.exports.addManyAsSlice(gpa, exports_len)) |*exp| {
839 const name, pos = readBytes(bytes, pos);
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});
426896 }
427 try assertEnd(reader);
428 },
429 .start => {
430 parser.object.start = try readLeb(u32, reader);
431 try assertEnd(reader);
432 },
433 .element => {
434 for (try readVec(&parser.object.elements, reader, gpa)) |*elem| {
435 elem.table_index = try readLeb(u32, reader);
436 elem.offset = try readInit(reader);
897 //const expr, pos = if (flags != .passive) try readInit(wasm, bytes, pos) else .{ .none, pos };
898 if (flags != .passive) pos = try skipInit(bytes, pos);
899 const data_len, pos = readLeb(u32, bytes, pos);
900 const segment_start = pos;
901 const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..data_len]);
902 pos += data_len;
903 elem.* = .{
904 .payload = payload,
905 .name = .none, // Populated from segment_info
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| {
439 idx.* = try readLeb(u32, reader);
440 }
441 }
442 try assertEnd(reader);
920 const target_features = comp.root_mod.resolved_target.result.cpu.features;
921
922 if (has_tls) {
923 if (!std.Target.wasm.featureSetHas(target_features, .atomics))
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", .{});
443937 },
444 .code => {
445 const start = reader.context.bytes_left;
446 var index: u32 = 0;
447 const count = try readLeb(u32, reader);
448 const imported_function_count = parser.object.imported_functions_count;
449 var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count);
450 defer relocatable_data.deinit();
451 while (index < count) : (index += 1) {
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 });
938 else => {
939 const f = feat.tag.toCpuFeature().?;
940 if (std.Target.wasm.featureSetHas(target_features, f)) {
941 return diags.failParse(
942 path,
943 "object forbids {s} but specified target features include {s}",
944 .{ @tagName(feat.tag), @tagName(f) },
945 );
465946 }
466 try parser.object.relocatable_data.put(gpa, .code, try relocatable_data.toOwnedSlice());
467947 },
468 .data => {
469 const start = reader.context.bytes_left;
470 var index: u32 = 0;
471 const count = try readLeb(u32, reader);
472 var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count);
473 defer relocatable_data.deinit();
474 while (index < count) : (index += 1) {
475 const flags = try readLeb(u32, reader);
476 const data_offset = try readInit(reader);
477 _ = flags; // TODO: Do we need to check flags to detect passive/active memory?
478 _ = data_offset;
479 const data_len = try readLeb(u32, reader);
480 const offset = @as(u32, @intCast(start - reader.context.bytes_left));
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 });
948 },
949 .@"+", .@"=" => switch (feat.tag) {
950 .@"shared-mem" => if (!comp.config.shared_memory) {
951 return diags.failParse(path, "object requires shared-mem but compilation disables it", .{});
952 },
953 else => {
954 const f = feat.tag.toCpuFeature().?;
955 if (!std.Target.wasm.featureSetHas(target_features, f)) {
956 return diags.failParse(
957 path,
958 "object requires {s} but specified target features exclude {s}",
959 .{ @tagName(feat.tag), @tagName(f) },
960 );
492961 }
493 try parser.object.relocatable_data.put(gpa, .data, try relocatable_data.toOwnedSlice());
494962 },
495 else => try parser.reader.reader().skipBytes(len, .{}),
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 });
963 },
558964 }
559
560 try parser.object.relocations.putNoClobber(gpa, section, relocations);
561965 }
562966
563 /// Parses the "linking" custom section. Versions that are not
564 /// supported will be an error. `payload_size` is required to be able
565 /// to calculate the subsections we need to parse, as that data is not
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 }
967 // Apply function type information.
968 for (ss.func_type_indexes.items, wasm.object_functions.items[functions_start..]) |func_type, *func| {
969 func.type_index = func_type.ptr(ss).*;
578970 }
579971
580 /// Parses a `spec.Subsection`.
581 /// The `reader` param for this is to provide a `LimitedReader`, which allows
582 /// us to only read until a max length.
583 ///
584 /// `parser` is used to provide access to other sections that may be needed,
585 /// such as access to the `import` section to find the name of a symbol.
586 fn parseSubsection(parser: *Parser, gpa: Allocator, reader: anytype) !void {
587 const wasm = parser.wasm;
588 const sub_type = try leb.readUleb128(u8, reader);
589 log.debug("Found subsection: {s}", .{@tagName(@as(Wasm.SubsectionType, @enumFromInt(sub_type)))});
590 const payload_len = try leb.readUleb128(u32, reader);
591 if (payload_len == 0) return;
592
593 var limited = std.io.limitedReader(reader, payload_len);
594 const limited_reader = limited.reader();
595
596 // every subsection contains a 'count' field
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,
972 // Apply symbol table information.
973 for (ss.symbol_table.items) |symbol| switch (symbol.pointee) {
974 .function_import => |index| {
975 const ptr = index.ptr(ss);
976 const name = symbol.name.unwrap() orelse ptr.name;
977 if (symbol.flags.binding == .local) {
978 diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
979 continue;
980 }
981 const gop = try wasm.object_function_imports.getOrPut(gpa, name);
982 const fn_ty_index = ptr.function_index.ptr(ss).*;
983 if (gop.found_existing) {
984 if (gop.value_ptr.type != fn_ty_index) {
985 var err = try diags.addErrorWithNotes(2);
986 try err.addMsg("symbol '{s}' mismatching function signatures", .{name.slice(wasm)});
987 gop.value_ptr.source_location.addNote(&err, "imported as {} here", .{
988 gop.value_ptr.type.fmt(wasm),
617989 });
618
619 // support legacy object files that specified being TLS by the name instead of the TLS flag.
620 if (!segment.isTLS() and (std.mem.startsWith(u8, segment.name, ".tdata") or std.mem.startsWith(u8, segment.name, ".tbss"))) {
621 // set the flag so we can simply check for the flag in the rest of the linker.
622 segment.flags |= @intFromEnum(Wasm.NamedSegment.Flags.WASM_SEG_FLAG_TLS);
990 source_location.addNote(&err, "imported as {} here", .{fn_ty_index.fmt(wasm)});
991 continue;
992 }
993 if (gop.value_ptr.module_name != ptr.module_name.toOptional()) {
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", .{});
6231000 }
1001 source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
1002 continue;
6241003 }
625 parser.object.segment_info = segments;
626 },
627 .WASM_INIT_FUNCS => {
628 const funcs = try gpa.alloc(Wasm.InitFunc, count);
629 errdefer gpa.free(funcs);
630 for (funcs) |*func| {
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 });
1004 if (gop.value_ptr.name != ptr.name) {
1005 var err = try diags.addErrorWithNotes(2);
1006 try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)});
1007 gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)});
1008 source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)});
1009 continue;
6361010 }
637 parser.object.init_funcs = funcs;
638 },
639 .WASM_COMDAT_INFO => {
640 const comdats = try gpa.alloc(Wasm.Comdat, count);
641 errdefer gpa.free(comdats);
642 for (comdats) |*comdat| {
643 const name_len = try leb.readUleb128(u32, reader);
644 const name = try gpa.alloc(u8, name_len);
645 errdefer gpa.free(name);
646 try reader.readNoEof(name);
647
648 const flags = try leb.readUleb128(u32, reader);
649 if (flags != 0) {
650 return error.UnexpectedValue;
651 }
652
653 const symbol_count = try leb.readUleb128(u32, reader);
654 const symbols = try gpa.alloc(Wasm.ComdatSym, symbol_count);
655 errdefer gpa.free(symbols);
656 for (symbols) |*symbol| {
657 symbol.* = .{
658 .kind = @as(Wasm.ComdatSym.Type, @enumFromInt(try leb.readUleb128(u8, reader))),
659 .index = try leb.readUleb128(u32, reader),
660 };
1011 } else {
1012 gop.value_ptr.* = .{
1013 .flags = symbol.flags,
1014 .module_name = ptr.module_name.toOptional(),
1015 .name = ptr.name,
1016 .source_location = source_location,
1017 .resolution = .unresolved,
1018 .type = fn_ty_index,
1019 };
1020 }
1021 },
1022 .global_import => |index| {
1023 const ptr = index.ptr(ss);
1024 const name = symbol.name.unwrap() orelse ptr.name;
1025 if (symbol.flags.binding == .local) {
1026 diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
1027 continue;
1028 }
1029 const gop = try wasm.object_global_imports.getOrPut(gpa, name);
1030 if (gop.found_existing) {
1031 const existing_ty = gop.value_ptr.type();
1032 if (ptr.valtype != existing_ty.valtype) {
1033 var err = try diags.addErrorWithNotes(2);
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", .{});
6611057 }
662
663 comdat.* = .{
664 .name = name,
665 .flags = flags,
666 .symbols = symbols,
667 };
1058 source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
1059 continue;
6681060 }
669
670 parser.object.comdat_info = comdats;
671 },
672 .WASM_SYMBOL_TABLE => {
673 var symbols = try std.ArrayList(Symbol).initCapacity(gpa, count);
674
675 var i: usize = 0;
676 while (i < count) : (i += 1) {
677 const symbol = symbols.addOneAssumeCapacity();
678 symbol.* = try parser.parseSymbol(gpa, reader);
679 log.debug("Found symbol: type({s}) name({s}) flags(0b{b:0>8})", .{
680 @tagName(symbol.tag),
681 wasm.stringSlice(symbol.name),
682 symbol.flags,
1061 if (gop.value_ptr.name != ptr.name) {
1062 var err = try diags.addErrorWithNotes(2);
1063 try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)});
1064 gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)});
1065 source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)});
1066 continue;
1067 }
1068 } else {
1069 gop.value_ptr.* = .{
1070 .flags = symbol.flags,
1071 .module_name = ptr.module_name.toOptional(),
1072 .name = ptr.name,
1073 .source_location = source_location,
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),
6831104 });
1105 source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
1106 continue;
6841107 }
685
686 // we found all symbols, check for indirect function table
687 // in case of an MVP object file
688 if (try parser.object.checkLegacyIndirectFunctionTable(parser.wasm)) |symbol| {
689 try symbols.append(symbol);
690 log.debug("Found legacy indirect function table. Created symbol", .{});
1108 if (gop.value_ptr.name != ptr.name) {
1109 var err = try diags.addErrorWithNotes(2);
1110 try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)});
1111 gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)});
1112 source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)});
1113 continue;
6911114 }
692
693 // Not all debug sections may be represented by a symbol, for those sections
694 // we manually create a symbol.
695 if (parser.object.relocatable_data.get(.custom)) |custom_sections| {
696 for (custom_sections) |*data| {
697 if (!data.represented) {
698 const name = wasm.castToString(data.index);
699 try symbols.append(.{
700 .name = name,
701 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
702 .tag = .section,
703 .virtual_address = 0,
704 .index = data.section_index,
705 });
706 data.represented = true;
707 log.debug("Created synthetic custom section symbol for '{s}'", .{
708 wasm.stringSlice(name),
709 });
710 }
711 }
1115 if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong;
1116 if (!symbol.flags.visibility_hidden) gop.value_ptr.flags.visibility_hidden = false;
1117 if (symbol.flags.no_strip) gop.value_ptr.flags.no_strip = true;
1118 } else {
1119 gop.value_ptr.* = .{
1120 .flags = symbol.flags,
1121 .module_name = ptr.module_name,
1122 .name = ptr.name,
1123 .source_location = source_location,
1124 .resolution = .unresolved,
1125 .limits_min = ptr.limits_min,
1126 .limits_max = ptr.limits_max,
1127 };
1128 gop.value_ptr.flags.limits_has_max = ptr.limits_has_max;
1129 gop.value_ptr.flags.limits_is_shared = ptr.limits_is_shared;
1130 gop.value_ptr.flags.ref_type = .from(ptr.ref_type);
1131 }
1132 },
1133 .data_import => {
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;
7121164 }
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;
7151312 },
7161313 }
7171314 }
7181315
719 /// Parses the symbol information based on its kind,
720 /// requires access to `Object` to find the name of a symbol when it's
721 /// an import and flag `WASM_SYM_EXPLICIT_NAME` is not set.
722 fn parseSymbol(parser: *Parser, gpa: Allocator, reader: anytype) !Symbol {
723 const wasm = parser.wasm;
724 const tag: Symbol.Tag = @enumFromInt(try leb.readUleb128(u8, reader));
725 const flags = try leb.readUleb128(u32, reader);
726 var symbol: Symbol = .{
727 .flags = flags,
728 .tag = tag,
729 .name = undefined,
730 .index = undefined,
731 .virtual_address = undefined,
1316 // Apply segment_info.
1317 const data_segments = wasm.object_data_segments.items[data_segment_start..];
1318 if (data_segments.len != ss.segment_info.items.len) {
1319 return diags.failParse(path, "expected {d} segment_info entries; found {d}", .{
1320 data_segments.len, ss.segment_info.items.len,
1321 });
1322 }
1323 for (data_segments, ss.segment_info.items) |*data, info| {
1324 data.name = info.name.toOptional();
1325 data.flags = .{
1326 .is_passive = data.flags.is_passive,
1327 .strings = info.flags.strings,
1328 .tls = info.flags.tls,
1329 .retain = info.flags.retain,
1330 .alignment = info.flags.alignment,
7321331 };
1332 }
7331333
734 switch (tag) {
735 .data => {
736 const name_len = try leb.readUleb128(u32, reader);
737 const name = try gpa.alloc(u8, name_len);
738 defer gpa.free(name);
739 try reader.readNoEof(name);
740 symbol.name = try wasm.internString(name);
741
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 },
1334 // Check for indirect function table in case of an MVP object file.
1335 legacy_indirect_function_table: {
1336 // If there is a symbol for each import table, this is not a legacy object file.
1337 if (ss.table_imports.items.len == table_import_symbol_count) break :legacy_indirect_function_table;
1338 if (table_import_symbol_count != 0) {
1339 return diags.failParse(path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
1340 ss.table_imports.items.len, table_import_symbol_count,
1341 });
7731342 }
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 });
7751371 }
776};
7771372
778/// First reads the count from the reader and then allocate
779/// a slice of ptr child's element type.
780fn readVec(ptr: anytype, reader: anytype, gpa: Allocator) ![]ElementType(@TypeOf(ptr)) {
781 const len = try readLeb(u32, reader);
782 const slice = try gpa.alloc(ElementType(@TypeOf(ptr)), len);
783 ptr.* = slice;
784 return slice;
1373 const functions_len: u32 = @intCast(wasm.object_functions.items.len - functions_start);
1374 if (functions_len > 0 and code_section_index == null)
1375 return diags.failParse(path, "code section missing ({d} functions)", .{functions_len});
1376
1377 return .{
1378 .version = version,
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 };
7851420}
7861421
787fn ElementType(comptime ptr: type) type {
788 return meta.Elem(meta.Child(ptr));
1422/// Based on the "features" custom section, parses it into a list of
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 };
7891460}
7901461
791/// Uses either `readIleb128` or `readUleb128` depending on the
792/// signedness of the given type `T`.
793/// Asserts `T` is an integer.
794fn readLeb(comptime T: type, reader: anytype) !T {
795 return switch (@typeInfo(T).int.signedness) {
796 .signed => try leb.readIleb128(T, reader),
797 .unsigned => try leb.readUleb128(T, reader),
1462fn readLeb(comptime T: type, bytes: []const u8, pos: usize) struct { T, usize } {
1463 var fbr = std.io.fixedBufferStream(bytes[pos..]);
1464 return .{
1465 switch (@typeInfo(T).int.signedness) {
1466 .signed => std.leb.readIleb128(T, fbr.reader()) catch unreachable,
1467 .unsigned => std.leb.readUleb128(T, fbr.reader()) catch unreachable,
1468 },
1469 pos + fbr.pos,
7981470 };
7991471}
8001472
801/// Reads an enum type from the given reader.
802/// Asserts `T` is an enum
803fn readEnum(comptime T: type, reader: anytype) !T {
804 switch (@typeInfo(T)) {
805 .@"enum" => |enum_type| return @as(T, @enumFromInt(try readLeb(enum_type.tag_type, reader))),
806 else => @compileError("T must be an enum. Instead was given type " ++ @typeName(T)),
807 }
1473fn readBytes(bytes: []const u8, start_pos: usize) struct { []const u8, usize } {
1474 const len, const pos = readLeb(u32, bytes, start_pos);
1475 return .{
1476 bytes[pos..][0..len],
1477 pos + len,
1478 };
8081479}
8091480
810fn readLimits(reader: anytype) !std.wasm.Limits {
811 const flags = try reader.readByte();
812 const min = try readLeb(u32, reader);
813 var limits: std.wasm.Limits = .{
1481fn readEnum(comptime T: type, bytes: []const u8, pos: usize) struct { T, usize } {
1482 const Tag = @typeInfo(T).@"enum".tag_type;
1483 const int, const new_pos = readLeb(Tag, bytes, pos);
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 .{ .{
8141492 .flags = flags,
8151493 .min = min,
816 .max = undefined,
817 };
818 if (limits.hasFlag(.WASM_LIMITS_FLAG_HAS_MAX)) {
819 limits.max = try readLeb(u32, reader);
820 }
821 return limits;
1494 .max = max,
1495 }, end_pos };
8221496}
8231497
824fn readInit(reader: anytype) !std.wasm.InitExpression {
825 const opcode = try reader.readByte();
826 const init_expr: std.wasm.InitExpression = switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) {
827 .i32_const => .{ .i32_const = try readLeb(i32, reader) },
828 .global_get => .{ .global_get = try readLeb(u32, reader) },
829 else => @panic("TODO: initexpression for other opcodes"),
830 };
1498fn readInit(wasm: *Wasm, bytes: []const u8, pos: usize) !struct { Wasm.Expr, usize } {
1499 const end_pos = try skipInit(bytes, pos); // one after the end opcode
1500 return .{ try wasm.addExpr(bytes[pos..end_pos]), end_pos };
1501}
8311502
832 if ((try readEnum(std.wasm.Opcode, reader)) != .end) return error.MissingEndForExpression;
833 return init_expr;
1503pub fn exprEndPos(bytes: []const u8, pos: usize) error{InvalidInitOpcode}!usize {
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 };
8341513}
8351514
836fn assertEnd(reader: anytype) !void {
837 var buf: [1]u8 = undefined;
838 const len = try reader.read(&buf);
839 if (len != 0) return error.MalformedSection;
840 if (reader.context.bytes_left != 0) return error.MalformedSection;
1515fn skipInit(bytes: []const u8, pos: usize) !usize {
1516 const end_pos = try exprEndPos(bytes, pos);
1517 const op, const final_pos = readEnum(std.wasm.Opcode, bytes, end_pos);
1518 if (op != .end) return error.InitExprMissingEnd;
1519 return final_pos;
8411520}
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 {
7575 process.exit(1);
7676}
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
7882const normal_usage =
7983 \\Usage: zig [command] [options]
8084 \\
src/register_manager.zig+9-14
......@@ -14,19 +14,14 @@ const link = @import("link.zig");
1414
1515const log = std.log.scoped(.register_manager);
1616
17pub const AllocateRegistersError = error{
18 /// No registers are available anymore
17pub const AllocationError = error{
1918 OutOfRegisters,
20 /// Can happen when spilling an instruction in codegen runs out of
21 /// memory, so we propagate that error
2219 OutOfMemory,
23 /// Can happen when spilling an instruction in codegen triggers integer
24 /// overflow, so we propagate that error
20 /// Compiler was asked to operate on a number larger than supported.
2521 Overflow,
26 /// Can happen when spilling an instruction triggers a codegen
27 /// error, so we propagate that error
22 /// Indicates the error is already stored in `failed_codegen` on the Zcu.
2823 CodegenFail,
29} || link.File.UpdateDebugInfoError;
24};
3025
3126pub fn RegisterManager(
3227 comptime Function: type,
......@@ -281,7 +276,7 @@ pub fn RegisterManager(
281276 comptime count: comptime_int,
282277 insts: [count]?Air.Inst.Index,
283278 register_class: RegisterBitSet,
284 ) AllocateRegistersError![count]Register {
279 ) AllocationError![count]Register {
285280 comptime assert(count > 0 and count <= tracked_registers.len);
286281
287282 var locked_registers = self.locked_registers;
......@@ -338,7 +333,7 @@ pub fn RegisterManager(
338333 self: *Self,
339334 inst: ?Air.Inst.Index,
340335 register_class: RegisterBitSet,
341 ) AllocateRegistersError!Register {
336 ) AllocationError!Register {
342337 return (try self.allocRegs(1, .{inst}, register_class))[0];
343338 }
344339
......@@ -349,7 +344,7 @@ pub fn RegisterManager(
349344 self: *Self,
350345 tracked_index: TrackedIndex,
351346 inst: ?Air.Inst.Index,
352 ) AllocateRegistersError!void {
347 ) AllocationError!void {
353348 log.debug("getReg {} for inst {?}", .{ regAtTrackedIndex(tracked_index), inst });
354349 if (!self.isRegIndexFree(tracked_index)) {
355350 // Move the instruction that was previously there to a
......@@ -362,7 +357,7 @@ pub fn RegisterManager(
362357 }
363358 self.getRegIndexAssumeFree(tracked_index, inst);
364359 }
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 {
366361 log.debug("getting reg: {}", .{reg});
367362 return self.getRegIndex(indexOfRegIntoTracked(reg) orelse return, inst);
368363 }
......@@ -370,7 +365,7 @@ pub fn RegisterManager(
370365 self: *Self,
371366 comptime reg: Register,
372367 inst: ?Air.Inst.Index,
373 ) AllocateRegistersError!void {
368 ) AllocationError!void {
374369 return self.getRegIndex((comptime indexOfRegIntoTracked(reg)) orelse return, inst);
375370 }
376371
test/behavior.zig+10-3
......@@ -31,8 +31,6 @@ test {
3131 _ = @import("behavior/error.zig");
3232 _ = @import("behavior/eval.zig");
3333 _ = @import("behavior/export_builtin.zig");
34 _ = @import("behavior/export_self_referential_type_info.zig");
35 _ = @import("behavior/extern.zig");
3634 _ = @import("behavior/field_parent_ptr.zig");
3735 _ = @import("behavior/floatop.zig");
3836 _ = @import("behavior/fn.zig");
......@@ -45,7 +43,6 @@ test {
4543 _ = @import("behavior/hasfield.zig");
4644 _ = @import("behavior/if.zig");
4745 _ = @import("behavior/import.zig");
48 _ = @import("behavior/import_c_keywords.zig");
4946 _ = @import("behavior/incomplete_struct_param_tld.zig");
5047 _ = @import("behavior/inline_switch.zig");
5148 _ = @import("behavior/int128.zig");
......@@ -127,6 +124,16 @@ test {
127124 {
128125 _ = @import("behavior/export_keyword.zig");
129126 }
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 }
130137}
131138
132139// This bug only repros in the root file
test/behavior/export_builtin.zig+15-1
......@@ -6,6 +6,11 @@ test "exporting enum value" {
66 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
77 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
914 const S = struct {
1015 const E = enum(c_int) { one, two };
1116 const e: E = .two;
......@@ -33,6 +38,11 @@ test "exporting using namespace access" {
3338 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
3439 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
3646 const S = struct {
3747 const Inner = struct {
3848 const x: u32 = 5;
......@@ -46,7 +56,6 @@ test "exporting using namespace access" {
4656}
4757
4858test "exporting comptime-known value" {
49 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
5059 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
5160 if (builtin.zig_backend == .stage2_x86_64 and
5261 (builtin.target.ofmt != .elf and
......@@ -56,6 +65,11 @@ test "exporting comptime-known value" {
5665 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
5766 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
5973 const x: u32 = 10;
6074 @export(&x, .{ .name = "exporting_comptime_known_value_foo" });
6175 const S = struct {
test/incremental/add_decl+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67const std = @import("std");
test/incremental/add_decl_namespaced+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67const std = @import("std");
test/incremental/change_generic_line_number+1
......@@ -1,4 +1,5 @@
11#target=x86_64-linux-selfhosted
2#target=wasm32-wasi-selfhosted
23#update=initial version
34#file=main.zig
45const std = @import("std");
test/incremental/change_line_number+1
......@@ -1,4 +1,5 @@
11#target=x86_64-linux-selfhosted
2#target=wasm32-wasi-selfhosted
23#update=initial version
34#file=main.zig
45const std = @import("std");
test/incremental/change_shift_op+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67pub fn main() !void {
test/incremental/change_struct_same_fields+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67const S = extern struct { x: u8, y: u8 };
test/incremental/compile_error_then_log+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version with compile error
56#file=main.zig
67comptime {
test/incremental/delete_comptime_decls+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67pub fn main() void {}
test/incremental/fix_astgen_failure+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version with error
56#file=main.zig
67pub fn main() !void {
test/incremental/hello+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67const std = @import("std");
test/incremental/modify_inline_fn+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67const std = @import("std");
test/incremental/move_src+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67const std = @import("std");
test/incremental/recursive_function_becomes_non_recursive+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67pub fn main() !void {
test/incremental/remove_enum_field+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67const MyEnum = enum(u8) {
test/incremental/remove_invalid_union_backing_enum+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67const E = enum { a, b, c };
test/incremental/temporary_parse_error+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67const std = @import("std");
test/incremental/type_becomes_comptime_only+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67const SomeType = u32;
test/incremental/unreferenced_error+1
......@@ -1,6 +1,7 @@
11#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
4#target=wasm32-wasi-selfhosted
45#update=initial version
56#file=main.zig
67const std = @import("std");
test/link/build.zig.zon-6
......@@ -24,9 +24,6 @@
2424 .wasm_basic_features = .{
2525 .path = "wasm/basic-features",
2626 },
27 .wasm_bss = .{
28 .path = "wasm/bss",
29 },
3027 .wasm_export = .{
3128 .path = "wasm/export",
3229 },
......@@ -48,9 +45,6 @@
4845 .wasm_producers = .{
4946 .path = "wasm/producers",
5047 },
51 .wasm_segments = .{
52 .path = "wasm/segments",
53 },
5448 .wasm_shared_memory = .{
5549 .path = "wasm/shared-memory",
5650 },
test/link/wasm/archive/build.zig-2
......@@ -1,7 +1,5 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
53pub fn build(b: *std.Build) void {
64 const test_step = b.step("test", "Test it");
75 b.default_step = test_step;
test/link/wasm/basic-features/build.zig-2
......@@ -1,7 +1,5 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
53pub fn build(b: *std.Build) void {
64 // Library with explicitly set cpu features
75 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 {
44 const test_step = b.step("test", "Test");
55 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
127 const lib = b.addExecutable(.{
138 .name = "lib",
149 .root_module = b.createModule(.{
1510 .root_source_file = b.path("lib.zig"),
16 .optimize = .ReleaseSafe, // to make the output deterministic in address positions
11 .optimize = .Debug,
1712 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
1813 }),
1914 });
2015 lib.entry = .disabled;
2116 lib.use_lld = false;
2217 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 parse
24
25 const check_lib = lib.checkObject();
26
27 check_lib.checkInHeaders();
28 check_lib.checkExact("Section global");
29 check_lib.checkExact("entries 3");
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");
18 // Object being linked has neither functions nor globals named "foo" or "bar" and
19 // so these names correctly fail to be exported when creating an executable.
20 lib.expect_errors = .{ .exact = &.{
21 "error: manually specified export name 'foo' undefined",
22 "error: manually specified export name 'bar' undefined",
23 } };
24 _ = lib.getEmittedBin();
4925
50 test_step.dependOn(&check_lib.step);
26 test_step.dependOn(&lib.step);
5127}
test/link/wasm/export/build.zig+2-7
......@@ -1,22 +1,17 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
53pub fn build(b: *std.Build) void {
64 const test_step = b.step("test", "Test it");
75 b.default_step = test_step;
86
97 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
138}
149
1510fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
1611 const no_export = b.addExecutable(.{
1712 .name = "no-export",
1813 .root_module = b.createModule(.{
19 .root_source_file = b.path("main.zig"),
14 .root_source_file = b.path("main-hidden.zig"),
2015 .optimize = optimize,
2116 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
2217 }),
......@@ -41,7 +36,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
4136 const force_export = b.addExecutable(.{
4237 .name = "force",
4338 .root_module = b.createModule(.{
44 .root_source_file = b.path("main.zig"),
39 .root_source_file = b.path("main-hidden.zig"),
4540 .optimize = optimize,
4641 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
4742 }),
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 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
53pub fn build(b: *std.Build) void {
64 const test_step = b.step("test", "Test it");
75 b.default_step = test_step;
86
97 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
138}
149
1510fn 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 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
53pub fn build(b: *std.Build) void {
64 const test_step = b.step("test", "Test it");
75 b.default_step = test_step;
86
97 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
138}
149
1510fn 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
3011 const export_table = b.addExecutable(.{
3112 .name = "export_table",
3213 .root_module = b.createModule(.{
......@@ -54,24 +35,12 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
5435 regular_table.use_lld = false;
5536 regular_table.link_gc_sections = false; // Ensure function table is not empty
5637
57 const check_import = import_table.checkObject();
5838 const check_export = export_table.checkObject();
5939 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
7241 check_export.checkInHeaders();
7342 check_export.checkExact("Section export");
74 check_export.checkExact("entries 2");
43 check_export.checkExact("entries 3");
7544 check_export.checkExact("name __indirect_function_table"); // as per linker specification
7645 check_export.checkExact("kind table");
7746
......@@ -89,7 +58,6 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
8958 check_regular.checkExact("i32.const 1"); // we want to start function indexes at 1
9059 check_regular.checkExact("indexes 1"); // 1 function pointer
9160
92 test_step.dependOn(&check_import.step);
9361 test_step.dependOn(&check_export.step);
9462 test_step.dependOn(&check_regular.step);
9563}
test/link/wasm/infer-features/build.zig+3-22
......@@ -1,7 +1,5 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
53pub fn build(b: *std.Build) void {
64 // Wasm Object file which we will use to infer the features from
75 const c_obj = b.addObject(.{
......@@ -37,27 +35,10 @@ pub fn build(b: *std.Build) void {
3735 lib.use_lld = false;
3836 lib.root_module.addObject(c_obj);
3937
40 // Verify the result contains the features from the C Object file.
41 const check = lib.checkObject();
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");
38 lib.expect_errors = .{ .contains = "error: object requires atomics but specified target features exclude atomics" };
39 _ = lib.getEmittedBin();
5940
6041 const test_step = b.step("test", "Run linker test");
61 test_step.dependOn(&check.step);
42 test_step.dependOn(&lib.step);
6243 b.default_step = test_step;
6344}
test/link/wasm/producers/build.zig-2
......@@ -1,8 +1,6 @@
11const std = @import("std");
22const builtin = @import("builtin");
33
4pub const requires_stage2 = true;
5
64pub fn build(b: *std.Build) void {
75 const test_step = b.step("test", "Test it");
86 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 {
66
77 add(b, test_step, .Debug);
88 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
119}
1210
1311fn 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
4543 check_exe.checkInHeaders();
4644 check_exe.checkExact("Section export");
4745 check_exe.checkExact("entries 2");
46 check_exe.checkExact("name foo");
4847 check_exe.checkExact("name memory"); // ensure we also export memory again
4948
5049 // 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
7170 check_exe.checkExact("type function");
7271 if (optimize_mode == .Debug) {
7372 check_exe.checkExact("name __wasm_init_memory");
73 check_exe.checkExact("name __wasm_init_tls");
7474 }
75 check_exe.checkExact("name __wasm_init_tls");
7675 check_exe.checkExact("type global");
7776
7877 // In debug mode the symbol __tls_base is resolved to an undefined symbol
7978 // from the object file, hence its placement differs than in release modes
8079 // where the entire tls segment is optimized away, and tls_base will have
8180 // 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");
8781 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");
8887 check_exe.checkExact("names 1");
8988 check_exe.checkExact("index 0");
9089 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");
9194 }
9295
9396 test_step.dependOn(&check_exe.step);
test/link/wasm/stack_pointer/build.zig-2
......@@ -1,7 +1,5 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
53pub fn build(b: *std.Build) void {
64 const test_step = b.step("test", "Test it");
75 b.default_step = test_step;
test/link/wasm/type/build.zig-5
......@@ -1,15 +1,10 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
53pub fn build(b: *std.Build) void {
64 const test_step = b.step("test", "Test it");
75 b.default_step = test_step;
86
97 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
138}
149
1510fn 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 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
53pub fn build(b: *std.Build) void {
64 const test_step = b.step("test", "Test the program");
75 b.default_step = test_step;
test/tests.zig+5
......@@ -1375,6 +1375,7 @@ const ModuleTestOptions = struct {
13751375 skip_single_threaded: bool,
13761376 skip_non_native: bool,
13771377 skip_libc: bool,
1378 use_llvm: ?bool = null,
13781379 max_rss: usize = 0,
13791380 no_builtin: bool = false,
13801381 build_options: ?*std.Build.Step.Options = null,
......@@ -1411,6 +1412,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
14111412 if (options.skip_single_threaded and test_target.single_threaded == true)
14121413 continue;
14131414
1415 if (options.use_llvm) |use_llvm| {
1416 if (test_target.use_llvm != use_llvm) continue;
1417 }
1418
14141419 // TODO get compiler-rt tests passing for self-hosted backends.
14151420 if ((target.cpu.arch != .x86_64 or target.ofmt != .elf) and
14161421 test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt"))