authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-05 23:46:46-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:35-08:00
loge521879e4730fd85a92081c0040db7dc5daad8a3
treebe07a1a575ad2728027846e0b8cce0f50b807cf0
parentb9355edfb1db042098bc232cf8e52e079f4fcf4e

rewrite wasm/Emit.zig

mainly, rework how relocations works. This is the point at which symbol indexes are known - not before. And don't emit unnecessary relocations! They're only needed when emitting an object file. Changes wasm linker to keep MIR around long-lived so that fixups can be reapplied after linker garbage collection. use labeled switch while we're at it

11 files changed, 1075 insertions(+), 1118 deletions(-)

src/arch/wasm/CodeGen.zig+366-332
......@@ -7,6 +7,7 @@ const leb = std.leb;
77const mem = std.mem;
88const log = std.log.scoped(.codegen);
99
10const CodeGen = @This();
1011const codegen = @import("../../codegen.zig");
1112const Zcu = @import("../../Zcu.zig");
1213const InternPool = @import("../../InternPool.zig");
......@@ -24,6 +25,98 @@ const abi = @import("abi.zig");
2425const Alignment = InternPool.Alignment;
2526const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
2627const errUnionErrorOffset = codegen.errUnionErrorOffset;
28const Wasm = link.File.Wasm;
29
30/// Reference to the function declaration the code
31/// section belongs to
32owner_nav: InternPool.Nav.Index,
33/// Current block depth. Used to calculate the relative difference between a break
34/// and block
35block_depth: u32 = 0,
36air: Air,
37liveness: Liveness,
38gpa: mem.Allocator,
39func_index: InternPool.Index,
40/// Contains a list of current branches.
41/// When we return from a branch, the branch will be popped from this list,
42/// which means branches can only contain references from within its own branch,
43/// or a branch higher (lower index) in the tree.
44branches: std.ArrayListUnmanaged(Branch) = .empty,
45/// Table to save `WValue`'s generated by an `Air.Inst`
46// values: ValueTable,
47/// Mapping from Air.Inst.Index to block ids
48blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct {
49 label: u32,
50 value: WValue,
51}) = .{},
52/// Maps `loop` instructions to their label. `br` to here repeats the loop.
53loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty,
54/// The index the next local generated will have
55/// NOTE: arguments share the index with locals therefore the first variable
56/// will have the index that comes after the last argument's index
57local_index: u32,
58/// The index of the current argument.
59/// Used to track which argument is being referenced in `airArg`.
60arg_index: u32 = 0,
61/// List of simd128 immediates. Each value is stored as an array of bytes.
62/// This list will only be populated for 128bit-simd values when the target features
63/// are enabled also.
64simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty,
65/// The Target we're emitting (used to call intInfo)
66target: *const std.Target,
67wasm: *link.File.Wasm,
68pt: Zcu.PerThread,
69/// List of MIR Instructions
70mir_instructions: *std.MultiArrayList(Mir.Inst),
71/// Contains extra data for MIR
72mir_extra: *std.ArrayListUnmanaged(u32),
73/// List of all locals' types generated throughout this declaration
74/// used to emit locals count at start of 'code' section.
75locals: *std.ArrayListUnmanaged(u8),
76/// When a function is executing, we store the the current stack pointer's value within this local.
77/// This value is then used to restore the stack pointer to the original value at the return of the function.
78initial_stack_value: WValue = .none,
79/// The current stack pointer subtracted with the stack size. From this value, we will calculate
80/// all offsets of the stack values.
81bottom_stack_value: WValue = .none,
82/// Arguments of this function declaration
83/// This will be set after `resolveCallingConventionValues`
84args: []WValue,
85/// This will only be `.none` if the function returns void, or returns an immediate.
86/// When it returns a pointer to the stack, the `.local` tag will be active and must be populated
87/// before this function returns its execution to the caller.
88return_value: WValue,
89/// The size of the stack this function occupies. In the function prologue
90/// we will move the stack pointer by this number, forward aligned with the `stack_alignment`.
91stack_size: u32 = 0,
92/// The stack alignment, which is 16 bytes by default. This is specified by the
93/// tool-conventions: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md
94/// and also what the llvm backend will emit.
95/// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default.
96stack_alignment: Alignment = .@"16",
97
98// For each individual Wasm valtype we store a seperate free list which
99// allows us to re-use locals that are no longer used. e.g. a temporary local.
100/// A list of indexes which represents a local of valtype `i32`.
101/// It is illegal to store a non-i32 valtype in this list.
102free_locals_i32: std.ArrayListUnmanaged(u32) = .empty,
103/// A list of indexes which represents a local of valtype `i64`.
104/// It is illegal to store a non-i64 valtype in this list.
105free_locals_i64: std.ArrayListUnmanaged(u32) = .empty,
106/// A list of indexes which represents a local of valtype `f32`.
107/// It is illegal to store a non-f32 valtype in this list.
108free_locals_f32: std.ArrayListUnmanaged(u32) = .empty,
109/// A list of indexes which represents a local of valtype `f64`.
110/// It is illegal to store a non-f64 valtype in this list.
111free_locals_f64: std.ArrayListUnmanaged(u32) = .empty,
112/// A list of indexes which represents a local of valtype `v127`.
113/// It is illegal to store a non-v128 valtype in this list.
114free_locals_v128: std.ArrayListUnmanaged(u32) = .empty,
115
116/// When in debug mode, this tracks if no `finishAir` was missed.
117/// Forgetting to call `finishAir` will cause the result to not be
118/// stored in our `values` map and therefore cause bugs.
119air_bookkeeping: @TypeOf(bookkeeping_init) = bookkeeping_init,
27120
28121/// Wasm Value, created when generating an instruction
29122const WValue = union(enum) {
......@@ -601,104 +694,6 @@ test "Wasm - buildOpcode" {
601694/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
602695pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue);
603696
604const CodeGen = @This();
605
606/// Reference to the function declaration the code
607/// section belongs to
608owner_nav: InternPool.Nav.Index,
609src_loc: Zcu.LazySrcLoc,
610/// Current block depth. Used to calculate the relative difference between a break
611/// and block
612block_depth: u32 = 0,
613air: Air,
614liveness: Liveness,
615gpa: mem.Allocator,
616debug_output: link.File.DebugInfoOutput,
617func_index: InternPool.Index,
618/// Contains a list of current branches.
619/// When we return from a branch, the branch will be popped from this list,
620/// which means branches can only contain references from within its own branch,
621/// or a branch higher (lower index) in the tree.
622branches: std.ArrayListUnmanaged(Branch) = .empty,
623/// Table to save `WValue`'s generated by an `Air.Inst`
624// values: ValueTable,
625/// Mapping from Air.Inst.Index to block ids
626blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct {
627 label: u32,
628 value: WValue,
629}) = .{},
630/// Maps `loop` instructions to their label. `br` to here repeats the loop.
631loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty,
632/// `bytes` contains the wasm bytecode belonging to the 'code' section.
633code: *std.ArrayListUnmanaged(u8),
634/// The index the next local generated will have
635/// NOTE: arguments share the index with locals therefore the first variable
636/// will have the index that comes after the last argument's index
637local_index: u32 = 0,
638/// The index of the current argument.
639/// Used to track which argument is being referenced in `airArg`.
640arg_index: u32 = 0,
641/// List of all locals' types generated throughout this declaration
642/// used to emit locals count at start of 'code' section.
643locals: std.ArrayListUnmanaged(u8),
644/// List of simd128 immediates. Each value is stored as an array of bytes.
645/// This list will only be populated for 128bit-simd values when the target features
646/// are enabled also.
647simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty,
648/// The Target we're emitting (used to call intInfo)
649target: *const std.Target,
650/// Represents the wasm binary file that is being linked.
651bin_file: *link.File.Wasm,
652pt: Zcu.PerThread,
653/// List of MIR Instructions
654mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
655/// Contains extra data for MIR
656mir_extra: std.ArrayListUnmanaged(u32) = .empty,
657/// When a function is executing, we store the the current stack pointer's value within this local.
658/// This value is then used to restore the stack pointer to the original value at the return of the function.
659initial_stack_value: WValue = .none,
660/// The current stack pointer subtracted with the stack size. From this value, we will calculate
661/// all offsets of the stack values.
662bottom_stack_value: WValue = .none,
663/// Arguments of this function declaration
664/// This will be set after `resolveCallingConventionValues`
665args: []WValue = &.{},
666/// This will only be `.none` if the function returns void, or returns an immediate.
667/// When it returns a pointer to the stack, the `.local` tag will be active and must be populated
668/// before this function returns its execution to the caller.
669return_value: WValue = .none,
670/// The size of the stack this function occupies. In the function prologue
671/// we will move the stack pointer by this number, forward aligned with the `stack_alignment`.
672stack_size: u32 = 0,
673/// The stack alignment, which is 16 bytes by default. This is specified by the
674/// tool-conventions: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md
675/// and also what the llvm backend will emit.
676/// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default.
677stack_alignment: Alignment = .@"16",
678
679// For each individual Wasm valtype we store a seperate free list which
680// allows us to re-use locals that are no longer used. e.g. a temporary local.
681/// A list of indexes which represents a local of valtype `i32`.
682/// It is illegal to store a non-i32 valtype in this list.
683free_locals_i32: std.ArrayListUnmanaged(u32) = .empty,
684/// A list of indexes which represents a local of valtype `i64`.
685/// It is illegal to store a non-i64 valtype in this list.
686free_locals_i64: std.ArrayListUnmanaged(u32) = .empty,
687/// A list of indexes which represents a local of valtype `f32`.
688/// It is illegal to store a non-f32 valtype in this list.
689free_locals_f32: std.ArrayListUnmanaged(u32) = .empty,
690/// A list of indexes which represents a local of valtype `f64`.
691/// It is illegal to store a non-f64 valtype in this list.
692free_locals_f64: std.ArrayListUnmanaged(u32) = .empty,
693/// A list of indexes which represents a local of valtype `v127`.
694/// It is illegal to store a non-v128 valtype in this list.
695free_locals_v128: std.ArrayListUnmanaged(u32) = .empty,
696
697/// When in debug mode, this tracks if no `finishAir` was missed.
698/// Forgetting to call `finishAir` will cause the result to not be
699/// stored in our `values` map and therefore cause bugs.
700air_bookkeeping: @TypeOf(bookkeeping_init) = bookkeeping_init,
701
702697const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
703698
704699const InnerError = error{
......@@ -719,8 +714,6 @@ pub fn deinit(func: *CodeGen) void {
719714 func.loops.deinit(func.gpa);
720715 func.locals.deinit(func.gpa);
721716 func.simd_immediates.deinit(func.gpa);
722 func.mir_instructions.deinit(func.gpa);
723 func.mir_extra.deinit(func.gpa);
724717 func.free_locals_i32.deinit(func.gpa);
725718 func.free_locals_i64.deinit(func.gpa);
726719 func.free_locals_f32.deinit(func.gpa);
......@@ -729,9 +722,10 @@ pub fn deinit(func: *CodeGen) void {
729722 func.* = undefined;
730723}
731724
732fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
733 const msg = try Zcu.ErrorMsg.create(func.gpa, func.src_loc, fmt, args);
734 return func.pt.zcu.codegenFailMsg(func.owner_nav, msg);
725fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
726 const zcu = cg.pt.zcu;
727 const func = zcu.funcInfo(cg.func_index);
728 return zcu.codegenFail(func.owner_nav, fmt, args);
735729}
736730
737731/// Resolves the `WValue` for the given instruction `inst`
......@@ -767,7 +761,7 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
767761 //
768762 // In the other cases, we will simply lower the constant to a value that fits
769763 // into a single local (such as a pointer, integer, bool, etc).
770 const result: WValue = if (isByRef(ty, pt, func.target.*))
764 const result: WValue = if (isByRef(ty, pt, func.target))
771765 .{ .memory = val.toIntern() }
772766 else
773767 try func.lowerConstant(val, ty);
......@@ -885,8 +879,12 @@ fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!vo
885879 try func.addInst(.{ .tag = tag, .data = .{ .label = label } });
886880}
887881
888fn addCallTagName(func: *CodeGen, ip_index: InternPool.Index) error{OutOfMemory}!void {
889 try func.addInst(.{ .tag = .call_tag_name, .data = .{ .ip_index = ip_index } });
882fn addIpIndex(func: *CodeGen, tag: Mir.Inst.Tag, i: InternPool.Index) Allocator.Error!void {
883 try func.addInst(.{ .tag = tag, .data = .{ .ip_index = i } });
884}
885
886fn addNav(func: *CodeGen, tag: Mir.Inst.Tag, i: InternPool.Nav.Index) Allocator.Error!void {
887 try func.addInst(.{ .tag = tag, .data = .{ .nav_index = i } });
890888}
891889
892890/// Accepts an unsigned 32bit integer rather than a signed integer to
......@@ -900,7 +898,7 @@ fn addImm32(func: *CodeGen, imm: u32) error{OutOfMemory}!void {
900898/// prevent us from having to bitcast multiple times as most values
901899/// within codegen are represented as unsigned rather than signed.
902900fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void {
903 const extra_index = try func.addExtra(Mir.Imm64.fromU64(imm));
901 const extra_index = try func.addExtra(Mir.Imm64.init(imm));
904902 try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });
905903}
906904
......@@ -916,7 +914,7 @@ fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void {
916914}
917915
918916fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {
919 const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float));
917 const extra_index = try func.addExtra(Mir.Float64.init(float));
920918 try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
921919}
922920
......@@ -956,6 +954,8 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32
956954 inline for (fields) |field| {
957955 func.mir_extra.appendAssumeCapacity(switch (field.type) {
958956 u32 => @field(extra, field.name),
957 i32 => @bitCast(@field(extra, field.name)),
958 InternPool.Index => @intFromEnum(@field(extra, field.name)),
959959 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
960960 });
961961 }
......@@ -963,11 +963,11 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32
963963}
964964
965965/// Using a given `Type`, returns the corresponding valtype for .auto callconv
966fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) std.wasm.Valtype {
966fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: *const std.Target) std.wasm.Valtype {
967967 const zcu = pt.zcu;
968968 const ip = &zcu.intern_pool;
969969 return switch (ty.zigTypeTag(zcu)) {
970 .float => switch (ty.floatBits(target)) {
970 .float => switch (ty.floatBits(target.*)) {
971971 16 => .i32, // stored/loaded as u16
972972 32 => .f32,
973973 64 => .f64,
......@@ -1003,14 +1003,14 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) std.wasm.Valty
10031003}
10041004
10051005/// Using a given `Type`, returns the byte representation of its wasm value type
1006fn genValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 {
1006fn genValtype(ty: Type, pt: Zcu.PerThread, target: *const std.Target) u8 {
10071007 return @intFromEnum(typeToValtype(ty, pt, target));
10081008}
10091009
10101010/// Using a given `Type`, returns the corresponding wasm value type
10111011/// Differently from `genValtype` this also allows `void` to create a block
10121012/// with no return type
1013fn genBlockType(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 {
1013fn genBlockType(ty: Type, pt: Zcu.PerThread, target: *const std.Target) u8 {
10141014 return switch (ty.ip_index) {
10151015 .void_type, .noreturn_type => std.wasm.block_empty,
10161016 else => genValtype(ty, pt, target),
......@@ -1028,15 +1028,17 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
10281028 .imm128 => |val| try func.addImm128(val),
10291029 .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
10301030 .float64 => |val| try func.addFloat64(val),
1031 .memory => |ptr| {
1032 const extra_index = try func.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 });
1033 try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
1034 },
1035 .memory_offset => |mem_off| {
1036 const extra_index = try func.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset });
1037 try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
1038 },
1039 .function_index => |index| try func.addLabel(.function_index, index), // write function index and generate relocation
1031 .memory => |ptr| try func.addInst(.{ .tag = .uav_ref, .data = .{ .ip_index = ptr } }),
1032 .memory_offset => |mo| try func.addInst(.{
1033 .tag = .uav_ref_off,
1034 .data = .{
1035 .payload = try func.addExtra(Mir.UavRefOff{
1036 .ip_index = mo.pointer,
1037 .offset = @intCast(mo.offset), // TODO should not be an assert
1038 }),
1039 },
1040 }),
1041 .function_index => |index| try func.addIpIndex(.function_index, index),
10401042 .stack_offset => try func.addLabel(.local_get, func.bottom_stack_value.local.value), // caller must ensure to address the offset
10411043 }
10421044}
......@@ -1075,7 +1077,7 @@ fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue {
10751077/// Returns a corresponding `Wvalue` with `local` as active tag
10761078fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
10771079 const pt = func.pt;
1078 const valtype = typeToValtype(ty, pt, func.target.*);
1080 const valtype = typeToValtype(ty, pt, func.target);
10791081 const index_or_null = switch (valtype) {
10801082 .i32 => func.free_locals_i32.popOrNull(),
10811083 .i64 => func.free_locals_i64.popOrNull(),
......@@ -1095,7 +1097,7 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
10951097/// to use a zero-initialized local.
10961098fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
10971099 const pt = func.pt;
1098 try func.locals.append(func.gpa, genValtype(ty, pt, func.target.*));
1100 try func.locals.append(func.gpa, genValtype(ty, pt, func.target));
10991101 const initial_index = func.local_index;
11001102 func.local_index += 1;
11011103 return .{ .local = .{ .value = initial_index, .references = 1 } };
......@@ -1107,7 +1109,7 @@ fn genFunctype(
11071109 params: []const InternPool.Index,
11081110 return_type: Type,
11091111 pt: Zcu.PerThread,
1110 target: std.Target,
1112 target: *const std.Target,
11111113) !link.File.Wasm.FunctionType.Index {
11121114 const zcu = pt.zcu;
11131115 const gpa = zcu.gpa;
......@@ -1162,150 +1164,206 @@ fn genFunctype(
11621164 });
11631165}
11641166
1165pub fn generate(
1166 bin_file: *link.File,
1167pub const Function = extern struct {
1168 /// Index into `Wasm.mir_instructions`.
1169 mir_off: u32,
1170 /// This is unused except for as a safety slice bound and could be removed.
1171 mir_len: u32,
1172 /// Index into `Wasm.mir_extra`.
1173 mir_extra_off: u32,
1174 /// This is unused except for as a safety slice bound and could be removed.
1175 mir_extra_len: u32,
1176 locals_off: u32,
1177 locals_len: u32,
1178 prologue: Prologue,
1179
1180 pub const Prologue = extern struct {
1181 flags: Flags,
1182 sp_local: u32,
1183 stack_size: u32,
1184 bottom_stack_local: u32,
1185
1186 pub const Flags = packed struct(u32) {
1187 stack_alignment: Alignment,
1188 padding: u26 = 0,
1189 };
1190
1191 pub const none: Prologue = .{
1192 .sp_local = 0,
1193 .flags = .{ .stack_alignment = .none },
1194 .stack_size = 0,
1195 .bottom_stack_local = 0,
1196 };
1197
1198 pub fn isNone(p: *const Prologue) bool {
1199 return p.flags.stack_alignment != .none;
1200 }
1201 };
1202
1203 pub fn lower(f: *Function, wasm: *const Wasm, code: *std.ArrayList(u8)) Allocator.Error!void {
1204 const gpa = wasm.base.comp.gpa;
1205
1206 // Write the locals in the prologue of the function body.
1207 const locals = wasm.all_zcu_locals[f.locals_off..][0..f.locals_len];
1208 try code.ensureUnusedCapacity(gpa, 5 + locals.len * 6 + 38);
1209
1210 std.leb.writeUleb128(code.writer(gpa), @as(u32, @intCast(locals.len))) catch unreachable;
1211 for (locals) |local| {
1212 std.leb.writeUleb128(code.writer(gpa), @as(u32, 1)) catch unreachable;
1213 code.appendAssumeCapacity(local);
1214 }
1215
1216 // Stack management section of function prologue.
1217 const stack_alignment = f.prologue.flags.stack_alignment;
1218 if (stack_alignment.toByteUnits()) |align_bytes| {
1219 const sp_global = try wasm.stackPointerGlobalIndex();
1220 // load stack pointer
1221 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get));
1222 std.leb.writeULEB128(code.writer(gpa), @intFromEnum(sp_global)) catch unreachable;
1223 // store stack pointer so we can restore it when we return from the function
1224 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));
1225 leb.writeUleb128(code.writer(gpa), f.prologue.sp_local) catch unreachable;
1226 // get the total stack size
1227 const aligned_stack: i32 = @intCast(f.stack_alignment.forward(f.prologue.stack_size));
1228 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1229 leb.writeIleb128(code.writer(gpa), aligned_stack) catch unreachable;
1230 // subtract it from the current stack pointer
1231 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_sub));
1232 // Get negative stack alignment
1233 const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1;
1234 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1235 leb.writeIleb128(code.writer(gpa), neg_stack_align) catch unreachable;
1236 // Bitwise-and the value to get the new stack pointer to ensure the
1237 // pointers are aligned with the abi alignment.
1238 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_and));
1239 // The bottom will be used to calculate all stack pointer offsets.
1240 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));
1241 leb.writeUleb128(code.writer(gpa), f.prologue.bottom_stack_local) catch unreachable;
1242 // Store the current stack pointer value into the global stack pointer so other function calls will
1243 // start from this value instead and not overwrite the current stack.
1244 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
1245 std.leb.writeULEB128(code.writer(gpa), @intFromEnum(sp_global)) catch unreachable;
1246 }
1247
1248 var emit: Emit = .{
1249 .mir = .{
1250 .instruction_tags = wasm.mir_instructions.items(.tag)[f.mir_off..][0..f.mir_len],
1251 .instruction_datas = wasm.mir_instructions.items(.data)[f.mir_off..][0..f.mir_len],
1252 .extra = wasm.mir_extra[f.mir_extra_off..][0..f.mir_extra_len],
1253 },
1254 .wasm = wasm,
1255 .code = code,
1256 };
1257 try emit.lowerToCode();
1258 }
1259};
1260
1261pub const Error = error{
1262 OutOfMemory,
1263 /// Compiler was asked to operate on a number larger than supported.
1264 Overflow,
1265 /// Indicates the error is already stored in Zcu `failed_codegen`.
1266 CodegenFail,
1267};
1268
1269pub fn function(
1270 wasm: *Wasm,
11671271 pt: Zcu.PerThread,
1168 src_loc: Zcu.LazySrcLoc,
11691272 func_index: InternPool.Index,
11701273 air: Air,
11711274 liveness: Liveness,
1172 code: *std.ArrayListUnmanaged(u8),
1173 debug_output: link.File.DebugInfoOutput,
1174) codegen.CodeGenError!void {
1275) Error!Function {
11751276 const zcu = pt.zcu;
11761277 const gpa = zcu.gpa;
11771278 const func = zcu.funcInfo(func_index);
11781279 const file_scope = zcu.navFileScope(func.owner_nav);
11791280 const target = &file_scope.mod.resolved_target.result;
1281 const fn_ty = zcu.navValue(func.owner_nav).typeOf(zcu);
1282 const fn_info = zcu.typeToFunc(fn_ty).?;
1283 const ip = &zcu.intern_pool;
1284 const fn_ty_index = try genFunctype(wasm, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, target);
1285 const returns = fn_ty_index.ptr(wasm).returns.slice(wasm);
1286 const any_returns = returns.len != 0;
1287
1288 var cc_result = try resolveCallingConventionValues(pt, fn_ty, target);
1289 defer cc_result.deinit(gpa);
1290
11801291 var code_gen: CodeGen = .{
11811292 .gpa = gpa,
11821293 .pt = pt,
11831294 .air = air,
11841295 .liveness = liveness,
1185 .code = code,
11861296 .owner_nav = func.owner_nav,
1187 .src_loc = src_loc,
1188 .locals = .{},
11891297 .target = target,
1190 .bin_file = bin_file.cast(.wasm).?,
1191 .debug_output = debug_output,
1298 .wasm = wasm,
11921299 .func_index = func_index,
1300 .args = cc_result.args,
1301 .return_value = cc_result.return_value,
1302 .local_index = cc_result.local_index,
1303 .mir_instructions = &wasm.mir_instructions,
1304 .mir_extra = &wasm.mir_extra,
1305 .locals = &wasm.all_zcu_locals,
11931306 };
11941307 defer code_gen.deinit();
11951308
1196 genFunc(&code_gen) catch |err| switch (err) {
1309 return functionInner(&code_gen, any_returns) catch |err| switch (err) {
11971310 error.CodegenFail => return error.CodegenFail,
11981311 else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}),
11991312 };
12001313}
12011314
1202fn genFunc(func: *CodeGen) InnerError!void {
1203 const wasm = func.bin_file;
1204 const pt = func.pt;
1315fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function {
1316 const wasm = cg.wasm;
1317 const pt = cg.pt;
12051318 const zcu = pt.zcu;
1206 const ip = &zcu.intern_pool;
1207 const fn_ty = zcu.navValue(func.owner_nav).typeOf(zcu);
1208 const fn_info = zcu.typeToFunc(fn_ty).?;
1209 const fn_ty_index = try genFunctype(wasm, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target.*);
12101319
1211 var cc_result = try func.resolveCallingConventionValues(fn_ty);
1212 defer cc_result.deinit(func.gpa);
1320 const start_mir_off: u32 = @intCast(wasm.mir_instructions.len);
1321 const start_mir_extra_off: u32 = @intCast(wasm.mir_extra.items.len);
1322 const start_locals_off: u32 = @intCast(wasm.all_zcu_locals.items.len);
12131323
1214 func.args = cc_result.args;
1215 func.return_value = cc_result.return_value;
1216
1217 try func.addTag(.dbg_prologue_end);
1218
1219 try func.branches.append(func.gpa, .{});
1324 try cg.branches.append(cg.gpa, .{});
12201325 // clean up outer branch
12211326 defer {
1222 var outer_branch = func.branches.pop();
1223 outer_branch.deinit(func.gpa);
1224 assert(func.branches.items.len == 0); // missing branch merge
1327 var outer_branch = cg.branches.pop();
1328 outer_branch.deinit(cg.gpa);
1329 assert(cg.branches.items.len == 0); // missing branch merge
12251330 }
12261331 // Generate MIR for function body
1227 try func.genBody(func.air.getMainBody());
1332 try cg.genBody(cg.air.getMainBody());
12281333
12291334 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)
12301335 // we emit an unreachable instruction to tell the stack validator that part will never be reached.
1231 const returns = fn_ty_index.ptr(wasm).returns.slice(wasm);
1232 if (returns.len != 0 and func.air.instructions.len > 0) {
1233 const inst: Air.Inst.Index = @enumFromInt(func.air.instructions.len - 1);
1234 const last_inst_ty = func.typeOfIndex(inst);
1336 if (any_returns and cg.air.instructions.len > 0) {
1337 const inst: Air.Inst.Index = @enumFromInt(cg.air.instructions.len - 1);
1338 const last_inst_ty = cg.typeOfIndex(inst);
12351339 if (!last_inst_ty.hasRuntimeBitsIgnoreComptime(zcu) or last_inst_ty.isNoReturn(zcu)) {
1236 try func.addTag(.@"unreachable");
1340 try cg.addTag(.@"unreachable");
12371341 }
12381342 }
12391343 // End of function body
1240 try func.addTag(.end);
1241
1242 try func.addTag(.dbg_epilogue_begin);
1243
1244 // check if we have to initialize and allocate anything into the stack frame.
1245 // If so, create enough stack space and insert the instructions at the front of the list.
1246 if (func.initial_stack_value != .none) {
1247 var prologue = std.ArrayList(Mir.Inst).init(func.gpa);
1248 defer prologue.deinit();
1249
1250 const sp = @intFromEnum(wasm.zig_object.?.stack_pointer_sym);
1251 // load stack pointer
1252 try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } });
1253 // store stack pointer so we can restore it when we return from the function
1254 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_stack_value.local.value } });
1255 // get the total stack size
1256 const aligned_stack = func.stack_alignment.forward(func.stack_size);
1257 try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(aligned_stack) } });
1258 // subtract it from the current stack pointer
1259 try prologue.append(.{ .tag = .i32_sub, .data = .{ .tag = {} } });
1260 // Get negative stack alignment
1261 try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @as(i32, @intCast(func.stack_alignment.toByteUnits().?)) * -1 } });
1262 // Bitwise-and the value to get the new stack pointer to ensure the pointers are aligned with the abi alignment
1263 try prologue.append(.{ .tag = .i32_and, .data = .{ .tag = {} } });
1264 // store the current stack pointer as the bottom, which will be used to calculate all stack pointer offsets
1265 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.bottom_stack_value.local.value } });
1266 // Store the current stack pointer value into the global stack pointer so other function calls will
1267 // start from this value instead and not overwrite the current stack.
1268 try prologue.append(.{ .tag = .global_set, .data = .{ .label = sp } });
1269
1270 // reserve space and insert all prologue instructions at the front of the instruction list
1271 // We insert them in reserve order as there is no insertSlice in multiArrayList.
1272 try func.mir_instructions.ensureUnusedCapacity(func.gpa, prologue.items.len);
1273 for (prologue.items, 0..) |_, index| {
1274 const inst = prologue.items[prologue.items.len - 1 - index];
1275 func.mir_instructions.insertAssumeCapacity(0, inst);
1276 }
1277 }
1278
1279 var mir: Mir = .{
1280 .instructions = func.mir_instructions.toOwnedSlice(),
1281 .extra = try func.mir_extra.toOwnedSlice(func.gpa),
1282 };
1283 defer mir.deinit(func.gpa);
1284
1285 var emit: Emit = .{
1286 .mir = mir,
1287 .bin_file = wasm,
1288 .code = func.code,
1289 .locals = func.locals.items,
1290 .owner_nav = func.owner_nav,
1291 .dbg_output = func.debug_output,
1292 .prev_di_line = 0,
1293 .prev_di_column = 0,
1294 .prev_di_offset = 0,
1295 };
1296
1297 emit.emitMir() catch |err| switch (err) {
1298 error.EmitFail => {
1299 func.err_msg = emit.error_msg.?;
1300 return error.CodegenFail;
1344 try cg.addTag(.end);
1345 try cg.addTag(.dbg_epilogue_begin);
1346
1347 return .{
1348 .mir_off = start_mir_off,
1349 .mir_len = @intCast(wasm.mir_instructions.len - start_mir_off),
1350 .mir_extra_off = start_mir_extra_off,
1351 .mir_extra_len = @intCast(wasm.mir_extra.items.len - start_mir_extra_off),
1352 .locals_off = start_locals_off,
1353 .locals_len = @intCast(wasm.all_zcu_locals.items.len - start_locals_off),
1354 .prologue = if (cg.initial_stack_value == .none) .none else .{
1355 .sp_local = cg.initial_stack_value.local.value,
1356 .flags = .{ .stack_alignment = cg.stack_alignment },
1357 .stack_size = cg.stack_size,
1358 .bottom_stack_local = cg.bottom_stack_value.local.value,
13011359 },
1302 else => |e| return e,
13031360 };
13041361}
13051362
13061363const CallWValues = struct {
13071364 args: []WValue,
13081365 return_value: WValue,
1366 local_index: u32,
13091367
13101368 fn deinit(values: *CallWValues, gpa: Allocator) void {
13111369 gpa.free(values.args);
......@@ -1313,28 +1371,34 @@ const CallWValues = struct {
13131371 }
13141372};
13151373
1316fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWValues {
1317 const pt = func.pt;
1374fn resolveCallingConventionValues(
1375 pt: Zcu.PerThread,
1376 fn_ty: Type,
1377 target: *const std.Target,
1378) Allocator.Error!CallWValues {
13181379 const zcu = pt.zcu;
1380 const gpa = zcu.gpa;
13191381 const ip = &zcu.intern_pool;
13201382 const fn_info = zcu.typeToFunc(fn_ty).?;
13211383 const cc = fn_info.cc;
1384
13221385 var result: CallWValues = .{
13231386 .args = &.{},
13241387 .return_value = .none,
1388 .local_index = 0,
13251389 };
13261390 if (cc == .naked) return result;
13271391
1328 var args = std.ArrayList(WValue).init(func.gpa);
1392 var args = std.ArrayList(WValue).init(gpa);
13291393 defer args.deinit();
13301394
13311395 // Check if we store the result as a pointer to the stack rather than
13321396 // by value
1333 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) {
1397 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, target)) {
13341398 // the sret arg will be passed as first argument, therefore we
13351399 // set the `return_value` before allocating locals for regular args.
1336 result.return_value = .{ .local = .{ .value = func.local_index, .references = 1 } };
1337 func.local_index += 1;
1400 result.return_value = .{ .local = .{ .value = result.local_index, .references = 1 } };
1401 result.local_index += 1;
13381402 }
13391403
13401404 switch (cc) {
......@@ -1344,8 +1408,8 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV
13441408 continue;
13451409 }
13461410
1347 try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } });
1348 func.local_index += 1;
1411 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1412 result.local_index += 1;
13491413 }
13501414 },
13511415 .wasm_watc => {
......@@ -1353,18 +1417,23 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV
13531417 const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu);
13541418 for (ty_classes) |class| {
13551419 if (class == .none) continue;
1356 try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } });
1357 func.local_index += 1;
1420 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1421 result.local_index += 1;
13581422 }
13591423 }
13601424 },
1361 else => return func.fail("calling convention '{s}' not supported for Wasm", .{@tagName(cc)}),
1425 else => unreachable, // Frontend is responsible for emitting an error earlier.
13621426 }
13631427 result.args = try args.toOwnedSlice();
13641428 return result;
13651429}
13661430
1367fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu.PerThread, target: std.Target) bool {
1431fn firstParamSRet(
1432 cc: std.builtin.CallingConvention,
1433 return_type: Type,
1434 pt: Zcu.PerThread,
1435 target: *const std.Target,
1436) bool {
13681437 switch (cc) {
13691438 .@"inline" => unreachable,
13701439 .auto => return isByRef(return_type, pt, target),
......@@ -1466,8 +1535,7 @@ fn restoreStackPointer(func: *CodeGen) !void {
14661535 // Get the original stack pointer's value
14671536 try func.emitWValue(func.initial_stack_value);
14681537
1469 // save its value in the global stack pointer
1470 try func.addLabel(.global_set, @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym));
1538 try func.addTag(.global_set_sp);
14711539}
14721540
14731541/// From a given type, will create space on the virtual stack to store the value of such type.
......@@ -1675,7 +1743,7 @@ fn arch(func: *const CodeGen) std.Target.Cpu.Arch {
16751743
16761744/// For a given `Type`, will return true when the type will be passed
16771745/// by reference, rather than by value
1678fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool {
1746fn isByRef(ty: Type, pt: Zcu.PerThread, target: *const std.Target) bool {
16791747 const zcu = pt.zcu;
16801748 const ip = &zcu.intern_pool;
16811749 switch (ty.zigTypeTag(zcu)) {
......@@ -1716,7 +1784,7 @@ fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool {
17161784 .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled,
17171785 .int => return ty.intInfo(zcu).bits > 64,
17181786 .@"enum" => return ty.intInfo(zcu).bits > 64,
1719 .float => return ty.floatBits(target) > 64,
1787 .float => return ty.floatBits(target.*) > 64,
17201788 .error_union => {
17211789 const pl_ty = ty.errorUnionPayload(zcu);
17221790 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
......@@ -1747,7 +1815,7 @@ const SimdStoreStrategy = enum {
17471815/// This means when a given type is 128 bits and either the simd128 or relaxed-simd
17481816/// features are enabled, the function will return `.direct`. This would allow to store
17491817/// it using a instruction, rather than an unrolled version.
1750fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStoreStrategy {
1818fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: *const std.Target) SimdStoreStrategy {
17511819 assert(ty.zigTypeTag(zcu) == .vector);
17521820 if (ty.bitSize(zcu) != 128) return .unrolled;
17531821 const hasFeature = std.Target.wasm.featureSetHas;
......@@ -2076,7 +2144,7 @@ fn airRet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
20762144 .op = .load,
20772145 .width = @as(u8, @intCast(scalar_type.abiSize(zcu) * 8)),
20782146 .signedness = if (scalar_type.isSignedInt(zcu)) .signed else .unsigned,
2079 .valtype1 = typeToValtype(scalar_type, pt, func.target.*),
2147 .valtype1 = typeToValtype(scalar_type, pt, func.target),
20802148 });
20812149 try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
20822150 .offset = operand.offset(),
......@@ -2109,7 +2177,7 @@ fn airRetPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
21092177 }
21102178
21112179 const fn_info = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?;
2112 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) {
2180 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target)) {
21132181 break :result func.return_value;
21142182 }
21152183
......@@ -2131,7 +2199,7 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
21312199 if (ret_ty.isError(zcu)) {
21322200 try func.addImm32(0);
21332201 }
2134 } else if (!firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) {
2202 } else if (!firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target)) {
21352203 // leave on the stack
21362204 _ = try func.load(operand, ret_ty, 0);
21372205 }
......@@ -2142,7 +2210,7 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
21422210}
21432211
21442212fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {
2145 const wasm = func.bin_file;
2213 const wasm = func.wasm;
21462214 if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{});
21472215 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
21482216 const extra = func.air.extraData(Air.Call, pl_op.payload);
......@@ -2159,7 +2227,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
21592227 };
21602228 const ret_ty = fn_ty.fnReturnType(zcu);
21612229 const fn_info = zcu.typeToFunc(fn_ty).?;
2162 const first_param_sret = firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*);
2230 const first_param_sret = firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target);
21632231
21642232 const callee: ?InternPool.Nav.Index = blk: {
21652233 const func_val = (try func.air.value(pl_op.operand, pt)) orelse break :blk null;
......@@ -2199,7 +2267,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
21992267 const operand = try func.resolveInst(pl_op.operand);
22002268 try func.emitWValue(operand);
22012269
2202 const fn_type_index = try genFunctype(wasm, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target.*);
2270 const fn_type_index = try genFunctype(wasm, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target);
22032271 try func.addLabel(.call_indirect, @intFromEnum(fn_type_index));
22042272 }
22052273
......@@ -2260,7 +2328,7 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void
22602328 // load the value, and then shift+or the rhs into the result location.
22612329 const int_elem_ty = try pt.intType(.unsigned, ptr_info.packed_offset.host_size * 8);
22622330
2263 if (isByRef(int_elem_ty, pt, func.target.*)) {
2331 if (isByRef(int_elem_ty, pt, func.target)) {
22642332 return func.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{});
22652333 }
22662334
......@@ -2326,11 +2394,11 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
23262394 const len = @as(u32, @intCast(abi_size));
23272395 return func.memcpy(lhs, rhs, .{ .imm32 = len });
23282396 },
2329 .@"struct", .array, .@"union" => if (isByRef(ty, pt, func.target.*)) {
2397 .@"struct", .array, .@"union" => if (isByRef(ty, pt, func.target)) {
23302398 const len = @as(u32, @intCast(abi_size));
23312399 return func.memcpy(lhs, rhs, .{ .imm32 = len });
23322400 },
2333 .vector => switch (determineSimdStoreStrategy(ty, zcu, func.target.*)) {
2401 .vector => switch (determineSimdStoreStrategy(ty, zcu, func.target)) {
23342402 .unrolled => {
23352403 const len: u32 = @intCast(abi_size);
23362404 return func.memcpy(lhs, rhs, .{ .imm32 = len });
......@@ -2388,7 +2456,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
23882456 // into lhs, so we calculate that and emit that instead
23892457 try func.lowerToStack(rhs);
23902458
2391 const valtype = typeToValtype(ty, pt, func.target.*);
2459 const valtype = typeToValtype(ty, pt, func.target);
23922460 const opcode = buildOpcode(.{
23932461 .valtype1 = valtype,
23942462 .width = @as(u8, @intCast(abi_size * 8)),
......@@ -2417,7 +2485,7 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
24172485 if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return func.finishAir(inst, .none, &.{ty_op.operand});
24182486
24192487 const result = result: {
2420 if (isByRef(ty, pt, func.target.*)) {
2488 if (isByRef(ty, pt, func.target)) {
24212489 const new_local = try func.allocStack(ty);
24222490 try func.store(new_local, operand, ty, 0);
24232491 break :result new_local;
......@@ -2467,7 +2535,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
24672535
24682536 const abi_size: u8 = @intCast(ty.abiSize(zcu));
24692537 const opcode = buildOpcode(.{
2470 .valtype1 = typeToValtype(ty, pt, func.target.*),
2538 .valtype1 = typeToValtype(ty, pt, func.target),
24712539 .width = abi_size * 8,
24722540 .op = .load,
24732541 .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned,
......@@ -2517,19 +2585,6 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
25172585 func.arg_index += 1;
25182586 }
25192587
2520 switch (func.debug_output) {
2521 .dwarf => |dwarf| {
2522 const name = func.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
2523 if (name != .none) try dwarf.genLocalDebugInfo(
2524 .local_arg,
2525 name.toSlice(func.air),
2526 arg_ty,
2527 .{ .wasm_ext = .{ .local = arg.local.value } },
2528 );
2529 },
2530 else => {},
2531 }
2532
25332588 return func.finishAir(inst, arg, &.{});
25342589}
25352590
......@@ -2577,7 +2632,7 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!
25772632 return func.floatOp(float_op, ty, &.{ lhs, rhs });
25782633 }
25792634
2580 if (isByRef(ty, pt, func.target.*)) {
2635 if (isByRef(ty, pt, func.target)) {
25812636 if (ty.zigTypeTag(zcu) == .int) {
25822637 return func.binOpBigInt(lhs, rhs, ty, op);
25832638 } else {
......@@ -2590,7 +2645,7 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!
25902645
25912646 const opcode: std.wasm.Opcode = buildOpcode(.{
25922647 .op = op,
2593 .valtype1 = typeToValtype(ty, pt, func.target.*),
2648 .valtype1 = typeToValtype(ty, pt, func.target),
25942649 .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned,
25952650 });
25962651 try func.emitWValue(lhs);
......@@ -2854,7 +2909,7 @@ fn floatOp(func: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) In
28542909 for (args) |operand| {
28552910 try func.emitWValue(operand);
28562911 }
2857 const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, pt, func.target.*) });
2912 const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, pt, func.target) });
28582913 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
28592914 return .stack;
28602915 }
......@@ -3141,8 +3196,8 @@ fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) Inn
31413196 return .{ .imm32 = 0xaaaaaaaa };
31423197 }
31433198
3144 const atom_index = try func.bin_file.getOrCreateAtomForNav(pt, nav_index);
3145 const atom = func.bin_file.getAtom(atom_index);
3199 const atom_index = try func.wasm.getOrCreateAtomForNav(pt, nav_index);
3200 const atom = func.wasm.getAtom(atom_index);
31463201
31473202 const target_sym_index = @intFromEnum(atom.sym_index);
31483203 if (ip.isFunctionType(nav_ty)) {
......@@ -3156,7 +3211,7 @@ fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) Inn
31563211fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
31573212 const pt = func.pt;
31583213 const zcu = pt.zcu;
3159 assert(!isByRef(ty, pt, func.target.*));
3214 assert(!isByRef(ty, pt, func.target));
31603215 const ip = &zcu.intern_pool;
31613216 if (val.isUndefDeep(zcu)) return func.emitUndefined(ty);
31623217
......@@ -3267,7 +3322,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
32673322 .aggregate => switch (ip.indexToKey(ty.ip_index)) {
32683323 .array_type => return func.fail("Wasm TODO: LowerConstant for {}", .{ty.fmt(pt)}),
32693324 .vector_type => {
3270 assert(determineSimdStoreStrategy(ty, zcu, func.target.*) == .direct);
3325 assert(determineSimdStoreStrategy(ty, zcu, func.target) == .direct);
32713326 var buf: [16]u8 = undefined;
32723327 val.writeToMemory(pt, &buf) catch unreachable;
32733328 return func.storeSimdImmd(buf);
......@@ -3398,11 +3453,11 @@ fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
33983453
33993454fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void {
34003455 const pt = func.pt;
3401 const wasm_block_ty = genBlockType(block_ty, pt, func.target.*);
3456 const wasm_block_ty = genBlockType(block_ty, pt, func.target);
34023457
34033458 // if wasm_block_ty is non-empty, we create a register to store the temporary value
34043459 const block_result: WValue = if (wasm_block_ty != std.wasm.block_empty) blk: {
3405 const ty: Type = if (isByRef(block_ty, pt, func.target.*)) Type.u32 else block_ty;
3460 const ty: Type = if (isByRef(block_ty, pt, func.target)) Type.u32 else block_ty;
34063461 break :blk try func.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten
34073462 } else .none;
34083463
......@@ -3527,7 +3582,7 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO
35273582 }
35283583 } else if (ty.isAnyFloat()) {
35293584 return func.cmpFloat(ty, lhs, rhs, op);
3530 } else if (isByRef(ty, pt, func.target.*)) {
3585 } else if (isByRef(ty, pt, func.target)) {
35313586 return func.cmpBigInt(lhs, rhs, ty, op);
35323587 }
35333588
......@@ -3545,7 +3600,7 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO
35453600 try func.lowerToStack(rhs);
35463601
35473602 const opcode: std.wasm.Opcode = buildOpcode(.{
3548 .valtype1 = typeToValtype(ty, pt, func.target.*),
3603 .valtype1 = typeToValtype(ty, pt, func.target),
35493604 .op = switch (op) {
35503605 .lt => .lt,
35513606 .lte => .le,
......@@ -3612,7 +3667,7 @@ fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
36123667fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
36133668 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
36143669 const operand = try func.resolveInst(un_op);
3615 const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null);
3670 const sym_index = try func.wasm.getGlobalSymbol("__zig_errors_len", null);
36163671 const errors_len: WValue = .{ .memory = @intFromEnum(sym_index) };
36173672
36183673 try func.emitWValue(operand);
......@@ -3758,7 +3813,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
37583813 break :result try func.bitcast(wanted_ty, given_ty, operand);
37593814 }
37603815
3761 if (isByRef(given_ty, pt, func.target.*) and !isByRef(wanted_ty, pt, func.target.*)) {
3816 if (isByRef(given_ty, pt, func.target) and !isByRef(wanted_ty, pt, func.target)) {
37623817 const loaded_memory = try func.load(operand, wanted_ty, 0);
37633818 if (needs_wrapping) {
37643819 break :result try func.wrapOperand(loaded_memory, wanted_ty);
......@@ -3766,7 +3821,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
37663821 break :result loaded_memory;
37673822 }
37683823 }
3769 if (!isByRef(given_ty, pt, func.target.*) and isByRef(wanted_ty, pt, func.target.*)) {
3824 if (!isByRef(given_ty, pt, func.target) and isByRef(wanted_ty, pt, func.target)) {
37703825 const stack_memory = try func.allocStack(wanted_ty);
37713826 try func.store(stack_memory, operand, given_ty, 0);
37723827 if (needs_wrapping) {
......@@ -3796,8 +3851,8 @@ fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) Inn
37963851
37973852 const opcode = buildOpcode(.{
37983853 .op = .reinterpret,
3799 .valtype1 = typeToValtype(wanted_ty, pt, func.target.*),
3800 .valtype2 = typeToValtype(given_ty, pt, func.target.*),
3854 .valtype1 = typeToValtype(wanted_ty, pt, func.target),
3855 .valtype2 = typeToValtype(given_ty, pt, func.target),
38013856 });
38023857 try func.emitWValue(operand);
38033858 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
......@@ -3919,8 +3974,8 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
39193974 break :result try func.trunc(shifted_value, field_ty, backing_ty);
39203975 },
39213976 .@"union" => result: {
3922 if (isByRef(struct_ty, pt, func.target.*)) {
3923 if (!isByRef(field_ty, pt, func.target.*)) {
3977 if (isByRef(struct_ty, pt, func.target)) {
3978 if (!isByRef(field_ty, pt, func.target)) {
39243979 break :result try func.load(operand, field_ty, 0);
39253980 } else {
39263981 const new_stack_val = try func.allocStack(field_ty);
......@@ -3946,7 +4001,7 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
39464001 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, zcu)) orelse {
39474002 return func.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(pt)});
39484003 };
3949 if (isByRef(field_ty, pt, func.target.*)) {
4004 if (isByRef(field_ty, pt, func.target)) {
39504005 switch (operand) {
39514006 .stack_offset => |stack_offset| {
39524007 break :result .{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
......@@ -4209,7 +4264,7 @@ fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: boo
42094264 }
42104265
42114266 const pl_offset = @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu)));
4212 if (op_is_ptr or isByRef(payload_ty, pt, func.target.*)) {
4267 if (op_is_ptr or isByRef(payload_ty, pt, func.target)) {
42134268 break :result try func.buildPointerOffset(operand, pl_offset, .new);
42144269 }
42154270
......@@ -4436,7 +4491,7 @@ fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
44364491 const operand = try func.resolveInst(ty_op.operand);
44374492 if (opt_ty.optionalReprIsPayload(zcu)) break :result func.reuseOperand(ty_op.operand, operand);
44384493
4439 if (isByRef(payload_ty, pt, func.target.*)) {
4494 if (isByRef(payload_ty, pt, func.target)) {
44404495 break :result try func.buildPointerOffset(operand, 0, .new);
44414496 }
44424497
......@@ -4570,7 +4625,7 @@ fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
45704625 try func.addTag(.i32_mul);
45714626 try func.addTag(.i32_add);
45724627
4573 const elem_result = if (isByRef(elem_ty, pt, func.target.*))
4628 const elem_result = if (isByRef(elem_ty, pt, func.target))
45744629 .stack
45754630 else
45764631 try func.load(.stack, elem_ty, 0);
......@@ -4729,7 +4784,7 @@ fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
47294784 try func.addTag(.i32_mul);
47304785 try func.addTag(.i32_add);
47314786
4732 const elem_result = if (isByRef(elem_ty, pt, func.target.*))
4787 const elem_result = if (isByRef(elem_ty, pt, func.target))
47334788 .stack
47344789 else
47354790 try func.load(.stack, elem_ty, 0);
......@@ -4780,7 +4835,7 @@ fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
47804835 else => ptr_ty.childType(zcu),
47814836 };
47824837
4783 const valtype = typeToValtype(Type.usize, pt, func.target.*);
4838 const valtype = typeToValtype(Type.usize, pt, func.target);
47844839 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });
47854840 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });
47864841
......@@ -4927,7 +4982,7 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
49274982 const elem_ty = array_ty.childType(zcu);
49284983 const elem_size = elem_ty.abiSize(zcu);
49294984
4930 if (isByRef(array_ty, pt, func.target.*)) {
4985 if (isByRef(array_ty, pt, func.target)) {
49314986 try func.lowerToStack(array);
49324987 try func.emitWValue(index);
49334988 try func.addImm32(@intCast(elem_size));
......@@ -4970,7 +5025,7 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
49705025 }
49715026 }
49725027
4973 const elem_result = if (isByRef(elem_ty, pt, func.target.*))
5028 const elem_result = if (isByRef(elem_ty, pt, func.target))
49745029 .stack
49755030 else
49765031 try func.load(.stack, elem_ty, 0);
......@@ -5014,8 +5069,8 @@ fn airIntFromFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
50145069 try func.emitWValue(operand);
50155070 const op = buildOpcode(.{
50165071 .op = .trunc,
5017 .valtype1 = typeToValtype(dest_ty, pt, func.target.*),
5018 .valtype2 = typeToValtype(op_ty, pt, func.target.*),
5072 .valtype1 = typeToValtype(dest_ty, pt, func.target),
5073 .valtype2 = typeToValtype(op_ty, pt, func.target),
50195074 .signedness = dest_info.signedness,
50205075 });
50215076 try func.addTag(Mir.Inst.Tag.fromOpcode(op));
......@@ -5059,8 +5114,8 @@ fn airFloatFromInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
50595114 try func.emitWValue(operand);
50605115 const op = buildOpcode(.{
50615116 .op = .convert,
5062 .valtype1 = typeToValtype(dest_ty, pt, func.target.*),
5063 .valtype2 = typeToValtype(op_ty, pt, func.target.*),
5117 .valtype1 = typeToValtype(dest_ty, pt, func.target),
5118 .valtype2 = typeToValtype(op_ty, pt, func.target),
50645119 .signedness = op_info.signedness,
50655120 });
50665121 try func.addTag(Mir.Inst.Tag.fromOpcode(op));
......@@ -5076,7 +5131,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
50765131 const ty = func.typeOfIndex(inst);
50775132 const elem_ty = ty.childType(zcu);
50785133
5079 if (determineSimdStoreStrategy(ty, zcu, func.target.*) == .direct) blk: {
5134 if (determineSimdStoreStrategy(ty, zcu, func.target) == .direct) blk: {
50805135 switch (operand) {
50815136 // when the operand lives in the linear memory section, we can directly
50825137 // load and splat the value at once. Meaning we do not first have to load
......@@ -5160,7 +5215,7 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
51605215 const elem_size = child_ty.abiSize(zcu);
51615216
51625217 // TODO: One of them could be by ref; handle in loop
5163 if (isByRef(func.typeOf(extra.a), pt, func.target.*) or isByRef(inst_ty, pt, func.target.*)) {
5218 if (isByRef(func.typeOf(extra.a), pt, func.target) or isByRef(inst_ty, pt, func.target)) {
51645219 const result = try func.allocStack(inst_ty);
51655220
51665221 for (0..mask_len) |index| {
......@@ -5236,7 +5291,7 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52365291 // When the element type is by reference, we must copy the entire
52375292 // value. It is therefore safer to move the offset pointer and store
52385293 // each value individually, instead of using store offsets.
5239 if (isByRef(elem_ty, pt, func.target.*)) {
5294 if (isByRef(elem_ty, pt, func.target)) {
52405295 // copy stack pointer into a temporary local, which is
52415296 // moved for each element to store each value in the right position.
52425297 const offset = try func.buildPointerOffset(result, 0, .new);
......@@ -5266,7 +5321,7 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52665321 },
52675322 .@"struct" => switch (result_ty.containerLayout(zcu)) {
52685323 .@"packed" => {
5269 if (isByRef(result_ty, pt, func.target.*)) {
5324 if (isByRef(result_ty, pt, func.target)) {
52705325 return func.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{});
52715326 }
52725327 const packed_struct = zcu.typeToPackedStruct(result_ty).?;
......@@ -5369,15 +5424,15 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
53695424 if (layout.tag_size == 0) {
53705425 break :result .none;
53715426 }
5372 assert(!isByRef(union_ty, pt, func.target.*));
5427 assert(!isByRef(union_ty, pt, func.target));
53735428 break :result tag_int;
53745429 }
53755430
5376 if (isByRef(union_ty, pt, func.target.*)) {
5431 if (isByRef(union_ty, pt, func.target)) {
53775432 const result_ptr = try func.allocStack(union_ty);
53785433 const payload = try func.resolveInst(extra.init);
53795434 if (layout.tag_align.compare(.gte, layout.payload_align)) {
5380 if (isByRef(field_ty, pt, func.target.*)) {
5435 if (isByRef(field_ty, pt, func.target)) {
53815436 const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new);
53825437 try func.store(payload_ptr, payload, field_ty, 0);
53835438 } else {
......@@ -5458,7 +5513,7 @@ fn cmpOptionals(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op:
54585513
54595514 _ = try func.load(lhs, payload_ty, 0);
54605515 _ = try func.load(rhs, payload_ty, 0);
5461 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, pt, func.target.*) });
5516 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, pt, func.target) });
54625517 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
54635518 try func.addLabel(.br_if, 0);
54645519
......@@ -5910,7 +5965,7 @@ fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
59105965 // As the names are global and the slice elements are constant, we do not have
59115966 // to make a copy of the ptr+value but can point towards them directly.
59125967 const pt = func.pt;
5913 const error_table_symbol = try func.bin_file.getErrorTableSymbol(pt);
5968 const error_table_symbol = try func.wasm.getErrorTableSymbol(pt);
59145969 const name_ty = Type.slice_const_u8_sentinel_0;
59155970 const abi_size = name_ty.abiSize(pt.zcu);
59165971
......@@ -5943,7 +5998,7 @@ fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerE
59435998
59445999/// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits
59456000fn intZeroValue(func: *CodeGen, ty: Type) InnerError!WValue {
5946 const zcu = func.bin_file.base.comp.zcu.?;
6001 const zcu = func.wasm.base.comp.zcu.?;
59476002 const int_info = ty.intInfo(zcu);
59486003 const wasm_bits = toWasmBits(int_info.bits) orelse {
59496004 return func.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits});
......@@ -6379,8 +6434,6 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
63796434}
63806435
63816436fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6382 if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});
6383
63846437 const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
63856438 try func.addInst(.{ .tag = .dbg_line, .data = .{
63866439 .payload = try func.addExtra(Mir.DbgLineColumn{
......@@ -6405,26 +6458,7 @@ fn airDbgVar(
64056458 is_ptr: bool,
64066459) InnerError!void {
64076460 _ = is_ptr;
6408 if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});
6409
6410 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6411 const ty = func.typeOf(pl_op.operand);
6412 const operand = try func.resolveInst(pl_op.operand);
6413
6414 log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), operand });
6415
6416 const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
6417 log.debug(" var name = ({s})", .{name.toSlice(func.air)});
6418
6419 const loc: link.File.Dwarf.Loc = switch (operand) {
6420 .local => |local| .{ .wasm_ext = .{ .local = local.value } },
6421 else => blk: {
6422 log.debug("TODO generate debug info for {}", .{operand});
6423 break :blk .empty;
6424 },
6425 };
6426 try func.debug_output.dwarf.genLocalDebugInfo(local_tag, name.toSlice(func.air), ty, loc);
6427
6461 _ = local_tag;
64286462 return func.finishAir(inst, .none, &.{});
64296463}
64306464
......@@ -6500,7 +6534,7 @@ fn lowerTry(
65006534 }
65016535
65026536 const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));
6503 if (isByRef(pl_ty, pt, func.target.*)) {
6537 if (isByRef(pl_ty, pt, func.target)) {
65046538 return buildPointerOffset(func, err_union, pl_offset, .new);
65056539 }
65066540 const payload = try func.load(err_union, pl_ty, pl_offset);
......@@ -7074,15 +7108,15 @@ fn callIntrinsic(
70747108 args: []const WValue,
70757109) InnerError!WValue {
70767110 assert(param_types.len == args.len);
7077 const wasm = func.bin_file;
7111 const wasm = func.wasm;
70787112 const pt = func.pt;
70797113 const zcu = pt.zcu;
7080 const func_type_index = try genFunctype(wasm, .{ .wasm_watc = .{} }, param_types, return_type, pt, func.target.*);
7114 const func_type_index = try genFunctype(wasm, .{ .wasm_watc = .{} }, param_types, return_type, pt, func.target);
70817115 const func_index = wasm.getOutputFunction(try wasm.internString(name), func_type_index);
70827116
70837117 // Always pass over C-ABI
70847118
7085 const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, pt, func.target.*);
7119 const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, pt, func.target);
70867120 // if we want return as first param, we allocate a pointer to stack,
70877121 // and emit it as our first argument
70887122 const sret = if (want_sret_param) blk: {
......@@ -7121,7 +7155,7 @@ fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71217155 const result_ptr = try func.allocStack(func.typeOfIndex(inst));
71227156 try func.lowerToStack(result_ptr);
71237157 try func.emitWValue(operand);
7124 try func.addCallTagName(enum_ty.toIntern());
7158 try func.addIpIndex(.call_tag_name, enum_ty.toIntern());
71257159
71267160 return func.finishAir(inst, result_ptr, &.{un_op});
71277161}
......@@ -7265,7 +7299,7 @@ fn airCmpxchg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
72657299 break :val ptr_val;
72667300 };
72677301
7268 const result = if (isByRef(result_ty, pt, func.target.*)) val: {
7302 const result = if (isByRef(result_ty, pt, func.target)) val: {
72697303 try func.emitWValue(cmp_result);
72707304 try func.addImm32(~@as(u32, 0));
72717305 try func.addTag(.i32_xor);
src/arch/wasm/Emit.zig+569-618
......@@ -1,9 +1,9 @@
1//! Contains all logic to lower wasm MIR into its binary
2//! or textual representation.
3
41const Emit = @This();
2
53const std = @import("std");
6const leb128 = std.leb;
4const assert = std.debug.assert;
5const Allocator = std.mem.Allocator;
6const leb = std.leb;
77
88const Mir = @import("Mir.zig");
99const link = @import("../../link.zig");
......@@ -11,660 +11,611 @@ const Zcu = @import("../../Zcu.zig");
1111const InternPool = @import("../../InternPool.zig");
1212const codegen = @import("../../codegen.zig");
1313
14/// Contains our list of instructions
1514mir: Mir,
16/// Reference to the Wasm module linker
17bin_file: *link.File.Wasm,
18/// Possible error message. When set, the value is allocated and
19/// must be freed manually.
20error_msg: ?*Zcu.ErrorMsg = null,
21/// The binary representation that will be emit by this module.
22code: *std.ArrayList(u8),
23/// List of allocated locals.
24locals: []const u8,
25/// The declaration that code is being generated for.
26owner_nav: InternPool.Nav.Index,
27
28// Debug information
29/// Holds the debug information for this emission
30dbg_output: link.File.DebugInfoOutput,
31/// Previous debug info line
32prev_di_line: u32,
33/// Previous debug info column
34prev_di_column: u32,
35/// Previous offset relative to code section
36prev_di_offset: u32,
37
38const InnerError = error{
15wasm: *link.File.Wasm,
16/// The binary representation that will be emitted by this module.
17code: *std.ArrayListUnmanaged(u8),
18
19pub const Error = error{
3920 OutOfMemory,
40 EmitFail,
4121};
4222
43pub fn emitMir(emit: *Emit) InnerError!void {
44 const mir_tags = emit.mir.instructions.items(.tag);
45 // write the locals in the prologue of the function body
46 // before we emit the function body when lowering MIR
47 try emit.emitLocals();
48
49 for (mir_tags, 0..) |tag, index| {
50 const inst = @as(u32, @intCast(index));
51 switch (tag) {
52 // block instructions
53 .block => try emit.emitBlock(tag, inst),
54 .loop => try emit.emitBlock(tag, inst),
55
56 .dbg_line => try emit.emitDbgLine(inst),
57 .dbg_epilogue_begin => try emit.emitDbgEpilogueBegin(),
58 .dbg_prologue_end => try emit.emitDbgPrologueEnd(),
59
60 // branch instructions
61 .br_if => try emit.emitLabel(tag, inst),
62 .br_table => try emit.emitBrTable(inst),
63 .br => try emit.emitLabel(tag, inst),
64
65 // relocatables
66 .call => try emit.emitCall(inst),
67 .call_indirect => try emit.emitCallIndirect(inst),
68 .global_get => try emit.emitGlobal(tag, inst),
69 .global_set => try emit.emitGlobal(tag, inst),
70 .function_index => try emit.emitFunctionIndex(inst),
71 .memory_address => try emit.emitMemAddress(inst),
72
73 // immediates
74 .f32_const => try emit.emitFloat32(inst),
75 .f64_const => try emit.emitFloat64(inst),
76 .i32_const => try emit.emitImm32(inst),
77 .i64_const => try emit.emitImm64(inst),
78
79 // memory instructions
80 .i32_load => try emit.emitMemArg(tag, inst),
81 .i64_load => try emit.emitMemArg(tag, inst),
82 .f32_load => try emit.emitMemArg(tag, inst),
83 .f64_load => try emit.emitMemArg(tag, inst),
84 .i32_load8_s => try emit.emitMemArg(tag, inst),
85 .i32_load8_u => try emit.emitMemArg(tag, inst),
86 .i32_load16_s => try emit.emitMemArg(tag, inst),
87 .i32_load16_u => try emit.emitMemArg(tag, inst),
88 .i64_load8_s => try emit.emitMemArg(tag, inst),
89 .i64_load8_u => try emit.emitMemArg(tag, inst),
90 .i64_load16_s => try emit.emitMemArg(tag, inst),
91 .i64_load16_u => try emit.emitMemArg(tag, inst),
92 .i64_load32_s => try emit.emitMemArg(tag, inst),
93 .i64_load32_u => try emit.emitMemArg(tag, inst),
94 .i32_store => try emit.emitMemArg(tag, inst),
95 .i64_store => try emit.emitMemArg(tag, inst),
96 .f32_store => try emit.emitMemArg(tag, inst),
97 .f64_store => try emit.emitMemArg(tag, inst),
98 .i32_store8 => try emit.emitMemArg(tag, inst),
99 .i32_store16 => try emit.emitMemArg(tag, inst),
100 .i64_store8 => try emit.emitMemArg(tag, inst),
101 .i64_store16 => try emit.emitMemArg(tag, inst),
102 .i64_store32 => try emit.emitMemArg(tag, inst),
103
104 // Instructions with an index that do not require relocations
105 .local_get => try emit.emitLabel(tag, inst),
106 .local_set => try emit.emitLabel(tag, inst),
107 .local_tee => try emit.emitLabel(tag, inst),
108 .memory_grow => try emit.emitLabel(tag, inst),
109 .memory_size => try emit.emitLabel(tag, inst),
110
111 // no-ops
112 .end => try emit.emitTag(tag),
113 .@"return" => try emit.emitTag(tag),
114 .@"unreachable" => try emit.emitTag(tag),
115
116 .select => try emit.emitTag(tag),
117
118 // arithmetic
119 .i32_eqz => try emit.emitTag(tag),
120 .i32_eq => try emit.emitTag(tag),
121 .i32_ne => try emit.emitTag(tag),
122 .i32_lt_s => try emit.emitTag(tag),
123 .i32_lt_u => try emit.emitTag(tag),
124 .i32_gt_s => try emit.emitTag(tag),
125 .i32_gt_u => try emit.emitTag(tag),
126 .i32_le_s => try emit.emitTag(tag),
127 .i32_le_u => try emit.emitTag(tag),
128 .i32_ge_s => try emit.emitTag(tag),
129 .i32_ge_u => try emit.emitTag(tag),
130 .i64_eqz => try emit.emitTag(tag),
131 .i64_eq => try emit.emitTag(tag),
132 .i64_ne => try emit.emitTag(tag),
133 .i64_lt_s => try emit.emitTag(tag),
134 .i64_lt_u => try emit.emitTag(tag),
135 .i64_gt_s => try emit.emitTag(tag),
136 .i64_gt_u => try emit.emitTag(tag),
137 .i64_le_s => try emit.emitTag(tag),
138 .i64_le_u => try emit.emitTag(tag),
139 .i64_ge_s => try emit.emitTag(tag),
140 .i64_ge_u => try emit.emitTag(tag),
141 .f32_eq => try emit.emitTag(tag),
142 .f32_ne => try emit.emitTag(tag),
143 .f32_lt => try emit.emitTag(tag),
144 .f32_gt => try emit.emitTag(tag),
145 .f32_le => try emit.emitTag(tag),
146 .f32_ge => try emit.emitTag(tag),
147 .f64_eq => try emit.emitTag(tag),
148 .f64_ne => try emit.emitTag(tag),
149 .f64_lt => try emit.emitTag(tag),
150 .f64_gt => try emit.emitTag(tag),
151 .f64_le => try emit.emitTag(tag),
152 .f64_ge => try emit.emitTag(tag),
153 .i32_add => try emit.emitTag(tag),
154 .i32_sub => try emit.emitTag(tag),
155 .i32_mul => try emit.emitTag(tag),
156 .i32_div_s => try emit.emitTag(tag),
157 .i32_div_u => try emit.emitTag(tag),
158 .i32_and => try emit.emitTag(tag),
159 .i32_or => try emit.emitTag(tag),
160 .i32_xor => try emit.emitTag(tag),
161 .i32_shl => try emit.emitTag(tag),
162 .i32_shr_s => try emit.emitTag(tag),
163 .i32_shr_u => try emit.emitTag(tag),
164 .i64_add => try emit.emitTag(tag),
165 .i64_sub => try emit.emitTag(tag),
166 .i64_mul => try emit.emitTag(tag),
167 .i64_div_s => try emit.emitTag(tag),
168 .i64_div_u => try emit.emitTag(tag),
169 .i64_and => try emit.emitTag(tag),
170 .i64_or => try emit.emitTag(tag),
171 .i64_xor => try emit.emitTag(tag),
172 .i64_shl => try emit.emitTag(tag),
173 .i64_shr_s => try emit.emitTag(tag),
174 .i64_shr_u => try emit.emitTag(tag),
175 .f32_abs => try emit.emitTag(tag),
176 .f32_neg => try emit.emitTag(tag),
177 .f32_ceil => try emit.emitTag(tag),
178 .f32_floor => try emit.emitTag(tag),
179 .f32_trunc => try emit.emitTag(tag),
180 .f32_nearest => try emit.emitTag(tag),
181 .f32_sqrt => try emit.emitTag(tag),
182 .f32_add => try emit.emitTag(tag),
183 .f32_sub => try emit.emitTag(tag),
184 .f32_mul => try emit.emitTag(tag),
185 .f32_div => try emit.emitTag(tag),
186 .f32_min => try emit.emitTag(tag),
187 .f32_max => try emit.emitTag(tag),
188 .f32_copysign => try emit.emitTag(tag),
189 .f64_abs => try emit.emitTag(tag),
190 .f64_neg => try emit.emitTag(tag),
191 .f64_ceil => try emit.emitTag(tag),
192 .f64_floor => try emit.emitTag(tag),
193 .f64_trunc => try emit.emitTag(tag),
194 .f64_nearest => try emit.emitTag(tag),
195 .f64_sqrt => try emit.emitTag(tag),
196 .f64_add => try emit.emitTag(tag),
197 .f64_sub => try emit.emitTag(tag),
198 .f64_mul => try emit.emitTag(tag),
199 .f64_div => try emit.emitTag(tag),
200 .f64_min => try emit.emitTag(tag),
201 .f64_max => try emit.emitTag(tag),
202 .f64_copysign => try emit.emitTag(tag),
203 .i32_wrap_i64 => try emit.emitTag(tag),
204 .i64_extend_i32_s => try emit.emitTag(tag),
205 .i64_extend_i32_u => try emit.emitTag(tag),
206 .i32_extend8_s => try emit.emitTag(tag),
207 .i32_extend16_s => try emit.emitTag(tag),
208 .i64_extend8_s => try emit.emitTag(tag),
209 .i64_extend16_s => try emit.emitTag(tag),
210 .i64_extend32_s => try emit.emitTag(tag),
211 .f32_demote_f64 => try emit.emitTag(tag),
212 .f64_promote_f32 => try emit.emitTag(tag),
213 .i32_reinterpret_f32 => try emit.emitTag(tag),
214 .i64_reinterpret_f64 => try emit.emitTag(tag),
215 .f32_reinterpret_i32 => try emit.emitTag(tag),
216 .f64_reinterpret_i64 => try emit.emitTag(tag),
217 .i32_trunc_f32_s => try emit.emitTag(tag),
218 .i32_trunc_f32_u => try emit.emitTag(tag),
219 .i32_trunc_f64_s => try emit.emitTag(tag),
220 .i32_trunc_f64_u => try emit.emitTag(tag),
221 .i64_trunc_f32_s => try emit.emitTag(tag),
222 .i64_trunc_f32_u => try emit.emitTag(tag),
223 .i64_trunc_f64_s => try emit.emitTag(tag),
224 .i64_trunc_f64_u => try emit.emitTag(tag),
225 .f32_convert_i32_s => try emit.emitTag(tag),
226 .f32_convert_i32_u => try emit.emitTag(tag),
227 .f32_convert_i64_s => try emit.emitTag(tag),
228 .f32_convert_i64_u => try emit.emitTag(tag),
229 .f64_convert_i32_s => try emit.emitTag(tag),
230 .f64_convert_i32_u => try emit.emitTag(tag),
231 .f64_convert_i64_s => try emit.emitTag(tag),
232 .f64_convert_i64_u => try emit.emitTag(tag),
233 .i32_rem_s => try emit.emitTag(tag),
234 .i32_rem_u => try emit.emitTag(tag),
235 .i64_rem_s => try emit.emitTag(tag),
236 .i64_rem_u => try emit.emitTag(tag),
237 .i32_popcnt => try emit.emitTag(tag),
238 .i64_popcnt => try emit.emitTag(tag),
239 .i32_clz => try emit.emitTag(tag),
240 .i32_ctz => try emit.emitTag(tag),
241 .i64_clz => try emit.emitTag(tag),
242 .i64_ctz => try emit.emitTag(tag),
243
244 .misc_prefix => try emit.emitExtended(inst),
245 .simd_prefix => try emit.emitSimd(inst),
246 .atomics_prefix => try emit.emitAtomic(inst),
247 }
248 }
249}
250
251fn offset(self: Emit) u32 {
252 return @as(u32, @intCast(self.code.items.len));
253}
254
255fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
256 @branchHint(.cold);
257 std.debug.assert(emit.error_msg == null);
258 const wasm = emit.bin_file;
23pub fn lowerToCode(emit: *Emit) Error!void {
24 const mir = &emit.mir;
25 const code = emit.code;
26 const wasm = emit.wasm;
25927 const comp = wasm.base.comp;
260 const zcu = comp.zcu.?;
26128 const gpa = comp.gpa;
262 emit.error_msg = try Zcu.ErrorMsg.create(gpa, zcu.navSrcLoc(emit.owner_nav), format, args);
263 return error.EmitFail;
264}
265
266fn emitLocals(emit: *Emit) !void {
267 const writer = emit.code.writer();
268 try leb128.writeUleb128(writer, @as(u32, @intCast(emit.locals.len)));
269 // emit the actual locals amount
270 for (emit.locals) |local| {
271 try leb128.writeUleb128(writer, @as(u32, 1));
272 try writer.writeByte(local);
273 }
274}
29 const is_obj = comp.config.output_mode == .Obj;
27530
276fn emitTag(emit: *Emit, tag: Mir.Inst.Tag) !void {
277 try emit.code.append(@intFromEnum(tag));
278}
31 const tags = mir.instructions.items(.tag);
32 const datas = mir.instructions.items(.data);
33 var inst: u32 = 0;
27934
280fn emitBlock(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
281 const block_type = emit.mir.instructions.items(.data)[inst].block_type;
282 try emit.code.append(@intFromEnum(tag));
283 try emit.code.append(block_type);
284}
35 loop: switch (tags[inst]) {
36 .block, .loop => {
37 const block_type = datas[inst].block_type;
38 try code.ensureUnusedCapacity(gpa, 2);
39 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
40 code.appendAssumeCapacity(block_type);
28541
286fn emitBrTable(emit: *Emit, inst: Mir.Inst.Index) !void {
287 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
288 const extra = emit.mir.extraData(Mir.JumpTable, extra_index);
289 const labels = emit.mir.extra[extra.end..][0..extra.data.length];
290 const writer = emit.code.writer();
42 inst += 1;
43 continue :loop tags[inst];
44 },
29145
292 try emit.code.append(@intFromEnum(std.wasm.Opcode.br_table));
293 try leb128.writeUleb128(writer, extra.data.length - 1); // Default label is not part of length/depth
294 for (labels) |label| {
295 try leb128.writeUleb128(writer, label);
296 }
297}
46 .uav_ref => {
47 try uavRefOff(wasm, code, .{ .ip_index = datas[inst].ip_index, .offset = 0 });
29848
299fn emitLabel(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
300 const label = emit.mir.instructions.items(.data)[inst].label;
301 try emit.code.append(@intFromEnum(tag));
302 try leb128.writeUleb128(emit.code.writer(), label);
303}
49 inst += 1;
50 continue :loop tags[inst];
51 },
52 .uav_ref_off => {
53 try uavRefOff(wasm, code, mir.extraData(Mir.UavRefOff, datas[inst].payload).data);
30454
305fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
306 const wasm = emit.bin_file;
307 const comp = wasm.base.comp;
308 const gpa = comp.gpa;
309 const label = emit.mir.instructions.items(.data)[inst].label;
310 try emit.code.append(@intFromEnum(tag));
311 var buf: [5]u8 = undefined;
312 leb128.writeUnsignedFixed(5, &buf, label);
313 const global_offset = emit.offset();
314 try emit.code.appendSlice(&buf);
315
316 const zo = wasm.zig_object.?;
317 try zo.relocs.append(gpa, .{
318 .nav_index = emit.nav_index,
319 .index = label,
320 .offset = global_offset,
321 .tag = .GLOBAL_INDEX_LEB,
322 });
323}
55 inst += 1;
56 continue :loop tags[inst];
57 },
32458
325fn emitImm32(emit: *Emit, inst: Mir.Inst.Index) !void {
326 const value: i32 = emit.mir.instructions.items(.data)[inst].imm32;
327 try emit.code.append(@intFromEnum(std.wasm.Opcode.i32_const));
328 try leb128.writeIleb128(emit.code.writer(), value);
329}
59 .dbg_line => {
60 inst += 1;
61 continue :loop tags[inst];
62 },
63 .dbg_epilogue_begin => {
64 return;
65 },
33066
331fn emitImm64(emit: *Emit, inst: Mir.Inst.Index) !void {
332 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
333 const value = emit.mir.extraData(Mir.Imm64, extra_index);
334 try emit.code.append(@intFromEnum(std.wasm.Opcode.i64_const));
335 try leb128.writeIleb128(emit.code.writer(), @as(i64, @bitCast(value.data.toU64())));
336}
67 .br_if, .br, .memory_grow, .memory_size => {
68 try code.ensureUnusedCapacity(gpa, 11);
69 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
70 leb.writeUleb128(code.fixedWriter(), datas[inst].label) catch unreachable;
33771
338fn emitFloat32(emit: *Emit, inst: Mir.Inst.Index) !void {
339 const value: f32 = emit.mir.instructions.items(.data)[inst].float32;
340 try emit.code.append(@intFromEnum(std.wasm.Opcode.f32_const));
341 try emit.code.writer().writeInt(u32, @bitCast(value), .little);
342}
72 inst += 1;
73 continue :loop tags[inst];
74 },
34375
344fn emitFloat64(emit: *Emit, inst: Mir.Inst.Index) !void {
345 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
346 const value = emit.mir.extraData(Mir.Float64, extra_index);
347 try emit.code.append(@intFromEnum(std.wasm.Opcode.f64_const));
348 try emit.code.writer().writeInt(u64, value.data.toU64(), .little);
349}
76 .local_get, .local_set, .local_tee => {
77 try code.ensureUnusedCapacity(gpa, 11);
78 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
79 leb.writeUleb128(code.fixedWriter(), datas[inst].local) catch unreachable;
35080
351fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
352 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
353 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;
354 try emit.code.append(@intFromEnum(tag));
355 try encodeMemArg(mem_arg, emit.code.writer());
356}
81 inst += 1;
82 continue :loop tags[inst];
83 },
35784
358fn encodeMemArg(mem_arg: Mir.MemArg, writer: anytype) !void {
359 // wasm encodes alignment as power of 2, rather than natural alignment
360 const encoded_alignment = @ctz(mem_arg.alignment);
361 try leb128.writeUleb128(writer, encoded_alignment);
362 try leb128.writeUleb128(writer, mem_arg.offset);
363}
85 .br_table => {
86 const extra_index = mir.instructions.items(.data)[inst].payload;
87 const extra = mir.extraData(Mir.JumpTable, extra_index);
88 const labels = mir.extra[extra.end..][0..extra.data.length];
89 try code.ensureUnusedCapacity(gpa, 11 + 10 * labels.len);
90 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_table));
91 // -1 because default label is not part of length/depth.
92 leb.writeUleb128(code.fixedWriter(), extra.data.length - 1) catch unreachable;
93 for (labels) |label| leb.writeUleb128(code.fixedWriter(), label) catch unreachable;
94
95 inst += 1;
96 continue :loop tags[inst];
97 },
36498
365fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
366 const wasm = emit.bin_file;
367 const comp = wasm.base.comp;
368 const gpa = comp.gpa;
369 const label = emit.mir.instructions.items(.data)[inst].label;
370 try emit.code.append(@intFromEnum(std.wasm.Opcode.call));
371 const call_offset = emit.offset();
372 var buf: [5]u8 = undefined;
373 leb128.writeUnsignedFixed(5, &buf, label);
374 try emit.code.appendSlice(&buf);
375
376 const zo = wasm.zig_object.?;
377 try zo.relocs.append(gpa, .{
378 .offset = call_offset,
379 .index = label,
380 .tag = .FUNCTION_INDEX_LEB,
381 });
382}
99 .call_nav => {
100 try code.ensureUnusedCapacity(gpa, 6);
101 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
102 if (is_obj) {
103 try wasm.out_relocs.append(gpa, .{
104 .offset = @intCast(code.items.len),
105 .index = try wasm.navSymbolIndex(datas[inst].nav_index),
106 .tag = .FUNCTION_INDEX_LEB,
107 .addend = 0,
108 });
109 code.appendNTimesAssumeCapacity(0, 5);
110 } else {
111 const func_index = try wasm.navFunctionIndex(datas[inst].nav_index);
112 leb.writeUleb128(code.fixedWriter(), @intFromEnum(func_index)) catch unreachable;
113 }
114
115 inst += 1;
116 continue :loop tags[inst];
117 },
383118
384fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
385 const wasm = emit.bin_file;
386 const type_index = emit.mir.instructions.items(.data)[inst].label;
387 try emit.code.append(@intFromEnum(std.wasm.Opcode.call_indirect));
388 // NOTE: If we remove unused function types in the future for incremental
389 // linking, we must also emit a relocation for this `type_index`
390 const call_offset = emit.offset();
391 var buf: [5]u8 = undefined;
392 leb128.writeUnsignedFixed(5, &buf, type_index);
393 try emit.code.appendSlice(&buf);
394
395 const zo = wasm.zig_object.?;
396 try zo.relocs.append(wasm.base.comp.gpa, .{
397 .offset = call_offset,
398 .index = type_index,
399 .tag = .TYPE_INDEX_LEB,
400 });
401
402 try leb128.writeUleb128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index
403}
119 .call_indirect => {
120 try code.ensureUnusedCapacity(gpa, 11);
121 const func_ty_index = datas[inst].func_ty;
122 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect));
123 if (is_obj) {
124 try wasm.out_relocs.append(gpa, .{
125 .offset = @intCast(code.items.len),
126 .index = func_ty_index,
127 .tag = .TYPE_INDEX_LEB,
128 .addend = 0,
129 });
130 code.appendNTimesAssumeCapacity(0, 5);
131 } else {
132 leb.writeUleb128(code.fixedWriter(), @intFromEnum(func_ty_index)) catch unreachable;
133 }
134 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // table index
135
136 inst += 1;
137 continue :loop tags[inst];
138 },
404139
405fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
406 const wasm = emit.bin_file;
407 const comp = wasm.base.comp;
408 const gpa = comp.gpa;
409 const symbol_index = emit.mir.instructions.items(.data)[inst].label;
410 try emit.code.append(@intFromEnum(std.wasm.Opcode.i32_const));
411 const index_offset = emit.offset();
412 var buf: [5]u8 = undefined;
413 leb128.writeUnsignedFixed(5, &buf, symbol_index);
414 try emit.code.appendSlice(&buf);
415
416 const zo = wasm.zig_object.?;
417 try zo.relocs.append(gpa, .{
418 .offset = index_offset,
419 .index = symbol_index,
420 .tag = .TABLE_INDEX_SLEB,
421 });
422}
140 .global_set => {
141 try code.ensureUnusedCapacity(gpa, 6);
142 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
143 if (is_obj) {
144 try wasm.out_relocs.append(gpa, .{
145 .offset = @intCast(code.items.len),
146 .index = try wasm.stackPointerSymbolIndex(),
147 .tag = .GLOBAL_INDEX_LEB,
148 .addend = 0,
149 });
150 code.appendNTimesAssumeCapacity(0, 5);
151 } else {
152 const sp_global = try wasm.stackPointerGlobalIndex();
153 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
154 }
155
156 inst += 1;
157 continue :loop tags[inst];
158 },
423159
424fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
425 const wasm = emit.bin_file;
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 = wasm.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(@intFromEnum(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(@intFromEnum(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 }
160 .function_index => {
161 try code.ensureUnusedCapacity(gpa, 6);
162 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
163 if (is_obj) {
164 try wasm.out_relocs.append(gpa, .{
165 .offset = @intCast(code.items.len),
166 .index = try wasm.functionSymbolIndex(datas[inst].ip_index),
167 .tag = .TABLE_INDEX_SLEB,
168 .addend = 0,
169 });
170 code.appendNTimesAssumeCapacity(0, 5);
171 } else {
172 const func_index = try wasm.functionIndex(datas[inst].ip_index);
173 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(func_index)) catch unreachable;
174 }
175
176 inst += 1;
177 continue :loop tags[inst];
178 },
444179
445 const zo = wasm.zig_object.?;
446 try zo.relocs.append(gpa, .{
447 .offset = mem_offset,
448 .index = mem.pointer,
449 .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
450 .addend = @as(i32, @intCast(mem.offset)),
451 });
452}
180 .f32_const => {
181 try code.ensureUnusedCapacity(gpa, 5);
182 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f32_const));
183 std.mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @bitCast(datas[inst].float32), .little);
453184
454fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
455 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
456 const opcode = emit.mir.extra[extra_index];
457 const writer = emit.code.writer();
458 try emit.code.append(@intFromEnum(std.wasm.Opcode.misc_prefix));
459 try leb128.writeUleb128(writer, opcode);
460 switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) {
461 // bulk-memory opcodes
462 .data_drop => {
463 const segment = emit.mir.extra[extra_index + 1];
464 try leb128.writeUleb128(writer, segment);
185 inst += 1;
186 continue :loop tags[inst];
465187 },
466 .memory_init => {
467 const segment = emit.mir.extra[extra_index + 1];
468 try leb128.writeUleb128(writer, segment);
469 try leb128.writeUleb128(writer, @as(u32, 0)); // memory index
188
189 .f64_const => {
190 try code.ensureUnusedCapacity(gpa, 9);
191 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f64_const));
192 const float64 = mir.extraData(Mir.Float64, datas[inst].payload).data;
193 std.mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), float64.toInt(), .little);
194
195 inst += 1;
196 continue :loop tags[inst];
470197 },
471 .memory_fill => {
472 try leb128.writeUleb128(writer, @as(u32, 0)); // memory index
198 .i32_const => {
199 try code.ensureUnusedCapacity(gpa, 6);
200 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
201 leb.writeIleb128(code.fixedWriter(), datas[inst].imm32) catch unreachable;
202
203 inst += 1;
204 continue :loop tags[inst];
473205 },
474 .memory_copy => {
475 try leb128.writeUleb128(writer, @as(u32, 0)); // dst memory index
476 try leb128.writeUleb128(writer, @as(u32, 0)); // src memory index
206 .i64_const => {
207 try code.ensureUnusedCapacity(gpa, 11);
208 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const));
209 const int64: i64 = @bitCast(mir.extraData(Mir.Imm64, datas[inst].payload).data.toInt());
210 leb.writeIleb128(code.writer(), int64) catch unreachable;
211
212 inst += 1;
213 continue :loop tags[inst];
477214 },
478215
479 // nontrapping-float-to-int-conversion opcodes
480 .i32_trunc_sat_f32_s,
481 .i32_trunc_sat_f32_u,
482 .i32_trunc_sat_f64_s,
483 .i32_trunc_sat_f64_u,
484 .i64_trunc_sat_f32_s,
485 .i64_trunc_sat_f32_u,
486 .i64_trunc_sat_f64_s,
487 .i64_trunc_sat_f64_u,
488 => {}, // opcode already written
489 else => |tag| return emit.fail("TODO: Implement extension instruction: {s}\n", .{@tagName(tag)}),
490 }
491}
492
493fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
494 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
495 const opcode = emit.mir.extra[extra_index];
496 const writer = emit.code.writer();
497 try emit.code.append(@intFromEnum(std.wasm.Opcode.simd_prefix));
498 try leb128.writeUleb128(writer, opcode);
499 switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) {
500 .v128_store,
501 .v128_load,
502 .v128_load8_splat,
503 .v128_load16_splat,
504 .v128_load32_splat,
505 .v128_load64_splat,
506 => {
507 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data;
508 try encodeMemArg(mem_arg, writer);
509 },
510 .v128_const,
511 .i8x16_shuffle,
216 .i32_load,
217 .i64_load,
218 .f32_load,
219 .f64_load,
220 .i32_load8_s,
221 .i32_load8_u,
222 .i32_load16_s,
223 .i32_load16_u,
224 .i64_load8_s,
225 .i64_load8_u,
226 .i64_load16_s,
227 .i64_load16_u,
228 .i64_load32_s,
229 .i64_load32_u,
230 .i32_store,
231 .i64_store,
232 .f32_store,
233 .f64_store,
234 .i32_store8,
235 .i32_store16,
236 .i64_store8,
237 .i64_store16,
238 .i64_store32,
512239 => {
513 const simd_value = emit.mir.extra[extra_index + 1 ..][0..4];
514 try writer.writeAll(std.mem.asBytes(simd_value));
240 try code.ensureUnusedCapacity(gpa, 1 + 20);
241 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
242 encodeMemArg(code, mir.extraData(Mir.MemArg, datas[inst]).data);
243 inst += 1;
244 continue :loop tags[inst];
515245 },
516 .i8x16_extract_lane_s,
517 .i8x16_extract_lane_u,
518 .i8x16_replace_lane,
519 .i16x8_extract_lane_s,
520 .i16x8_extract_lane_u,
521 .i16x8_replace_lane,
522 .i32x4_extract_lane,
523 .i32x4_replace_lane,
524 .i64x2_extract_lane,
525 .i64x2_replace_lane,
526 .f32x4_extract_lane,
527 .f32x4_replace_lane,
528 .f64x2_extract_lane,
529 .f64x2_replace_lane,
246
247 .end,
248 .@"return",
249 .@"unreachable",
250 .select,
251 .i32_eqz,
252 .i32_eq,
253 .i32_ne,
254 .i32_lt_s,
255 .i32_lt_u,
256 .i32_gt_s,
257 .i32_gt_u,
258 .i32_le_s,
259 .i32_le_u,
260 .i32_ge_s,
261 .i32_ge_u,
262 .i64_eqz,
263 .i64_eq,
264 .i64_ne,
265 .i64_lt_s,
266 .i64_lt_u,
267 .i64_gt_s,
268 .i64_gt_u,
269 .i64_le_s,
270 .i64_le_u,
271 .i64_ge_s,
272 .i64_ge_u,
273 .f32_eq,
274 .f32_ne,
275 .f32_lt,
276 .f32_gt,
277 .f32_le,
278 .f32_ge,
279 .f64_eq,
280 .f64_ne,
281 .f64_lt,
282 .f64_gt,
283 .f64_le,
284 .f64_ge,
285 .i32_add,
286 .i32_sub,
287 .i32_mul,
288 .i32_div_s,
289 .i32_div_u,
290 .i32_and,
291 .i32_or,
292 .i32_xor,
293 .i32_shl,
294 .i32_shr_s,
295 .i32_shr_u,
296 .i64_add,
297 .i64_sub,
298 .i64_mul,
299 .i64_div_s,
300 .i64_div_u,
301 .i64_and,
302 .i64_or,
303 .i64_xor,
304 .i64_shl,
305 .i64_shr_s,
306 .i64_shr_u,
307 .f32_abs,
308 .f32_neg,
309 .f32_ceil,
310 .f32_floor,
311 .f32_trunc,
312 .f32_nearest,
313 .f32_sqrt,
314 .f32_add,
315 .f32_sub,
316 .f32_mul,
317 .f32_div,
318 .f32_min,
319 .f32_max,
320 .f32_copysign,
321 .f64_abs,
322 .f64_neg,
323 .f64_ceil,
324 .f64_floor,
325 .f64_trunc,
326 .f64_nearest,
327 .f64_sqrt,
328 .f64_add,
329 .f64_sub,
330 .f64_mul,
331 .f64_div,
332 .f64_min,
333 .f64_max,
334 .f64_copysign,
335 .i32_wrap_i64,
336 .i64_extend_i32_s,
337 .i64_extend_i32_u,
338 .i32_extend8_s,
339 .i32_extend16_s,
340 .i64_extend8_s,
341 .i64_extend16_s,
342 .i64_extend32_s,
343 .f32_demote_f64,
344 .f64_promote_f32,
345 .i32_reinterpret_f32,
346 .i64_reinterpret_f64,
347 .f32_reinterpret_i32,
348 .f64_reinterpret_i64,
349 .i32_trunc_f32_s,
350 .i32_trunc_f32_u,
351 .i32_trunc_f64_s,
352 .i32_trunc_f64_u,
353 .i64_trunc_f32_s,
354 .i64_trunc_f32_u,
355 .i64_trunc_f64_s,
356 .i64_trunc_f64_u,
357 .f32_convert_i32_s,
358 .f32_convert_i32_u,
359 .f32_convert_i64_s,
360 .f32_convert_i64_u,
361 .f64_convert_i32_s,
362 .f64_convert_i32_u,
363 .f64_convert_i64_s,
364 .f64_convert_i64_u,
365 .i32_rem_s,
366 .i32_rem_u,
367 .i64_rem_s,
368 .i64_rem_u,
369 .i32_popcnt,
370 .i64_popcnt,
371 .i32_clz,
372 .i32_ctz,
373 .i64_clz,
374 .i64_ctz,
530375 => {
531 try writer.writeByte(@as(u8, @intCast(emit.mir.extra[extra_index + 1])));
376 try code.append(gpa, @intFromEnum(tags[inst]));
377 inst += 1;
378 continue :loop tags[inst];
532379 },
533 .i8x16_splat,
534 .i16x8_splat,
535 .i32x4_splat,
536 .i64x2_splat,
537 .f32x4_splat,
538 .f64x2_splat,
539 => {}, // opcode already written
540 else => |tag| return emit.fail("TODO: Implement simd instruction: {s}", .{@tagName(tag)}),
541 }
542}
543380
544fn emitAtomic(emit: *Emit, inst: Mir.Inst.Index) !void {
545 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
546 const opcode = emit.mir.extra[extra_index];
547 const writer = emit.code.writer();
548 try emit.code.append(@intFromEnum(std.wasm.Opcode.atomics_prefix));
549 try leb128.writeUleb128(writer, opcode);
550 switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) {
551 .i32_atomic_load,
552 .i64_atomic_load,
553 .i32_atomic_load8_u,
554 .i32_atomic_load16_u,
555 .i64_atomic_load8_u,
556 .i64_atomic_load16_u,
557 .i64_atomic_load32_u,
558 .i32_atomic_store,
559 .i64_atomic_store,
560 .i32_atomic_store8,
561 .i32_atomic_store16,
562 .i64_atomic_store8,
563 .i64_atomic_store16,
564 .i64_atomic_store32,
565 .i32_atomic_rmw_add,
566 .i64_atomic_rmw_add,
567 .i32_atomic_rmw8_add_u,
568 .i32_atomic_rmw16_add_u,
569 .i64_atomic_rmw8_add_u,
570 .i64_atomic_rmw16_add_u,
571 .i64_atomic_rmw32_add_u,
572 .i32_atomic_rmw_sub,
573 .i64_atomic_rmw_sub,
574 .i32_atomic_rmw8_sub_u,
575 .i32_atomic_rmw16_sub_u,
576 .i64_atomic_rmw8_sub_u,
577 .i64_atomic_rmw16_sub_u,
578 .i64_atomic_rmw32_sub_u,
579 .i32_atomic_rmw_and,
580 .i64_atomic_rmw_and,
581 .i32_atomic_rmw8_and_u,
582 .i32_atomic_rmw16_and_u,
583 .i64_atomic_rmw8_and_u,
584 .i64_atomic_rmw16_and_u,
585 .i64_atomic_rmw32_and_u,
586 .i32_atomic_rmw_or,
587 .i64_atomic_rmw_or,
588 .i32_atomic_rmw8_or_u,
589 .i32_atomic_rmw16_or_u,
590 .i64_atomic_rmw8_or_u,
591 .i64_atomic_rmw16_or_u,
592 .i64_atomic_rmw32_or_u,
593 .i32_atomic_rmw_xor,
594 .i64_atomic_rmw_xor,
595 .i32_atomic_rmw8_xor_u,
596 .i32_atomic_rmw16_xor_u,
597 .i64_atomic_rmw8_xor_u,
598 .i64_atomic_rmw16_xor_u,
599 .i64_atomic_rmw32_xor_u,
600 .i32_atomic_rmw_xchg,
601 .i64_atomic_rmw_xchg,
602 .i32_atomic_rmw8_xchg_u,
603 .i32_atomic_rmw16_xchg_u,
604 .i64_atomic_rmw8_xchg_u,
605 .i64_atomic_rmw16_xchg_u,
606 .i64_atomic_rmw32_xchg_u,
607
608 .i32_atomic_rmw_cmpxchg,
609 .i64_atomic_rmw_cmpxchg,
610 .i32_atomic_rmw8_cmpxchg_u,
611 .i32_atomic_rmw16_cmpxchg_u,
612 .i64_atomic_rmw8_cmpxchg_u,
613 .i64_atomic_rmw16_cmpxchg_u,
614 .i64_atomic_rmw32_cmpxchg_u,
615 => {
616 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data;
617 try encodeMemArg(mem_arg, writer);
381 .misc_prefix => {
382 try code.ensureUnusedCapacity(gpa, 6 + 6);
383 const extra_index = datas[inst].payload;
384 const opcode = mir.extra[extra_index];
385 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix));
386 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;
387 switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) {
388 // bulk-memory opcodes
389 .data_drop => {
390 const segment = mir.extra[extra_index + 1];
391 leb.writeUleb128(code.fixedWriter(), segment) catch unreachable;
392
393 inst += 1;
394 continue :loop tags[inst];
395 },
396 .memory_init => {
397 const segment = mir.extra[extra_index + 1];
398 leb.writeUleb128(code.fixedWriter(), segment) catch unreachable;
399 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index
400
401 inst += 1;
402 continue :loop tags[inst];
403 },
404 .memory_fill => {
405 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index
406
407 inst += 1;
408 continue :loop tags[inst];
409 },
410 .memory_copy => {
411 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // dst memory index
412 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // src memory index
413
414 inst += 1;
415 continue :loop tags[inst];
416 },
417
418 // nontrapping-float-to-int-conversion opcodes
419 .i32_trunc_sat_f32_s,
420 .i32_trunc_sat_f32_u,
421 .i32_trunc_sat_f64_s,
422 .i32_trunc_sat_f64_u,
423 .i64_trunc_sat_f32_s,
424 .i64_trunc_sat_f32_u,
425 .i64_trunc_sat_f64_s,
426 .i64_trunc_sat_f64_u,
427 => {
428 inst += 1;
429 continue :loop tags[inst];
430 },
431
432 _ => unreachable,
433 }
434 unreachable;
435 },
436 .simd_prefix => {
437 try code.ensureUnusedCapacity(gpa, 6 + 20);
438 const extra_index = mir.instructions.items(.data)[inst].payload;
439 const opcode = mir.extra[extra_index];
440 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.simd_prefix));
441 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;
442 switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) {
443 .v128_store,
444 .v128_load,
445 .v128_load8_splat,
446 .v128_load16_splat,
447 .v128_load32_splat,
448 .v128_load64_splat,
449 => {
450 encodeMemArg(code, mir.extraData(Mir.MemArg, extra_index + 1).data);
451 inst += 1;
452 continue :loop tags[inst];
453 },
454 .v128_const, .i8x16_shuffle => {
455 code.appendSliceAssumeCapacity(std.mem.asBytes(mir.extra[extra_index + 1 ..][0..4]));
456 inst += 1;
457 continue :loop tags[inst];
458 },
459 .i8x16_extract_lane_s,
460 .i8x16_extract_lane_u,
461 .i8x16_replace_lane,
462 .i16x8_extract_lane_s,
463 .i16x8_extract_lane_u,
464 .i16x8_replace_lane,
465 .i32x4_extract_lane,
466 .i32x4_replace_lane,
467 .i64x2_extract_lane,
468 .i64x2_replace_lane,
469 .f32x4_extract_lane,
470 .f32x4_replace_lane,
471 .f64x2_extract_lane,
472 .f64x2_replace_lane,
473 => {
474 code.appendAssumeCapacity(@intCast(mir.extra[extra_index + 1]));
475 inst += 1;
476 continue :loop tags[inst];
477 },
478 .i8x16_splat,
479 .i16x8_splat,
480 .i32x4_splat,
481 .i64x2_splat,
482 .f32x4_splat,
483 .f64x2_splat,
484 => {
485 inst += 1;
486 continue :loop tags[inst];
487 },
488 _ => unreachable,
489 }
490 unreachable;
618491 },
619 .atomic_fence => {
620 // TODO: When multi-memory proposal is accepted and implemented in the compiler,
621 // change this to (user-)specified index, rather than hardcode it to memory index 0.
622 const memory_index: u32 = 0;
623 try leb128.writeUleb128(writer, memory_index);
492 .atomics_prefix => {
493 try code.ensureUnusedCapacity(gpa, 6 + 20);
494
495 const extra_index = mir.instructions.items(.data)[inst].payload;
496 const opcode = mir.extra[extra_index];
497 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));
498 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;
499 switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) {
500 .i32_atomic_load,
501 .i64_atomic_load,
502 .i32_atomic_load8_u,
503 .i32_atomic_load16_u,
504 .i64_atomic_load8_u,
505 .i64_atomic_load16_u,
506 .i64_atomic_load32_u,
507 .i32_atomic_store,
508 .i64_atomic_store,
509 .i32_atomic_store8,
510 .i32_atomic_store16,
511 .i64_atomic_store8,
512 .i64_atomic_store16,
513 .i64_atomic_store32,
514 .i32_atomic_rmw_add,
515 .i64_atomic_rmw_add,
516 .i32_atomic_rmw8_add_u,
517 .i32_atomic_rmw16_add_u,
518 .i64_atomic_rmw8_add_u,
519 .i64_atomic_rmw16_add_u,
520 .i64_atomic_rmw32_add_u,
521 .i32_atomic_rmw_sub,
522 .i64_atomic_rmw_sub,
523 .i32_atomic_rmw8_sub_u,
524 .i32_atomic_rmw16_sub_u,
525 .i64_atomic_rmw8_sub_u,
526 .i64_atomic_rmw16_sub_u,
527 .i64_atomic_rmw32_sub_u,
528 .i32_atomic_rmw_and,
529 .i64_atomic_rmw_and,
530 .i32_atomic_rmw8_and_u,
531 .i32_atomic_rmw16_and_u,
532 .i64_atomic_rmw8_and_u,
533 .i64_atomic_rmw16_and_u,
534 .i64_atomic_rmw32_and_u,
535 .i32_atomic_rmw_or,
536 .i64_atomic_rmw_or,
537 .i32_atomic_rmw8_or_u,
538 .i32_atomic_rmw16_or_u,
539 .i64_atomic_rmw8_or_u,
540 .i64_atomic_rmw16_or_u,
541 .i64_atomic_rmw32_or_u,
542 .i32_atomic_rmw_xor,
543 .i64_atomic_rmw_xor,
544 .i32_atomic_rmw8_xor_u,
545 .i32_atomic_rmw16_xor_u,
546 .i64_atomic_rmw8_xor_u,
547 .i64_atomic_rmw16_xor_u,
548 .i64_atomic_rmw32_xor_u,
549 .i32_atomic_rmw_xchg,
550 .i64_atomic_rmw_xchg,
551 .i32_atomic_rmw8_xchg_u,
552 .i32_atomic_rmw16_xchg_u,
553 .i64_atomic_rmw8_xchg_u,
554 .i64_atomic_rmw16_xchg_u,
555 .i64_atomic_rmw32_xchg_u,
556
557 .i32_atomic_rmw_cmpxchg,
558 .i64_atomic_rmw_cmpxchg,
559 .i32_atomic_rmw8_cmpxchg_u,
560 .i32_atomic_rmw16_cmpxchg_u,
561 .i64_atomic_rmw8_cmpxchg_u,
562 .i64_atomic_rmw16_cmpxchg_u,
563 .i64_atomic_rmw32_cmpxchg_u,
564 => {
565 const mem_arg = mir.extraData(Mir.MemArg, extra_index + 1).data;
566 encodeMemArg(code, mem_arg);
567 inst += 1;
568 continue :loop tags[inst];
569 },
570 .atomic_fence => {
571 // Hard-codes memory index 0 since multi-memory proposal is
572 // not yet accepted nor implemented.
573 const memory_index: u32 = 0;
574 leb.writeUleb128(code.fixedWriter(), memory_index) catch unreachable;
575 inst += 1;
576 continue :loop tags[inst];
577 },
578 }
579 unreachable;
624580 },
625 else => |tag| return emit.fail("TODO: Implement atomic instruction: {s}", .{@tagName(tag)}),
626581 }
582 unreachable;
627583}
628584
629fn emitMemFill(emit: *Emit) !void {
630 try emit.code.append(0xFC);
631 try emit.code.append(0x0B);
632 // When multi-memory proposal reaches phase 4, we
633 // can emit a different memory index here.
634 // For now we will always emit index 0.
635 try leb128.writeUleb128(emit.code.writer(), @as(u32, 0));
636}
637
638fn emitDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
639 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
640 const dbg_line = emit.mir.extraData(Mir.DbgLineColumn, extra_index).data;
641 try emit.dbgAdvancePCAndLine(dbg_line.line, dbg_line.column);
642}
643
644fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
645 if (emit.dbg_output != .dwarf) return;
646
647 const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line));
648 const delta_pc = emit.offset() - emit.prev_di_offset;
649 // TODO: This must emit a relocation to calculate the offset relative
650 // to the code section start.
651 try emit.dbg_output.dwarf.advancePCAndLine(delta_line, delta_pc);
652
653 emit.prev_di_line = line;
654 emit.prev_di_column = column;
655 emit.prev_di_offset = emit.offset();
656}
657
658fn emitDbgPrologueEnd(emit: *Emit) !void {
659 if (emit.dbg_output != .dwarf) return;
660
661 try emit.dbg_output.dwarf.setPrologueEnd();
662 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
585/// Assert 20 unused capacity.
586fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {
587 assert(code.unusedCapacitySlice().len >= 20);
588 // Wasm encodes alignment as power of 2, rather than natural alignment.
589 const encoded_alignment = @ctz(mem_arg.alignment);
590 leb.writeUleb128(code.fixedWriter(), encoded_alignment) catch unreachable;
591 leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable;
663592}
664593
665fn emitDbgEpilogueBegin(emit: *Emit) !void {
666 if (emit.dbg_output != .dwarf) return;
594fn uavRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOff) !void {
595 const comp = wasm.base.comp;
596 const gpa = comp.gpa;
597 const target = comp.root_mod.resolved_target.result;
598 const is_wasm32 = target.cpu.arch == .wasm32;
599 const is_obj = comp.config.output_mode == .Obj;
600 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
601
602 try code.ensureUnusedCapacity(gpa, 11);
603 code.appendAssumeCapacity(@intFromEnum(opcode));
604
605 // If outputting an object file, this needs to be a relocation, since global
606 // constant data may be mixed with other object files in the final link.
607 if (is_obj) {
608 try wasm.out_relocs.append(gpa, .{
609 .offset = @intCast(code.items.len),
610 .index = try wasm.uavSymbolIndex(data.ip_index),
611 .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
612 .addend = data.offset,
613 });
614 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
615 return;
616 }
667617
668 try emit.dbg_output.dwarf.setEpilogueBegin();
669 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
618 // When linking into the final binary, no relocation mechanism is necessary.
619 const addr: i64 = try wasm.uavAddr(data.ip_index);
620 leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;
670621}
src/arch/wasm/Mir.zig+69-83
......@@ -10,10 +10,12 @@ const Mir = @This();
1010const InternPool = @import("../../InternPool.zig");
1111const Wasm = @import("../../link/Wasm.zig");
1212
13const builtin = @import("builtin");
1314const std = @import("std");
15const assert = std.debug.assert;
1416
15/// A struct of array that represents each individual wasm
16instructions: std.MultiArrayList(Inst).Slice,
17instruction_tags: []const Inst.Tag,
18instruction_datas: []const Inst.Data,
1719/// A slice of indexes where the meaning of the data is determined by the
1820/// `Inst.Tag` value.
1921extra: []const u32,
......@@ -28,13 +30,7 @@ pub const Inst = struct {
2830 /// The position of a given MIR isntruction with the instruction list.
2931 pub const Index = u32;
3032
31 /// Contains all possible wasm opcodes the Zig compiler may emit
32 /// Rather than re-using std.wasm.Opcode, we only declare the opcodes
33 /// we need, and also use this possibility to document how to access
34 /// their payload.
35 ///
36 /// Note: Uses its actual opcode value representation to easily convert
37 /// to and from its binary representation.
33 /// Some tags match wasm opcode values to facilitate trivial lowering.
3834 pub const Tag = enum(u8) {
3935 /// Uses `nop`
4036 @"unreachable" = 0x00,
......@@ -46,19 +42,27 @@ pub const Inst = struct {
4642 ///
4743 /// Type of the loop is given in data `block_type`
4844 loop = 0x03,
45 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
46 /// memory address of an unnamed constant. When emitting an object
47 /// file, this adds a relocation.
48 ///
49 /// Data is `ip_index`.
50 uav_ref,
51 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
52 /// memory address of an unnamed constant, offset by an integer value.
53 /// When emitting an object file, this adds a relocation.
54 ///
55 /// Data is `payload` pointing to a `UavRefOff`.
56 uav_ref_off,
4957 /// Inserts debug information about the current line and column
5058 /// of the source code
5159 ///
5260 /// Uses `payload` of which the payload type is `DbgLineColumn`
5361 dbg_line = 0x06,
54 /// Emits epilogue begin debug information
62 /// Emits epilogue begin debug information. Marks the end of the function.
5563 ///
5664 /// Uses `nop`
5765 dbg_epilogue_begin = 0x07,
58 /// Emits prologue end debug information
59 ///
60 /// Uses `nop`
61 dbg_prologue_end = 0x08,
6266 /// Represents the end of a function body or an initialization expression
6367 ///
6468 /// Payload is `nop`
......@@ -80,13 +84,13 @@ pub const Inst = struct {
8084 ///
8185 /// Uses `nop`
8286 @"return" = 0x0F,
87 /// Calls a function using `nav_index`.
88 call_nav,
8389 /// Calls a function pointer by its function signature
8490 /// and index into the function table.
8591 ///
86 /// Uses `label`
92 /// Uses `func_ty`
8793 call_indirect = 0x11,
88 /// Calls a function using `nav_index`.
89 call_nav,
9094 /// Calls a function using `func_index`.
9195 call_func,
9296 /// Calls a function by its index.
......@@ -94,9 +98,11 @@ pub const Inst = struct {
9498 /// The function is the auto-generated tag name function for the type
9599 /// provided in `ip_index`.
96100 call_tag_name,
97 /// Contains a symbol to a function pointer
98 /// uses `label`
101 /// Lowers to an i32_const containing the index of a function.
102 /// When emitting an object file, this adds a relocation.
103 /// Uses `ip_index`.
99104 function_index,
105
100106 /// Pops three values from the stack and pushes
101107 /// the first or second value dependent on the third value.
102108 /// Uses `tag`
......@@ -115,15 +121,11 @@ pub const Inst = struct {
115121 ///
116122 /// Uses `label`
117123 local_tee = 0x22,
118 /// Loads a (mutable) global at given index onto the stack
124 /// Pops a value from the stack and sets the stack pointer global.
125 /// The value must be the same type as the stack pointer global.
119126 ///
120 /// Uses `label`
121 global_get = 0x23,
122 /// Pops a value from the stack and sets the global at given index.
123 /// Note: Both types must be equal and global must be marked mutable.
124 ///
125 /// Uses `label`.
126 global_set = 0x24,
127 /// Uses `tag` (no additional data).
128 global_set_sp,
127129 /// Loads a 32-bit integer from memory (data section) onto the stack
128130 /// Pops the value from the stack which represents the offset into memory.
129131 ///
......@@ -259,19 +261,19 @@ pub const Inst = struct {
259261 /// Loads a 32-bit signed immediate value onto the stack
260262 ///
261263 /// Uses `imm32`
262 i32_const = 0x41,
264 i32_const,
263265 /// Loads a i64-bit signed immediate value onto the stack
264266 ///
265267 /// uses `payload` of type `Imm64`
266 i64_const = 0x42,
268 i64_const,
267269 /// Loads a 32-bit float value onto the stack.
268270 ///
269271 /// Uses `float32`
270 f32_const = 0x43,
272 f32_const,
271273 /// Loads a 64-bit float value onto the stack.
272274 ///
273275 /// Uses `payload` of type `Float64`
274 f64_const = 0x44,
276 f64_const,
275277 /// Uses `tag`
276278 i32_eqz = 0x45,
277279 /// Uses `tag`
......@@ -525,25 +527,19 @@ pub const Inst = struct {
525527 ///
526528 /// The `data` field depends on the extension instruction and
527529 /// may contain additional data.
528 misc_prefix = 0xFC,
530 misc_prefix,
529531 /// The instruction consists of a simd opcode.
530532 /// The actual simd-opcode is found at payload's index.
531533 ///
532534 /// The `data` field depends on the simd instruction and
533535 /// may contain additional data.
534 simd_prefix = 0xFD,
536 simd_prefix,
535537 /// The instruction consists of an atomics opcode.
536538 /// The actual atomics-opcode is found at payload's index.
537539 ///
538540 /// The `data` field depends on the atomics instruction and
539541 /// may contain additional data.
540542 atomics_prefix = 0xFE,
541 /// Contains a symbol to a memory address
542 /// Uses `label`
543 ///
544 /// Note: This uses `0xFF` as value as it is unused and not reserved
545 /// by the wasm specification, making it safe to use.
546 memory_address = 0xFF,
547543
548544 /// From a given wasm opcode, returns a MIR tag.
549545 pub fn fromOpcode(opcode: std.wasm.Opcode) Tag {
......@@ -563,30 +559,38 @@ pub const Inst = struct {
563559 /// Uses no additional data
564560 tag: void,
565561 /// Contains the result type of a block
566 ///
567 /// Used by `block` and `loop`
568562 block_type: u8,
569 /// Contains an u32 index into a wasm section entry, such as a local.
570 /// Note: This is not an index to another instruction.
571 ///
572 /// Used by e.g. `local_get`, `local_set`, etc.
563 /// Label: Each structured control instruction introduces an implicit label.
564 /// Labels are targets for branch instructions that reference them with
565 /// label indices. Unlike with other index spaces, indexing of labels
566 /// is relative by nesting depth, that is, label 0 refers to the
567 /// innermost structured control instruction enclosing the referring
568 /// branch instruction, while increasing indices refer to those farther
569 /// out. Consequently, labels can only be referenced from within the
570 /// associated structured control instruction.
573571 label: u32,
572 /// Local: The index space for locals is only accessible inside a function and
573 /// includes the parameters of that function, which precede the local
574 /// variables.
575 local: u32,
574576 /// A 32-bit immediate value.
575 ///
576 /// Used by `i32_const`
577577 imm32: i32,
578578 /// A 32-bit float value
579 ///
580 /// Used by `f32_float`
581579 float32: f32,
582580 /// Index into `extra`. Meaning of what can be found there is context-dependent.
583 ///
584 /// Used by e.g. `br_table`
585581 payload: u32,
586582
587583 ip_index: InternPool.Index,
588584 nav_index: InternPool.Nav.Index,
589585 func_index: Wasm.FunctionIndex,
586 func_ty: Wasm.FunctionType.Index,
587
588 comptime {
589 switch (builtin.mode) {
590 .Debug, .ReleaseSafe => {},
591 .ReleaseFast, .ReleaseSmall => assert(@sizeOf(Data) == 4),
592 }
593 }
590594 };
591595};
592596
......@@ -616,28 +620,19 @@ pub const JumpTable = struct {
616620 length: u32,
617621};
618622
619/// Stores an unsigned 64bit integer
620/// into a 32bit most significant bits field
621/// and a 32bit least significant bits field.
622///
623/// This uses an unsigned integer rather than a signed integer
624/// as we can easily store those into `extra`
625623pub const Imm64 = struct {
626624 msb: u32,
627625 lsb: u32,
628626
629 pub fn fromU64(imm: u64) Imm64 {
627 pub fn init(full: u64) Imm64 {
630628 return .{
631 .msb = @as(u32, @truncate(imm >> 32)),
632 .lsb = @as(u32, @truncate(imm)),
629 .msb = @truncate(full >> 32),
630 .lsb = @truncate(full),
633631 };
634632 }
635633
636 pub fn toU64(self: Imm64) u64 {
637 var result: u64 = 0;
638 result |= @as(u64, self.msb) << 32;
639 result |= @as(u64, self.lsb);
640 return result;
634 pub fn toInt(i: Imm64) u64 {
635 return (@as(u64, i.msb) << 32) | @as(u64, i.lsb);
641636 }
642637};
643638
......@@ -645,23 +640,16 @@ pub const Float64 = struct {
645640 msb: u32,
646641 lsb: u32,
647642
648 pub fn fromFloat64(float: f64) Float64 {
649 const tmp = @as(u64, @bitCast(float));
643 pub fn init(f: f64) Float64 {
644 const int: u64 = @bitCast(f);
650645 return .{
651 .msb = @as(u32, @truncate(tmp >> 32)),
652 .lsb = @as(u32, @truncate(tmp)),
646 .msb = @truncate(int >> 32),
647 .lsb = @truncate(int),
653648 };
654649 }
655650
656 pub fn toF64(self: Float64) f64 {
657 @as(f64, @bitCast(self.toU64()));
658 }
659
660 pub fn toU64(self: Float64) u64 {
661 var result: u64 = 0;
662 result |= @as(u64, self.msb) << 32;
663 result |= @as(u64, self.lsb);
664 return result;
651 pub fn toInt(f: Float64) u64 {
652 return (@as(u64, f.msb) << 32) | @as(u64, f.lsb);
665653 }
666654};
667655
......@@ -670,11 +658,9 @@ pub const MemArg = struct {
670658 alignment: u32,
671659};
672660
673/// Represents a memory address, which holds both the pointer
674/// or the parent pointer and the offset to it.
675pub const Memory = struct {
676 pointer: u32,
677 offset: u32,
661pub const UavRefOff = struct {
662 ip_index: InternPool.Index,
663 offset: i32,
678664};
679665
680666/// Maps a source line with wasm bytecode
src/arch/x86_64/CodeGen.zig+1-1
......@@ -1040,7 +1040,7 @@ pub fn generateLazy(
10401040 emit.emitMir() catch |err| switch (err) {
10411041 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?),
10421042 error.InvalidInstruction => return function.fail("failed to find a viable x86 instruction (Zig compiler bug)", .{}),
1043 error.CannotEncode => return function.fail("failed to find encode x86 instruction (Zig compiler bug)", .{}),
1043 error.CannotEncode => return function.fail("failed to encode x86 instruction (Zig compiler bug)", .{}),
10441044 else => |e| return function.fail("failed to emit MIR: {s}", .{@errorName(e)}),
10451045 };
10461046}
src/codegen/llvm.zig+5-5
......@@ -1838,11 +1838,11 @@ pub const Object = struct {
18381838 o: *Object,
18391839 zcu: *Zcu,
18401840 exported_value: InternPool.Index,
1841 export_indices: []const u32,
1841 export_indices: []const Zcu.Export.Index,
18421842 ) link.File.UpdateExportsError!void {
18431843 const gpa = zcu.gpa;
18441844 const ip = &zcu.intern_pool;
1845 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));
18461846 const global_index = i: {
18471847 const gop = try o.uav_map.getOrPut(gpa, exported_value);
18481848 if (gop.found_existing) {
......@@ -1873,11 +1873,11 @@ pub const Object = struct {
18731873 o: *Object,
18741874 zcu: *Zcu,
18751875 global_index: Builder.Global.Index,
1876 export_indices: []const u32,
1876 export_indices: []const Zcu.Export.Index,
18771877 ) link.File.UpdateExportsError!void {
18781878 const comp = zcu.comp;
18791879 const ip = &zcu.intern_pool;
1880 const first_export = zcu.all_exports.items[export_indices[0]];
1880 const first_export = export_indices[0].ptr(zcu);
18811881
18821882 // We will rename this global to have a name matching `first_export`.
18831883 // Successive exports become aliases.
......@@ -1934,7 +1934,7 @@ pub const Object = struct {
19341934 // Until then we iterate over existing aliases and make them point
19351935 // to the correct decl, or otherwise add a new alias. Old aliases are leaked.
19361936 for (export_indices[1..]) |export_idx| {
1937 const exp = zcu.all_exports.items[export_idx];
1937 const exp = export_idx.ptr(zcu);
19381938 const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip));
19391939 if (o.builder.getGlobal(exp_name)) |global| {
19401940 switch (global.ptrConst(&o.builder).kind) {
src/link/C.zig+1-1
......@@ -469,7 +469,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
469469 defer export_names.deinit(gpa);
470470 try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count()));
471471 for (zcu.single_exports.values()) |export_index| {
472 export_names.putAssumeCapacity(zcu.all_exports.items[export_index].opts.name, {});
472 export_names.putAssumeCapacity(export_index.ptr(zcu).opts.name, {});
473473 }
474474 for (zcu.multi_exports.values()) |info| {
475475 try export_names.ensureUnusedCapacity(gpa, info.len);
src/link/Coff.zig+4-4
......@@ -1478,7 +1478,7 @@ pub fn updateExports(
14781478 coff: *Coff,
14791479 pt: Zcu.PerThread,
14801480 exported: Zcu.Exported,
1481 export_indices: []const u32,
1481 export_indices: []const Zcu.Export.Index,
14821482) link.File.UpdateExportsError!void {
14831483 if (build_options.skip_non_native and builtin.object_format != .coff) {
14841484 @panic("Attempted to compile for object format that was disabled by build configuration");
......@@ -1493,7 +1493,7 @@ pub fn updateExports(
14931493 // Even in the case of LLVM, we need to notice certain exported symbols in order to
14941494 // detect the default subsystem.
14951495 for (export_indices) |export_idx| {
1496 const exp = zcu.all_exports.items[export_idx];
1496 const exp = export_idx.ptr(zcu);
14971497 const exported_nav_index = switch (exp.exported) {
14981498 .nav => |nav| nav,
14991499 .uav => continue,
......@@ -1536,7 +1536,7 @@ pub fn updateExports(
15361536 break :blk coff.navs.getPtr(nav).?;
15371537 },
15381538 .uav => |uav| coff.uavs.getPtr(uav) orelse blk: {
1539 const first_exp = zcu.all_exports.items[export_indices[0]];
1539 const first_exp = export_indices[0].ptr(zcu);
15401540 const res = try coff.lowerUav(pt, uav, .none, first_exp.src);
15411541 switch (res) {
15421542 .mcv => {},
......@@ -1555,7 +1555,7 @@ pub fn updateExports(
15551555 const atom = coff.getAtom(atom_index);
15561556
15571557 for (export_indices) |export_idx| {
1558 const exp = zcu.all_exports.items[export_idx];
1558 const exp = export_idx.ptr(zcu);
15591559 log.debug("adding new export '{}'", .{exp.opts.name.fmt(&zcu.intern_pool)});
15601560
15611561 if (exp.opts.section.toSlice(&zcu.intern_pool)) |section_name| {
src/link/Elf/ZigObject.zig+2-2
......@@ -1758,7 +1758,7 @@ pub fn updateExports(
17581758 break :blk self.navs.getPtr(nav).?;
17591759 },
17601760 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1761 const first_exp = zcu.all_exports.items[export_indices[0]];
1761 const first_exp = export_indices[0].ptr(zcu);
17621762 const res = try self.lowerUav(elf_file, pt, uav, .none, first_exp.src);
17631763 switch (res) {
17641764 .mcv => {},
......@@ -1779,7 +1779,7 @@ pub fn updateExports(
17791779 const esym_shndx = self.symtab.items(.shndx)[esym_index];
17801780
17811781 for (export_indices) |export_idx| {
1782 const exp = zcu.all_exports.items[export_idx];
1782 const exp = export_idx.ptr(zcu);
17831783 if (exp.opts.section.unwrap()) |section_name| {
17841784 if (!section_name.eqlSlice(".text", &zcu.intern_pool)) {
17851785 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
src/link/MachO/ZigObject.zig+2-2
......@@ -1259,7 +1259,7 @@ pub fn updateExports(
12591259 break :blk self.navs.getPtr(nav).?;
12601260 },
12611261 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1262 const first_exp = zcu.all_exports.items[export_indices[0]];
1262 const first_exp = export_indices[0].ptr(zcu);
12631263 const res = try self.lowerUav(macho_file, pt, uav, .none, first_exp.src);
12641264 switch (res) {
12651265 .mcv => {},
......@@ -1279,7 +1279,7 @@ pub fn updateExports(
12791279 const nlist = self.symtab.items(.nlist)[nlist_idx];
12801280
12811281 for (export_indices) |export_idx| {
1282 const exp = zcu.all_exports.items[export_idx];
1282 const exp = export_idx.ptr(zcu);
12831283 if (exp.opts.section.unwrap()) |section_name| {
12841284 if (!section_name.eqlSlice("__text", &zcu.intern_pool)) {
12851285 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
src/link/Plan9.zig+16-16
......@@ -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),
......@@ -415,7 +416,7 @@ pub fn updateFunc(
415416 };
416417 defer dbg_info_output.dbg_line.deinit();
417418
418 const res = try codegen.generateFunction(
419 try codegen.generateFunction(
419420 &self.base,
420421 pt,
421422 zcu.navSrcLoc(func.owner_nav),
......@@ -425,10 +426,7 @@ pub fn updateFunc(
425426 &code_buffer,
426427 .{ .plan9 = &dbg_info_output },
427428 );
428 const code = switch (res) {
429 .ok => try code_buffer.toOwnedSlice(),
430 .fail => |em| return zcu.failed_codegen.put(gpa, func.owner_nav, em),
431 };
429 const code = try code_buffer.toOwnedSlice();
432430 self.getAtomPtr(atom_idx).code = .{
433431 .code_ptr = null,
434432 .other = .{ .nav_index = func.owner_nav },
......@@ -439,7 +437,9 @@ pub fn updateFunc(
439437 .start_line = dbg_info_output.start_line.?,
440438 .end_line = dbg_info_output.end_line,
441439 };
442 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)});
443443 return self.updateFinish(pt, func.owner_nav);
444444}
445445
......@@ -915,25 +915,25 @@ pub fn flushModule(
915915}
916916fn addNavExports(
917917 self: *Plan9,
918 mod: *Zcu,
918 zcu: *Zcu,
919919 nav_index: InternPool.Nav.Index,
920 export_indices: []const u32,
920 export_indices: []const Zcu.Export.Index,
921921) !void {
922922 const gpa = self.base.comp.gpa;
923923 const metadata = self.navs.getPtr(nav_index).?;
924924 const atom = self.getAtom(metadata.index);
925925
926926 for (export_indices) |export_idx| {
927 const exp = mod.all_exports.items[export_idx];
928 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);
929929 // plan9 does not support custom sections
930930 if (exp.opts.section.unwrap()) |section_name| {
931 if (!section_name.eqlSlice(".text", &mod.intern_pool) and
932 !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))
933933 {
934 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(
935935 gpa,
936 mod.navSrcLoc(nav_index),
936 zcu.navSrcLoc(nav_index),
937937 "plan9 does not support extra sections",
938938 .{},
939939 ));
......@@ -1252,7 +1252,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
12521252 try self.writeSym(writer, sym);
12531253 if (self.nav_exports.get(nav_index)) |export_indices| {
12541254 for (export_indices) |export_idx| {
1255 const exp = zcu.all_exports.items[export_idx];
1255 const exp = export_idx.ptr(zcu);
12561256 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {
12571257 try self.writeSym(writer, self.syms.items[exp_i]);
12581258 }
......@@ -1291,7 +1291,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
12911291 try self.writeSym(writer, sym);
12921292 if (self.nav_exports.get(nav_index)) |export_indices| {
12931293 for (export_indices) |export_idx| {
1294 const exp = zcu.all_exports.items[export_idx];
1294 const exp = export_idx.ptr(zcu);
12951295 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {
12961296 const s = self.syms.items[exp_i];
12971297 if (mem.eql(u8, s.name, "_start"))
src/link/Wasm.zig+40-54
......@@ -30,6 +30,7 @@ const log = std.log.scoped(.link);
3030const mem = std.mem;
3131
3232const Air = @import("../Air.zig");
33const Mir = @import("../arch/wasm/Mir.zig");
3334const CodeGen = @import("../arch/wasm/CodeGen.zig");
3435const Compilation = @import("../Compilation.zig");
3536const Dwarf = @import("Dwarf.zig");
......@@ -151,6 +152,7 @@ dump_argv_list: std.ArrayListUnmanaged([]const u8),
151152preloaded_strings: PreloadedStrings,
152153
153154navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Nav) = .empty,
155zcu_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuFunc) = .empty,
154156nav_exports: std.AutoArrayHashMapUnmanaged(NavExport, Zcu.Export.Index) = .empty,
155157uav_exports: std.AutoArrayHashMapUnmanaged(UavExport, Zcu.Export.Index) = .empty,
156158imports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
......@@ -203,6 +205,13 @@ table_imports: std.AutoArrayHashMapUnmanaged(String, ObjectTableImportIndex) = .
203205
204206any_exports_updated: bool = true,
205207
208/// All MIR instructions for all Zcu functions.
209mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
210/// Corresponds to `mir_instructions`.
211mir_extra: std.ArrayListUnmanaged(u32) = .empty,
212/// All local types for all Zcu functions.
213all_zcu_locals: std.ArrayListUnmanaged(u8) = .empty,
214
206215/// Index into `objects`.
207216pub const ObjectIndex = enum(u32) {
208217 _,
......@@ -439,6 +448,24 @@ pub const Nav = extern struct {
439448 };
440449};
441450
451pub const ZcuFunc = extern struct {
452 function: CodeGen.Function,
453
454 /// Index into `zcu_funcs`.
455 /// Note that swapRemove is sometimes performed on `zcu_funcs`.
456 pub const Index = enum(u32) {
457 _,
458
459 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index {
460 return &wasm.zcu_funcs.keys()[@intFromEnum(i)];
461 }
462
463 pub fn value(i: @This(), wasm: *const Wasm) *ZcuFunc {
464 return &wasm.zcu_funcs.values()[@intFromEnum(i)];
465 }
466 };
467};
468
442469pub const NavExport = extern struct {
443470 name: String,
444471 nav_index: InternPool.Nav.Index,
......@@ -932,7 +959,7 @@ pub const ValtypeList = enum(u32) {
932959 }
933960
934961 pub fn slice(index: ValtypeList, wasm: *const Wasm) []const std.wasm.Valtype {
935 return @bitCast(String.slice(@enumFromInt(@intFromEnum(index)), wasm));
962 return @ptrCast(String.slice(@enumFromInt(@intFromEnum(index)), wasm));
936963 }
937964};
938965
......@@ -1430,12 +1457,17 @@ pub fn deinit(wasm: *Wasm) void {
14301457 if (wasm.llvm_object) |llvm_object| llvm_object.deinit();
14311458
14321459 wasm.navs.deinit(gpa);
1460 wasm.zcu_funcs.deinit(gpa);
14331461 wasm.nav_exports.deinit(gpa);
14341462 wasm.uav_exports.deinit(gpa);
14351463 wasm.imports.deinit(gpa);
14361464
14371465 wasm.flush_buffer.deinit(gpa);
14381466
1467 wasm.mir_instructions.deinit(gpa);
1468 wasm.mir_extra.deinit(gpa);
1469 wasm.all_zcu_locals.deinit(gpa);
1470
14391471 if (wasm.dwarf) |*dwarf| dwarf.deinit();
14401472
14411473 wasm.object_function_imports.deinit(gpa);
......@@ -1474,49 +1506,11 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index,
14741506 }
14751507 if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness);
14761508
1477 const zcu = pt.zcu;
1478 const gpa = zcu.gpa;
1479 const func = pt.zcu.funcInfo(func_index);
1480 const nav_index = func.owner_nav;
1481
1482 const code_start: u32 = @intCast(wasm.string_bytes.items.len);
1483 const relocs_start: u32 = @intCast(wasm.relocations.len);
1484 wasm.string_bytes_lock.lock();
1485
14861509 dev.check(.wasm_backend);
1487 try CodeGen.generate(
1488 &wasm.base,
1489 pt,
1490 zcu.navSrcLoc(nav_index),
1491 func_index,
1492 air,
1493 liveness,
1494 &wasm.string_bytes,
1495 .none,
1496 );
1497
1498 const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start);
1499 const relocs_len: u32 = @intCast(wasm.relocations.len - relocs_start);
1500 wasm.string_bytes_lock.unlock();
1501
1502 const code: Nav.Code = .{
1503 .off = code_start,
1504 .len = code_len,
1505 };
15061510
1507 const gop = try wasm.navs.getOrPut(gpa, nav_index);
1508 if (gop.found_existing) {
1509 @panic("TODO reuse these resources");
1510 } else {
1511 _ = wasm.imports.swapRemove(nav_index);
1512 }
1513 gop.value_ptr.* = .{
1514 .code = code,
1515 .relocs = .{
1516 .off = relocs_start,
1517 .len = relocs_len,
1518 },
1519 };
1511 try wasm.zcu_funcs.put(pt.zcu.gpa, func_index, .{
1512 .function = try CodeGen.function(wasm, pt, func_index, air, liveness),
1513 });
15201514}
15211515
15221516// Generate code for the "Nav", storing it in memory to be later written to
......@@ -1642,8 +1636,8 @@ pub fn updateExports(
16421636 const exp = export_idx.ptr(zcu);
16431637 const name = try wasm.internString(exp.opts.name.toSlice(ip));
16441638 switch (exported) {
1645 .nav => |nav_index| wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx),
1646 .uav => |uav_index| wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx),
1639 .nav => |nav_index| try wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx),
1640 .uav => |uav_index| try wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx),
16471641 }
16481642 }
16491643 wasm.any_exports_updated = true;
......@@ -1713,9 +1707,9 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
17131707 continue;
17141708 }
17151709 }
1716 try wasm.missing_exports.put(exp_name_interned, {});
1710 try missing_exports.put(gpa, exp_name_interned, {});
17171711 }
1718 wasm.missing_exports_init = try gpa.dupe(String, wasm.missing_exports.keys());
1712 wasm.missing_exports_init = try gpa.dupe(String, missing_exports.keys());
17191713 }
17201714
17211715 if (wasm.entry_name.unwrap()) |entry_name| {
......@@ -2347,14 +2341,6 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
23472341 }
23482342}
23492343
2350/// Returns the symbol index of the error name table.
2351///
2352/// When the symbol does not yet exist, it will create a new one instead.
2353pub fn getErrorTableSymbol(wasm: *Wasm, pt: Zcu.PerThread) !u32 {
2354 const sym_index = try wasm.zig_object.?.getErrorTableSymbol(wasm, pt);
2355 return @intFromEnum(sym_index);
2356}
2357
23582344fn defaultEntrySymbolName(
23592345 preloaded_strings: *const PreloadedStrings,
23602346 wasi_exec_model: std.builtin.WasiExecModel,