authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-06-03 16:25:16+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-06-12 13:55:40+01:00
logc0df70706695a67089d4e691d3d3a0f77b90298f
treecc05dafeb6b079455c95be905761f08982a1c6fa
parent5ab307cf47b1f0418d9ed4ab56df6fb798305c20
signaturelock-open Commit is signed but in an unrecognized format.

wasm: get self-hosted compiling, and supporting `separate_thread`

My original goal here was just to get the self-hosted Wasm backend compiling again after the pipeline change, but it turned out that from there it was pretty simple to entirely eliminate the shared state between `codegen.wasm` and `link.Wasm`. As such, this commit not only fixes the backend, but makes it the second backend (after CBE) to support the new 1:N:1 threading model.

10 files changed, 404 insertions(+), 311 deletions(-)

lib/std/multi_array_list.zig+16
...@@ -135,6 +135,22 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -135,6 +135,22 @@ pub fn MultiArrayList(comptime T: type) type {
135 self.* = undefined;135 self.* = undefined;
136 }136 }
137137
138 /// Returns a `Slice` representing a range of elements in `s`, analagous to `arr[off..len]`.
139 /// It is illegal to call `deinit` or `toMultiArrayList` on the returned `Slice`.
140 /// Asserts that `off + len <= s.len`.
141 pub fn subslice(s: Slice, off: usize, len: usize) Slice {
142 assert(off + len <= s.len);
143 var ptrs: [fields.len][*]u8 = undefined;
144 inline for (s.ptrs, &ptrs, fields) |in, *out, field| {
145 out.* = in + (off * @sizeOf(field.type));
146 }
147 return .{
148 .ptrs = ptrs,
149 .len = len,
150 .capacity = len,
151 };
152 }
153
138 /// This function is used in the debugger pretty formatters in tools/ to fetch the154 /// This function is used in the debugger pretty formatters in tools/ to fetch the
139 /// child field order and entry type to facilitate fancy debug printing for this type.155 /// child field order and entry type to facilitate fancy debug printing for this type.
140 fn dbHelper(self: *Slice, child: *Elem, field: *Field, entry: *Entry) void {156 fn dbHelper(self: *Slice, child: *Elem, field: *Field, entry: *Entry) void {
src/Compilation.zig+1-1
...@@ -3500,7 +3500,7 @@ pub fn saveState(comp: *Compilation) !void {...@@ -3500,7 +3500,7 @@ pub fn saveState(comp: *Compilation) !void {
3500 // TODO handle the union safety field3500 // TODO handle the union safety field
3501 //addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.data)));3501 //addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.data)));
3502 addBuf(&bufs, mem.sliceAsBytes(wasm.mir_extra.items));3502 addBuf(&bufs, mem.sliceAsBytes(wasm.mir_extra.items));
3503 addBuf(&bufs, mem.sliceAsBytes(wasm.all_zcu_locals.items));3503 addBuf(&bufs, mem.sliceAsBytes(wasm.mir_locals.items));
3504 addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_bytes.items));3504 addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_bytes.items));
3505 addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_offs.items));3505 addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_offs.items));
35063506
src/arch/wasm/CodeGen.zig+103-198
...@@ -3,7 +3,6 @@ const builtin = @import("builtin");...@@ -3,7 +3,6 @@ const builtin = @import("builtin");
3const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const testing = std.testing;5const testing = std.testing;
6const leb = std.leb;
7const mem = std.mem;6const mem = std.mem;
8const log = std.log.scoped(.codegen);7const log = std.log.scoped(.codegen);
98
...@@ -18,12 +17,10 @@ const Compilation = @import("../../Compilation.zig");...@@ -18,12 +17,10 @@ const Compilation = @import("../../Compilation.zig");
18const link = @import("../../link.zig");17const link = @import("../../link.zig");
19const Air = @import("../../Air.zig");18const Air = @import("../../Air.zig");
20const Mir = @import("Mir.zig");19const Mir = @import("Mir.zig");
21const Emit = @import("Emit.zig");
22const abi = @import("abi.zig");20const abi = @import("abi.zig");
23const Alignment = InternPool.Alignment;21const Alignment = InternPool.Alignment;
24const errUnionPayloadOffset = codegen.errUnionPayloadOffset;22const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
25const errUnionErrorOffset = codegen.errUnionErrorOffset;23const errUnionErrorOffset = codegen.errUnionErrorOffset;
26const Wasm = link.File.Wasm;
2724
28const target_util = @import("../../target.zig");25const target_util = @import("../../target.zig");
29const libcFloatPrefix = target_util.libcFloatPrefix;26const libcFloatPrefix = target_util.libcFloatPrefix;
...@@ -78,17 +75,24 @@ simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty,...@@ -78,17 +75,24 @@ simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty,
78/// The Target we're emitting (used to call intInfo)75/// The Target we're emitting (used to call intInfo)
79target: *const std.Target,76target: *const std.Target,
80ptr_size: enum { wasm32, wasm64 },77ptr_size: enum { wasm32, wasm64 },
81wasm: *link.File.Wasm,
82pt: Zcu.PerThread,78pt: Zcu.PerThread,
83/// List of MIR Instructions79/// List of MIR Instructions
84mir_instructions: *std.MultiArrayList(Mir.Inst),80mir_instructions: std.MultiArrayList(Mir.Inst),
85/// Contains extra data for MIR81/// Contains extra data for MIR
86mir_extra: *std.ArrayListUnmanaged(u32),82mir_extra: std.ArrayListUnmanaged(u32),
87start_mir_extra_off: u32,
88start_locals_off: u32,
89/// List of all locals' types generated throughout this declaration83/// List of all locals' types generated throughout this declaration
90/// used to emit locals count at start of 'code' section.84/// used to emit locals count at start of 'code' section.
91locals: *std.ArrayListUnmanaged(std.wasm.Valtype),85mir_locals: std.ArrayListUnmanaged(std.wasm.Valtype),
86/// Set of all UAVs referenced by this function. Key is the UAV value, value is the alignment.
87/// `.none` means naturally aligned. An explicit alignment is never less than the natural alignment.
88mir_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
89/// Set of all functions whose address this function has taken and which therefore might be called
90/// via a `call_indirect` function.
91mir_indirect_function_set: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void),
92/// Set of all function types used by this function. These must be interned by the linker.
93mir_func_tys: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
94/// The number of `error_name_table_ref` instructions emitted.
95error_name_table_ref_count: u32,
92/// When a function is executing, we store the the current stack pointer's value within this local.96/// When a function is executing, we store the the current stack pointer's value within this local.
93/// This value is then used to restore the stack pointer to the original value at the return of the function.97/// This value is then used to restore the stack pointer to the original value at the return of the function.
94initial_stack_value: WValue = .none,98initial_stack_value: WValue = .none,
...@@ -219,7 +223,7 @@ const WValue = union(enum) {...@@ -219,7 +223,7 @@ const WValue = union(enum) {
219 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.223 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.
220224
221 const index = local_value - reserved;225 const index = local_value - reserved;
222 const valtype = gen.locals.items[gen.start_locals_off + index];226 const valtype = gen.mir_locals.items[index];
223 switch (valtype) {227 switch (valtype) {
224 .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead228 .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead
225 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,229 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,
...@@ -716,6 +720,12 @@ pub fn deinit(cg: *CodeGen) void {...@@ -716,6 +720,12 @@ pub fn deinit(cg: *CodeGen) void {
716 cg.free_locals_f32.deinit(gpa);720 cg.free_locals_f32.deinit(gpa);
717 cg.free_locals_f64.deinit(gpa);721 cg.free_locals_f64.deinit(gpa);
718 cg.free_locals_v128.deinit(gpa);722 cg.free_locals_v128.deinit(gpa);
723 cg.mir_instructions.deinit(gpa);
724 cg.mir_extra.deinit(gpa);
725 cg.mir_locals.deinit(gpa);
726 cg.mir_uavs.deinit(gpa);
727 cg.mir_indirect_function_set.deinit(gpa);
728 cg.mir_func_tys.deinit(gpa);
719 cg.* = undefined;729 cg.* = undefined;
720}730}
721731
...@@ -876,7 +886,7 @@ fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {...@@ -876,7 +886,7 @@ fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
876}886}
877887
878fn addExtended(cg: *CodeGen, opcode: std.wasm.MiscOpcode) error{OutOfMemory}!void {888fn addExtended(cg: *CodeGen, opcode: std.wasm.MiscOpcode) error{OutOfMemory}!void {
879 const extra_index = cg.extraLen();889 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
880 try cg.mir_extra.append(cg.gpa, @intFromEnum(opcode));890 try cg.mir_extra.append(cg.gpa, @intFromEnum(opcode));
881 try cg.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });891 try cg.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });
882}892}
...@@ -889,10 +899,6 @@ fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void...@@ -889,10 +899,6 @@ fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void
889 try cg.addInst(.{ .tag = tag, .data = .{ .local = local } });899 try cg.addInst(.{ .tag = tag, .data = .{ .local = local } });
890}900}
891901
892fn addFuncTy(cg: *CodeGen, tag: Mir.Inst.Tag, i: Wasm.FunctionType.Index) error{OutOfMemory}!void {
893 try cg.addInst(.{ .tag = tag, .data = .{ .func_ty = i } });
894}
895
896/// Accepts an unsigned 32bit integer rather than a signed integer to902/// Accepts an unsigned 32bit integer rather than a signed integer to
897/// prevent us from having to bitcast multiple times as most values903/// prevent us from having to bitcast multiple times as most values
898/// within codegen are represented as unsigned rather than signed.904/// within codegen are represented as unsigned rather than signed.
...@@ -911,7 +917,7 @@ fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void {...@@ -911,7 +917,7 @@ fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void {
911/// Accepts the index into the list of 128bit-immediates917/// Accepts the index into the list of 128bit-immediates
912fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void {918fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void {
913 const simd_values = cg.simd_immediates.items[index];919 const simd_values = cg.simd_immediates.items[index];
914 const extra_index = cg.extraLen();920 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
915 // tag + 128bit value921 // tag + 128bit value
916 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 5);922 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 5);
917 cg.mir_extra.appendAssumeCapacity(@intFromEnum(std.wasm.SimdOpcode.v128_const));923 cg.mir_extra.appendAssumeCapacity(@intFromEnum(std.wasm.SimdOpcode.v128_const));
...@@ -956,15 +962,13 @@ fn addExtra(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {...@@ -956,15 +962,13 @@ fn addExtra(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
956/// Returns the index into `mir_extra`962/// Returns the index into `mir_extra`
957fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {963fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
958 const fields = std.meta.fields(@TypeOf(extra));964 const fields = std.meta.fields(@TypeOf(extra));
959 const result = cg.extraLen();965 const result: u32 = @intCast(cg.mir_extra.items.len);
960 inline for (fields) |field| {966 inline for (fields) |field| {
961 cg.mir_extra.appendAssumeCapacity(switch (field.type) {967 cg.mir_extra.appendAssumeCapacity(switch (field.type) {
962 u32 => @field(extra, field.name),968 u32 => @field(extra, field.name),
963 i32 => @bitCast(@field(extra, field.name)),969 i32 => @bitCast(@field(extra, field.name)),
964 InternPool.Index,970 InternPool.Index,
965 InternPool.Nav.Index,971 InternPool.Nav.Index,
966 Wasm.UavsObjIndex,
967 Wasm.UavsExeIndex,
968 => @intFromEnum(@field(extra, field.name)),972 => @intFromEnum(@field(extra, field.name)),
969 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),973 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
970 });974 });
...@@ -1034,18 +1038,12 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {...@@ -1034,18 +1038,12 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
1034 .float32 => |val| try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),1038 .float32 => |val| try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
1035 .float64 => |val| try cg.addFloat64(val),1039 .float64 => |val| try cg.addFloat64(val),
1036 .nav_ref => |nav_ref| {1040 .nav_ref => |nav_ref| {
1037 const wasm = cg.wasm;1041 const zcu = cg.pt.zcu;
1038 const comp = wasm.base.comp;
1039 const zcu = comp.zcu.?;
1040 const ip = &zcu.intern_pool;1042 const ip = &zcu.intern_pool;
1041 if (ip.getNav(nav_ref.nav_index).isFn(ip)) {1043 if (ip.getNav(nav_ref.nav_index).isFn(ip)) {
1042 assert(nav_ref.offset == 0);1044 assert(nav_ref.offset == 0);
1043 const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, nav_ref.nav_index);1045 try cg.mir_indirect_function_set.put(cg.gpa, nav_ref.nav_index, {});
1044 if (!gop.found_existing) gop.value_ptr.* = {};1046 try cg.addInst(.{ .tag = .func_ref, .data = .{ .nav_index = nav_ref.nav_index } });
1045 try cg.addInst(.{
1046 .tag = .func_ref,
1047 .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) },
1048 });
1049 } else if (nav_ref.offset == 0) {1047 } else if (nav_ref.offset == 0) {
1050 try cg.addInst(.{ .tag = .nav_ref, .data = .{ .nav_index = nav_ref.nav_index } });1048 try cg.addInst(.{ .tag = .nav_ref, .data = .{ .nav_index = nav_ref.nav_index } });
1051 } else {1049 } else {
...@@ -1061,41 +1059,37 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {...@@ -1061,41 +1059,37 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
1061 }1059 }
1062 },1060 },
1063 .uav_ref => |uav| {1061 .uav_ref => |uav| {
1064 const wasm = cg.wasm;1062 const zcu = cg.pt.zcu;
1065 const comp = wasm.base.comp;
1066 const is_obj = comp.config.output_mode == .Obj;
1067 const zcu = comp.zcu.?;
1068 const ip = &zcu.intern_pool;1063 const ip = &zcu.intern_pool;
1069 if (ip.isFunctionType(ip.typeOf(uav.ip_index))) {1064 assert(!ip.isFunctionType(ip.typeOf(uav.ip_index)));
1070 assert(uav.offset == 0);1065 const gop = try cg.mir_uavs.getOrPut(cg.gpa, uav.ip_index);
1071 const owner_nav = ip.toFunc(uav.ip_index).owner_nav;1066 const this_align: Alignment = a: {
1072 const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, owner_nav);1067 if (uav.orig_ptr_ty == .none) break :a .none;
1073 if (!gop.found_existing) gop.value_ptr.* = {};1068 const ptr_type = ip.indexToKey(uav.orig_ptr_ty).ptr_type;
1074 try cg.addInst(.{1069 const this_align = ptr_type.flags.alignment;
1075 .tag = .func_ref,1070 if (this_align == .none) break :a .none;
1076 .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) },1071 const abi_align = Type.fromInterned(ptr_type.child).abiAlignment(zcu);
1077 });1072 if (this_align.compare(.lte, abi_align)) break :a .none;
1078 } else if (uav.offset == 0) {1073 break :a this_align;
1074 };
1075 if (!gop.found_existing or
1076 gop.value_ptr.* == .none or
1077 (this_align != .none and this_align.compare(.gt, gop.value_ptr.*)))
1078 {
1079 gop.value_ptr.* = this_align;
1080 }
1081 if (uav.offset == 0) {
1079 try cg.addInst(.{1082 try cg.addInst(.{
1080 .tag = .uav_ref,1083 .tag = .uav_ref,
1081 .data = if (is_obj) .{1084 .data = .{ .ip_index = uav.ip_index },
1082 .uav_obj = try wasm.refUavObj(uav.ip_index, uav.orig_ptr_ty),
1083 } else .{
1084 .uav_exe = try wasm.refUavExe(uav.ip_index, uav.orig_ptr_ty),
1085 },
1086 });1085 });
1087 } else {1086 } else {
1088 try cg.addInst(.{1087 try cg.addInst(.{
1089 .tag = .uav_ref_off,1088 .tag = .uav_ref_off,
1090 .data = .{1089 .data = .{ .payload = try cg.addExtra(@as(Mir.UavRefOff, .{
1091 .payload = if (is_obj) try cg.addExtra(Mir.UavRefOffObj{1090 .value = uav.ip_index,
1092 .uav_obj = try wasm.refUavObj(uav.ip_index, uav.orig_ptr_ty),1091 .offset = uav.offset,
1093 .offset = uav.offset,1092 })) },
1094 }) else try cg.addExtra(Mir.UavRefOffExe{
1095 .uav_exe = try wasm.refUavExe(uav.ip_index, uav.orig_ptr_ty),
1096 .offset = uav.offset,
1097 }),
1098 },
1099 });1093 });
1100 }1094 }
1101 },1095 },
...@@ -1157,106 +1151,12 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {...@@ -1157,106 +1151,12 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
1157/// to use a zero-initialized local.1151/// to use a zero-initialized local.
1158fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {1152fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
1159 const zcu = cg.pt.zcu;1153 const zcu = cg.pt.zcu;
1160 try cg.locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target));1154 try cg.mir_locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target));
1161 const initial_index = cg.local_index;1155 const initial_index = cg.local_index;
1162 cg.local_index += 1;1156 cg.local_index += 1;
1163 return .{ .local = .{ .value = initial_index, .references = 1 } };1157 return .{ .local = .{ .value = initial_index, .references = 1 } };
1164}1158}
11651159
1166pub const Function = extern struct {
1167 /// Index into `Wasm.mir_instructions`.
1168 mir_off: u32,
1169 /// This is unused except for as a safety slice bound and could be removed.
1170 mir_len: u32,
1171 /// Index into `Wasm.mir_extra`.
1172 mir_extra_off: u32,
1173 /// This is unused except for as a safety slice bound and could be removed.
1174 mir_extra_len: u32,
1175 locals_off: u32,
1176 locals_len: u32,
1177 prologue: Prologue,
1178
1179 pub const Prologue = extern struct {
1180 flags: Flags,
1181 sp_local: u32,
1182 stack_size: u32,
1183 bottom_stack_local: u32,
1184
1185 pub const Flags = packed struct(u32) {
1186 stack_alignment: Alignment,
1187 padding: u26 = 0,
1188 };
1189
1190 pub const none: Prologue = .{
1191 .sp_local = 0,
1192 .flags = .{ .stack_alignment = .none },
1193 .stack_size = 0,
1194 .bottom_stack_local = 0,
1195 };
1196
1197 pub fn isNone(p: *const Prologue) bool {
1198 return p.flags.stack_alignment != .none;
1199 }
1200 };
1201
1202 pub fn lower(f: *Function, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) Allocator.Error!void {
1203 const gpa = wasm.base.comp.gpa;
1204
1205 // Write the locals in the prologue of the function body.
1206 const locals = wasm.all_zcu_locals.items[f.locals_off..][0..f.locals_len];
1207 try code.ensureUnusedCapacity(gpa, 5 + locals.len * 6 + 38);
1208
1209 std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(locals.len))) catch unreachable;
1210 for (locals) |local| {
1211 std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable;
1212 code.appendAssumeCapacity(@intFromEnum(local));
1213 }
1214
1215 // Stack management section of function prologue.
1216 const stack_alignment = f.prologue.flags.stack_alignment;
1217 if (stack_alignment.toByteUnits()) |align_bytes| {
1218 const sp_global: Wasm.GlobalIndex = .stack_pointer;
1219 // load stack pointer
1220 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get));
1221 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
1222 // store stack pointer so we can restore it when we return from the function
1223 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));
1224 leb.writeUleb128(code.fixedWriter(), f.prologue.sp_local) catch unreachable;
1225 // get the total stack size
1226 const aligned_stack: i32 = @intCast(stack_alignment.forward(f.prologue.stack_size));
1227 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1228 leb.writeIleb128(code.fixedWriter(), aligned_stack) catch unreachable;
1229 // subtract it from the current stack pointer
1230 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_sub));
1231 // Get negative stack alignment
1232 const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1;
1233 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1234 leb.writeIleb128(code.fixedWriter(), neg_stack_align) catch unreachable;
1235 // Bitwise-and the value to get the new stack pointer to ensure the
1236 // pointers are aligned with the abi alignment.
1237 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_and));
1238 // The bottom will be used to calculate all stack pointer offsets.
1239 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));
1240 leb.writeUleb128(code.fixedWriter(), f.prologue.bottom_stack_local) catch unreachable;
1241 // Store the current stack pointer value into the global stack pointer so other function calls will
1242 // start from this value instead and not overwrite the current stack.
1243 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
1244 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
1245 }
1246
1247 var emit: Emit = .{
1248 .mir = .{
1249 .instruction_tags = wasm.mir_instructions.items(.tag)[f.mir_off..][0..f.mir_len],
1250 .instruction_datas = wasm.mir_instructions.items(.data)[f.mir_off..][0..f.mir_len],
1251 .extra = wasm.mir_extra.items[f.mir_extra_off..][0..f.mir_extra_len],
1252 },
1253 .wasm = wasm,
1254 .code = code,
1255 };
1256 try emit.lowerToCode();
1257 }
1258};
1259
1260pub const Error = error{1160pub const Error = error{
1261 OutOfMemory,1161 OutOfMemory,
1262 /// Compiler was asked to operate on a number larger than supported.1162 /// Compiler was asked to operate on a number larger than supported.
...@@ -1265,13 +1165,16 @@ pub const Error = error{...@@ -1265,13 +1165,16 @@ pub const Error = error{
1265 CodegenFail,1165 CodegenFail,
1266};1166};
12671167
1268pub fn function(1168pub fn generate(
1269 wasm: *Wasm,1169 bin_file: *link.File,
1270 pt: Zcu.PerThread,1170 pt: Zcu.PerThread,
1171 src_loc: Zcu.LazySrcLoc,
1271 func_index: InternPool.Index,1172 func_index: InternPool.Index,
1272 air: Air,1173 air: *const Air,
1273 liveness: Air.Liveness,1174 liveness: *const Air.Liveness,
1274) Error!Function {1175) Error!Mir {
1176 _ = src_loc;
1177 _ = bin_file;
1275 const zcu = pt.zcu;1178 const zcu = pt.zcu;
1276 const gpa = zcu.gpa;1179 const gpa = zcu.gpa;
1277 const cg = zcu.funcInfo(func_index);1180 const cg = zcu.funcInfo(func_index);
...@@ -1279,10 +1182,8 @@ pub fn function(...@@ -1279,10 +1182,8 @@ pub fn function(
1279 const target = &file_scope.mod.?.resolved_target.result;1182 const target = &file_scope.mod.?.resolved_target.result;
1280 const fn_ty = zcu.navValue(cg.owner_nav).typeOf(zcu);1183 const fn_ty = zcu.navValue(cg.owner_nav).typeOf(zcu);
1281 const fn_info = zcu.typeToFunc(fn_ty).?;1184 const fn_info = zcu.typeToFunc(fn_ty).?;
1282 const ip = &zcu.intern_pool;1185 const ret_ty: Type = .fromInterned(fn_info.return_type);
1283 const fn_ty_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target);1186 const any_returns = !firstParamSRet(fn_info.cc, ret_ty, zcu, target) and ret_ty.hasRuntimeBitsIgnoreComptime(zcu);
1284 const returns = fn_ty_index.ptr(wasm).returns.slice(wasm);
1285 const any_returns = returns.len != 0;
12861187
1287 var cc_result = try resolveCallingConventionValues(zcu, fn_ty, target);1188 var cc_result = try resolveCallingConventionValues(zcu, fn_ty, target);
1288 defer cc_result.deinit(gpa);1189 defer cc_result.deinit(gpa);
...@@ -1290,8 +1191,8 @@ pub fn function(...@@ -1290,8 +1191,8 @@ pub fn function(
1290 var code_gen: CodeGen = .{1191 var code_gen: CodeGen = .{
1291 .gpa = gpa,1192 .gpa = gpa,
1292 .pt = pt,1193 .pt = pt,
1293 .air = air,1194 .air = air.*,
1294 .liveness = liveness,1195 .liveness = liveness.*,
1295 .owner_nav = cg.owner_nav,1196 .owner_nav = cg.owner_nav,
1296 .target = target,1197 .target = target,
1297 .ptr_size = switch (target.cpu.arch) {1198 .ptr_size = switch (target.cpu.arch) {
...@@ -1299,31 +1200,33 @@ pub fn function(...@@ -1299,31 +1200,33 @@ pub fn function(
1299 .wasm64 => .wasm64,1200 .wasm64 => .wasm64,
1300 else => unreachable,1201 else => unreachable,
1301 },1202 },
1302 .wasm = wasm,
1303 .func_index = func_index,1203 .func_index = func_index,
1304 .args = cc_result.args,1204 .args = cc_result.args,
1305 .return_value = cc_result.return_value,1205 .return_value = cc_result.return_value,
1306 .local_index = cc_result.local_index,1206 .local_index = cc_result.local_index,
1307 .mir_instructions = &wasm.mir_instructions,1207 .mir_instructions = .empty,
1308 .mir_extra = &wasm.mir_extra,1208 .mir_extra = .empty,
1309 .locals = &wasm.all_zcu_locals,1209 .mir_locals = .empty,
1310 .start_mir_extra_off = @intCast(wasm.mir_extra.items.len),1210 .mir_uavs = .empty,
1311 .start_locals_off = @intCast(wasm.all_zcu_locals.items.len),1211 .mir_indirect_function_set = .empty,
1212 .mir_func_tys = .empty,
1213 .error_name_table_ref_count = 0,
1312 };1214 };
1313 defer code_gen.deinit();1215 defer code_gen.deinit();
13141216
1315 return functionInner(&code_gen, any_returns) catch |err| switch (err) {1217 try code_gen.mir_func_tys.putNoClobber(gpa, fn_ty.toIntern(), {});
1316 error.CodegenFail => return error.CodegenFail,1218
1219 return generateInner(&code_gen, any_returns) catch |err| switch (err) {
1220 error.CodegenFail,
1221 error.OutOfMemory,
1222 error.Overflow,
1223 => |e| return e,
1317 else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}),1224 else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}),
1318 };1225 };
1319}1226}
13201227
1321fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function {1228fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir {
1322 const wasm = cg.wasm;
1323 const zcu = cg.pt.zcu;1229 const zcu = cg.pt.zcu;
1324
1325 const start_mir_off: u32 = @intCast(wasm.mir_instructions.len);
1326
1327 try cg.branches.append(cg.gpa, .{});1230 try cg.branches.append(cg.gpa, .{});
1328 // clean up outer branch1231 // clean up outer branch
1329 defer {1232 defer {
...@@ -1347,20 +1250,25 @@ fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function {...@@ -1347,20 +1250,25 @@ fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function {
1347 try cg.addTag(.end);1250 try cg.addTag(.end);
1348 try cg.addTag(.dbg_epilogue_begin);1251 try cg.addTag(.dbg_epilogue_begin);
13491252
1350 return .{1253 var mir: Mir = .{
1351 .mir_off = start_mir_off,1254 .instructions = cg.mir_instructions.toOwnedSlice(),
1352 .mir_len = @intCast(wasm.mir_instructions.len - start_mir_off),1255 .extra = &.{}, // fallible so assigned after errdefer
1353 .mir_extra_off = cg.start_mir_extra_off,1256 .locals = &.{}, // fallible so assigned after errdefer
1354 .mir_extra_len = cg.extraLen(),
1355 .locals_off = cg.start_locals_off,
1356 .locals_len = @intCast(wasm.all_zcu_locals.items.len - cg.start_locals_off),
1357 .prologue = if (cg.initial_stack_value == .none) .none else .{1257 .prologue = if (cg.initial_stack_value == .none) .none else .{
1358 .sp_local = cg.initial_stack_value.local.value,1258 .sp_local = cg.initial_stack_value.local.value,
1359 .flags = .{ .stack_alignment = cg.stack_alignment },1259 .flags = .{ .stack_alignment = cg.stack_alignment },
1360 .stack_size = cg.stack_size,1260 .stack_size = cg.stack_size,
1361 .bottom_stack_local = cg.bottom_stack_value.local.value,1261 .bottom_stack_local = cg.bottom_stack_value.local.value,
1362 },1262 },
1263 .uavs = cg.mir_uavs.move(),
1264 .indirect_function_set = cg.mir_indirect_function_set.move(),
1265 .func_tys = cg.mir_func_tys.move(),
1266 .error_name_table_ref_count = cg.error_name_table_ref_count,
1363 };1267 };
1268 errdefer mir.deinit(cg.gpa);
1269 mir.extra = try cg.mir_extra.toOwnedSlice(cg.gpa);
1270 mir.locals = try cg.mir_locals.toOwnedSlice(cg.gpa);
1271 return mir;
1364}1272}
13651273
1366const CallWValues = struct {1274const CallWValues = struct {
...@@ -2220,7 +2128,6 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2220,7 +2128,6 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2220}2128}
22212129
2222fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {2130fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {
2223 const wasm = cg.wasm;
2224 if (modifier == .always_tail) return cg.fail("TODO implement tail calls for wasm", .{});2131 if (modifier == .always_tail) return cg.fail("TODO implement tail calls for wasm", .{});
2225 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;2132 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
2226 const extra = cg.air.extraData(Air.Call, pl_op.payload);2133 const extra = cg.air.extraData(Air.Call, pl_op.payload);
...@@ -2277,8 +2184,11 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie...@@ -2277,8 +2184,11 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie
2277 const operand = try cg.resolveInst(pl_op.operand);2184 const operand = try cg.resolveInst(pl_op.operand);
2278 try cg.emitWValue(operand);2185 try cg.emitWValue(operand);
22792186
2280 const fn_type_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), cg.target);2187 try cg.mir_func_tys.put(cg.gpa, fn_ty.toIntern(), {});
2281 try cg.addFuncTy(.call_indirect, fn_type_index);2188 try cg.addInst(.{
2189 .tag = .call_indirect,
2190 .data = .{ .ip_index = fn_ty.toIntern() },
2191 });
2282 }2192 }
22832193
2284 const result_value = result_value: {2194 const result_value = result_value: {
...@@ -2449,7 +2359,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr...@@ -2449,7 +2359,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr
2449 try cg.emitWValue(lhs);2359 try cg.emitWValue(lhs);
2450 try cg.lowerToStack(rhs);2360 try cg.lowerToStack(rhs);
2451 // TODO: Add helper functions for simd opcodes2361 // TODO: Add helper functions for simd opcodes
2452 const extra_index = cg.extraLen();2362 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
2453 // stores as := opcode, offset, alignment (opcode::memarg)2363 // stores as := opcode, offset, alignment (opcode::memarg)
2454 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{2364 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
2455 @intFromEnum(std.wasm.SimdOpcode.v128_store),2365 @intFromEnum(std.wasm.SimdOpcode.v128_store),
...@@ -2574,7 +2484,7 @@ fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue...@@ -2574,7 +2484,7 @@ fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue
25742484
2575 if (ty.zigTypeTag(zcu) == .vector) {2485 if (ty.zigTypeTag(zcu) == .vector) {
2576 // TODO: Add helper functions for simd opcodes2486 // TODO: Add helper functions for simd opcodes
2577 const extra_index = cg.extraLen();2487 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
2578 // stores as := opcode, offset, alignment (opcode::memarg)2488 // stores as := opcode, offset, alignment (opcode::memarg)
2579 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{2489 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
2580 @intFromEnum(std.wasm.SimdOpcode.v128_load),2490 @intFromEnum(std.wasm.SimdOpcode.v128_load),
...@@ -4971,7 +4881,7 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4971,7 +4881,7 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
49714881
4972 try cg.emitWValue(array);4882 try cg.emitWValue(array);
49734883
4974 const extra_index = cg.extraLen();4884 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
4975 try cg.mir_extra.appendSlice(cg.gpa, &operands);4885 try cg.mir_extra.appendSlice(cg.gpa, &operands);
4976 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });4886 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
49774887
...@@ -5123,7 +5033,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5123,7 +5033,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5123 else => break :blk, // Cannot make use of simd-instructions5033 else => break :blk, // Cannot make use of simd-instructions
5124 };5034 };
5125 try cg.emitWValue(operand);5035 try cg.emitWValue(operand);
5126 const extra_index: u32 = cg.extraLen();5036 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
5127 // stores as := opcode, offset, alignment (opcode::memarg)5037 // stores as := opcode, offset, alignment (opcode::memarg)
5128 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{5038 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
5129 opcode,5039 opcode,
...@@ -5142,7 +5052,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5142,7 +5052,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5142 else => break :blk, // Cannot make use of simd-instructions5052 else => break :blk, // Cannot make use of simd-instructions
5143 };5053 };
5144 try cg.emitWValue(operand);5054 try cg.emitWValue(operand);
5145 const extra_index = cg.extraLen();5055 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
5146 try cg.mir_extra.append(cg.gpa, opcode);5056 try cg.mir_extra.append(cg.gpa, opcode);
5147 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });5057 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
5148 return cg.finishAir(inst, .stack, &.{ty_op.operand});5058 return cg.finishAir(inst, .stack, &.{ty_op.operand});
...@@ -5246,7 +5156,7 @@ fn airShuffleTwo(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5246,7 +5156,7 @@ fn airShuffleTwo(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5246 }5156 }
5247 try cg.emitWValue(operand_a);5157 try cg.emitWValue(operand_a);
5248 try cg.emitWValue(operand_b);5158 try cg.emitWValue(operand_b);
5249 const extra_index = cg.extraLen();5159 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
5250 try cg.mir_extra.appendSlice(cg.gpa, &.{5160 try cg.mir_extra.appendSlice(cg.gpa, &.{
5251 @intFromEnum(std.wasm.SimdOpcode.i8x16_shuffle),5161 @intFromEnum(std.wasm.SimdOpcode.i8x16_shuffle),
5252 @bitCast(lane_map[0..4].*),5162 @bitCast(lane_map[0..4].*),
...@@ -6016,9 +5926,8 @@ fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6016,9 +5926,8 @@ fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6016 const name_ty = Type.slice_const_u8_sentinel_0;5926 const name_ty = Type.slice_const_u8_sentinel_0;
6017 const abi_size = name_ty.abiSize(pt.zcu);5927 const abi_size = name_ty.abiSize(pt.zcu);
60185928
6019 cg.wasm.error_name_table_ref_count += 1;
6020
6021 // Lowers to a i32.const or i64.const with the error table memory address.5929 // Lowers to a i32.const or i64.const with the error table memory address.
5930 cg.error_name_table_ref_count += 1;
6022 try cg.addTag(.error_name_table_ref);5931 try cg.addTag(.error_name_table_ref);
6023 try cg.emitWValue(operand);5932 try cg.emitWValue(operand);
6024 switch (cg.ptr_size) {5933 switch (cg.ptr_size) {
...@@ -6046,7 +5955,7 @@ fn airPtrSliceFieldPtr(cg: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerErr...@@ -6046,7 +5955,7 @@ fn airPtrSliceFieldPtr(cg: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerErr
60465955
6047/// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits5956/// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits
6048fn intZeroValue(cg: *CodeGen, ty: Type) InnerError!WValue {5957fn intZeroValue(cg: *CodeGen, ty: Type) InnerError!WValue {
6049 const zcu = cg.wasm.base.comp.zcu.?;5958 const zcu = cg.pt.zcu;
6050 const int_info = ty.intInfo(zcu);5959 const int_info = ty.intInfo(zcu);
6051 const wasm_bits = toWasmBits(int_info.bits) orelse {5960 const wasm_bits = toWasmBits(int_info.bits) orelse {
6052 return cg.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits});5961 return cg.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits});
...@@ -7673,7 +7582,3 @@ fn floatCmpIntrinsic(op: std.math.CompareOperator, bits: u16) Mir.Intrinsic {...@@ -7673,7 +7582,3 @@ fn floatCmpIntrinsic(op: std.math.CompareOperator, bits: u16) Mir.Intrinsic {
7673 },7582 },
7674 };7583 };
7675}7584}
7676
7677fn extraLen(cg: *const CodeGen) u32 {
7678 return @intCast(cg.mir_extra.items.len - cg.start_mir_extra_off);
7679}
src/arch/wasm/Emit.zig+25-14
...@@ -31,8 +31,8 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -31,8 +31,8 @@ pub fn lowerToCode(emit: *Emit) Error!void {
31 const target = &comp.root_mod.resolved_target.result;31 const target = &comp.root_mod.resolved_target.result;
32 const is_wasm32 = target.cpu.arch == .wasm32;32 const is_wasm32 = target.cpu.arch == .wasm32;
3333
34 const tags = mir.instruction_tags;34 const tags = mir.instructions.items(.tag);
35 const datas = mir.instruction_datas;35 const datas = mir.instructions.items(.data);
36 var inst: u32 = 0;36 var inst: u32 = 0;
3737
38 loop: switch (tags[inst]) {38 loop: switch (tags[inst]) {
...@@ -50,18 +50,19 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -50,18 +50,19 @@ pub fn lowerToCode(emit: *Emit) Error!void {
50 },50 },
51 .uav_ref => {51 .uav_ref => {
52 if (is_obj) {52 if (is_obj) {
53 try uavRefOffObj(wasm, code, .{ .uav_obj = datas[inst].uav_obj, .offset = 0 }, is_wasm32);53 try uavRefObj(wasm, code, datas[inst].ip_index, 0, is_wasm32);
54 } else {54 } else {
55 try uavRefOffExe(wasm, code, .{ .uav_exe = datas[inst].uav_exe, .offset = 0 }, is_wasm32);55 try uavRefExe(wasm, code, datas[inst].ip_index, 0, is_wasm32);
56 }56 }
57 inst += 1;57 inst += 1;
58 continue :loop tags[inst];58 continue :loop tags[inst];
59 },59 },
60 .uav_ref_off => {60 .uav_ref_off => {
61 const extra = mir.extraData(Mir.UavRefOff, datas[inst].payload).data;
61 if (is_obj) {62 if (is_obj) {
62 try uavRefOffObj(wasm, code, mir.extraData(Mir.UavRefOffObj, datas[inst].payload).data, is_wasm32);63 try uavRefObj(wasm, code, extra.value, extra.offset, is_wasm32);
63 } else {64 } else {
64 try uavRefOffExe(wasm, code, mir.extraData(Mir.UavRefOffExe, datas[inst].payload).data, is_wasm32);65 try uavRefExe(wasm, code, extra.value, extra.offset, is_wasm32);
65 }66 }
66 inst += 1;67 inst += 1;
67 continue :loop tags[inst];68 continue :loop tags[inst];
...@@ -77,11 +78,14 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -77,11 +78,14 @@ pub fn lowerToCode(emit: *Emit) Error!void {
77 continue :loop tags[inst];78 continue :loop tags[inst];
78 },79 },
79 .func_ref => {80 .func_ref => {
81 const indirect_func_idx: Wasm.ZcuIndirectFunctionSetIndex = @enumFromInt(
82 wasm.zcu_indirect_function_set.getIndex(datas[inst].nav_index).?,
83 );
80 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));84 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
81 if (is_obj) {85 if (is_obj) {
82 @panic("TODO");86 @panic("TODO");
83 } else {87 } else {
84 leb.writeUleb128(code.fixedWriter(), 1 + @intFromEnum(datas[inst].indirect_function_table_index)) catch unreachable;88 leb.writeUleb128(code.fixedWriter(), 1 + @intFromEnum(indirect_func_idx)) catch unreachable;
85 }89 }
86 inst += 1;90 inst += 1;
87 continue :loop tags[inst];91 continue :loop tags[inst];
...@@ -101,6 +105,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -101,6 +105,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
101 continue :loop tags[inst];105 continue :loop tags[inst];
102 },106 },
103 .error_name_table_ref => {107 .error_name_table_ref => {
108 wasm.error_name_table_ref_count += 1;
104 try code.ensureUnusedCapacity(gpa, 11);109 try code.ensureUnusedCapacity(gpa, 11);
105 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;110 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
106 code.appendAssumeCapacity(@intFromEnum(opcode));111 code.appendAssumeCapacity(@intFromEnum(opcode));
...@@ -176,7 +181,13 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -176,7 +181,13 @@ pub fn lowerToCode(emit: *Emit) Error!void {
176181
177 .call_indirect => {182 .call_indirect => {
178 try code.ensureUnusedCapacity(gpa, 11);183 try code.ensureUnusedCapacity(gpa, 11);
179 const func_ty_index = datas[inst].func_ty;184 const fn_info = comp.zcu.?.typeToFunc(.fromInterned(datas[inst].ip_index)).?;
185 const func_ty_index = wasm.getExistingFunctionType(
186 fn_info.cc,
187 fn_info.param_types.get(&comp.zcu.?.intern_pool),
188 .fromInterned(fn_info.return_type),
189 target,
190 ).?;
180 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect));191 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect));
181 if (is_obj) {192 if (is_obj) {
182 try wasm.out_relocs.append(gpa, .{193 try wasm.out_relocs.append(gpa, .{
...@@ -912,7 +923,7 @@ fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {...@@ -912,7 +923,7 @@ fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {
912 leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable;923 leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable;
913}924}
914925
915fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffObj, is_wasm32: bool) !void {926fn uavRefObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), value: InternPool.Index, offset: i32, is_wasm32: bool) !void {
916 const comp = wasm.base.comp;927 const comp = wasm.base.comp;
917 const gpa = comp.gpa;928 const gpa = comp.gpa;
918 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;929 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
...@@ -922,14 +933,14 @@ fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef...@@ -922,14 +933,14 @@ fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef
922933
923 try wasm.out_relocs.append(gpa, .{934 try wasm.out_relocs.append(gpa, .{
924 .offset = @intCast(code.items.len),935 .offset = @intCast(code.items.len),
925 .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(data.uav_obj.key(wasm).*) },936 .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(value) },
926 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,937 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
927 .addend = data.offset,938 .addend = offset,
928 });939 });
929 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);940 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
930}941}
931942
932fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffExe, is_wasm32: bool) !void {943fn uavRefExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), value: InternPool.Index, offset: i32, is_wasm32: bool) !void {
933 const comp = wasm.base.comp;944 const comp = wasm.base.comp;
934 const gpa = comp.gpa;945 const gpa = comp.gpa;
935 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;946 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
...@@ -937,8 +948,8 @@ fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef...@@ -937,8 +948,8 @@ fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef
937 try code.ensureUnusedCapacity(gpa, 11);948 try code.ensureUnusedCapacity(gpa, 11);
938 code.appendAssumeCapacity(@intFromEnum(opcode));949 code.appendAssumeCapacity(@intFromEnum(opcode));
939950
940 const addr = wasm.uavAddr(data.uav_exe);951 const addr = wasm.uavAddr(value);
941 leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable;952 leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + offset))) catch unreachable;
942}953}
943954
944fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff, is_wasm32: bool) !void {955fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff, is_wasm32: bool) !void {
src/arch/wasm/Mir.zig+104-19
...@@ -9,16 +9,53 @@...@@ -9,16 +9,53 @@
9const Mir = @This();9const Mir = @This();
10const InternPool = @import("../../InternPool.zig");10const InternPool = @import("../../InternPool.zig");
11const Wasm = @import("../../link/Wasm.zig");11const Wasm = @import("../../link/Wasm.zig");
12const Emit = @import("Emit.zig");
13const Alignment = InternPool.Alignment;
1214
13const builtin = @import("builtin");15const builtin = @import("builtin");
14const std = @import("std");16const std = @import("std");
15const assert = std.debug.assert;17const assert = std.debug.assert;
18const leb = std.leb;
1619
17instruction_tags: []const Inst.Tag,20instructions: std.MultiArrayList(Inst).Slice,
18instruction_datas: []const Inst.Data,
19/// A slice of indexes where the meaning of the data is determined by the21/// A slice of indexes where the meaning of the data is determined by the
20/// `Inst.Tag` value.22/// `Inst.Tag` value.
21extra: []const u32,23extra: []const u32,
24locals: []const std.wasm.Valtype,
25prologue: Prologue,
26
27/// Not directly used by `Emit`, but the linker needs this to merge it with a global set.
28/// Value is the explicit alignment if greater than natural alignment, `.none` otherwise.
29uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
30/// Not directly used by `Emit`, but the linker needs this to merge it with a global set.
31indirect_function_set: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void),
32/// Not directly used by `Emit`, but the linker needs this to ensure these types are interned.
33func_tys: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
34/// Not directly used by `Emit`, but the linker needs this to add it to its own refcount.
35error_name_table_ref_count: u32,
36
37pub const Prologue = extern struct {
38 flags: Flags,
39 sp_local: u32,
40 stack_size: u32,
41 bottom_stack_local: u32,
42
43 pub const Flags = packed struct(u32) {
44 stack_alignment: Alignment,
45 padding: u26 = 0,
46 };
47
48 pub const none: Prologue = .{
49 .sp_local = 0,
50 .flags = .{ .stack_alignment = .none },
51 .stack_size = 0,
52 .bottom_stack_local = 0,
53 };
54
55 pub fn isNone(p: *const Prologue) bool {
56 return p.flags.stack_alignment != .none;
57 }
58};
2259
23pub const Inst = struct {60pub const Inst = struct {
24 /// The opcode that represents this instruction61 /// The opcode that represents this instruction
...@@ -80,7 +117,7 @@ pub const Inst = struct {...@@ -80,7 +117,7 @@ pub const Inst = struct {
80 /// Lowers to an i32_const which is the index of the function in the117 /// Lowers to an i32_const which is the index of the function in the
81 /// table section.118 /// table section.
82 ///119 ///
83 /// Uses `indirect_function_table_index`.120 /// Uses `nav_index`.
84 func_ref,121 func_ref,
85 /// Inserts debug information about the current line and column122 /// Inserts debug information about the current line and column
86 /// of the source code123 /// of the source code
...@@ -123,7 +160,7 @@ pub const Inst = struct {...@@ -123,7 +160,7 @@ pub const Inst = struct {
123 /// Calls a function pointer by its function signature160 /// Calls a function pointer by its function signature
124 /// and index into the function table.161 /// and index into the function table.
125 ///162 ///
126 /// Uses `func_ty`163 /// Uses `ip_index`; the `InternPool.Index` is the function type.
127 call_indirect,164 call_indirect,
128 /// Calls a function by its index.165 /// Calls a function by its index.
129 ///166 ///
...@@ -611,11 +648,7 @@ pub const Inst = struct {...@@ -611,11 +648,7 @@ pub const Inst = struct {
611648
612 ip_index: InternPool.Index,649 ip_index: InternPool.Index,
613 nav_index: InternPool.Nav.Index,650 nav_index: InternPool.Nav.Index,
614 func_ty: Wasm.FunctionType.Index,
615 intrinsic: Intrinsic,651 intrinsic: Intrinsic,
616 uav_obj: Wasm.UavsObjIndex,
617 uav_exe: Wasm.UavsExeIndex,
618 indirect_function_table_index: Wasm.ZcuIndirectFunctionSetIndex,
619652
620 comptime {653 comptime {
621 switch (builtin.mode) {654 switch (builtin.mode) {
...@@ -626,10 +659,66 @@ pub const Inst = struct {...@@ -626,10 +659,66 @@ pub const Inst = struct {
626 };659 };
627};660};
628661
629pub fn deinit(self: *Mir, gpa: std.mem.Allocator) void {662pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
630 self.instructions.deinit(gpa);663 mir.instructions.deinit(gpa);
631 gpa.free(self.extra);664 gpa.free(mir.extra);
632 self.* = undefined;665 gpa.free(mir.locals);
666 mir.uavs.deinit(gpa);
667 mir.indirect_function_set.deinit(gpa);
668 mir.func_tys.deinit(gpa);
669 mir.* = undefined;
670}
671
672pub fn lower(mir: *const Mir, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) std.mem.Allocator.Error!void {
673 const gpa = wasm.base.comp.gpa;
674
675 // Write the locals in the prologue of the function body.
676 try code.ensureUnusedCapacity(gpa, 5 + mir.locals.len * 6 + 38);
677
678 std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(mir.locals.len))) catch unreachable;
679 for (mir.locals) |local| {
680 std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable;
681 code.appendAssumeCapacity(@intFromEnum(local));
682 }
683
684 // Stack management section of function prologue.
685 const stack_alignment = mir.prologue.flags.stack_alignment;
686 if (stack_alignment.toByteUnits()) |align_bytes| {
687 const sp_global: Wasm.GlobalIndex = .stack_pointer;
688 // load stack pointer
689 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get));
690 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
691 // store stack pointer so we can restore it when we return from the function
692 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));
693 leb.writeUleb128(code.fixedWriter(), mir.prologue.sp_local) catch unreachable;
694 // get the total stack size
695 const aligned_stack: i32 = @intCast(stack_alignment.forward(mir.prologue.stack_size));
696 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
697 leb.writeIleb128(code.fixedWriter(), aligned_stack) catch unreachable;
698 // subtract it from the current stack pointer
699 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_sub));
700 // Get negative stack alignment
701 const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1;
702 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
703 leb.writeIleb128(code.fixedWriter(), neg_stack_align) catch unreachable;
704 // Bitwise-and the value to get the new stack pointer to ensure the
705 // pointers are aligned with the abi alignment.
706 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_and));
707 // The bottom will be used to calculate all stack pointer offsets.
708 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));
709 leb.writeUleb128(code.fixedWriter(), mir.prologue.bottom_stack_local) catch unreachable;
710 // Store the current stack pointer value into the global stack pointer so other function calls will
711 // start from this value instead and not overwrite the current stack.
712 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
713 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
714 }
715
716 var emit: Emit = .{
717 .mir = mir.*,
718 .wasm = wasm,
719 .code = code,
720 };
721 try emit.lowerToCode();
633}722}
634723
635pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data: T, end: usize } {724pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
...@@ -643,6 +732,7 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data...@@ -643,6 +732,7 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data
643 Wasm.UavsObjIndex,732 Wasm.UavsObjIndex,
644 Wasm.UavsExeIndex,733 Wasm.UavsExeIndex,
645 InternPool.Nav.Index,734 InternPool.Nav.Index,
735 InternPool.Index,
646 => @enumFromInt(self.extra[i]),736 => @enumFromInt(self.extra[i]),
647 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),737 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
648 };738 };
...@@ -695,13 +785,8 @@ pub const MemArg = struct {...@@ -695,13 +785,8 @@ pub const MemArg = struct {
695 alignment: u32,785 alignment: u32,
696};786};
697787
698pub const UavRefOffObj = struct {788pub const UavRefOff = struct {
699 uav_obj: Wasm.UavsObjIndex,789 value: InternPool.Index,
700 offset: i32,
701};
702
703pub const UavRefOffExe = struct {
704 uav_exe: Wasm.UavsExeIndex,
705 offset: i32,790 offset: i32,
706};791};
707792
src/codegen.zig+3-16
...@@ -123,6 +123,7 @@ pub const AnyMir = union {...@@ -123,6 +123,7 @@ pub const AnyMir = union {
123 .stage2_riscv64,123 .stage2_riscv64,
124 .stage2_sparc64,124 .stage2_sparc64,
125 .stage2_x86_64,125 .stage2_x86_64,
126 .stage2_wasm,
126 .stage2_c,127 .stage2_c,
127 => |backend_ct| @field(mir, tag(backend_ct)).deinit(gpa),128 => |backend_ct| @field(mir, tag(backend_ct)).deinit(gpa),
128 }129 }
...@@ -153,6 +154,7 @@ pub fn generateFunction(...@@ -153,6 +154,7 @@ pub fn generateFunction(
153 .stage2_riscv64,154 .stage2_riscv64,
154 .stage2_sparc64,155 .stage2_sparc64,
155 .stage2_x86_64,156 .stage2_x86_64,
157 .stage2_wasm,
156 .stage2_c,158 .stage2_c,
157 => |backend| {159 => |backend| {
158 dev.check(devFeatureForBackend(backend));160 dev.check(devFeatureForBackend(backend));
...@@ -784,7 +786,6 @@ fn lowerUavRef(...@@ -784,7 +786,6 @@ fn lowerUavRef(
784 const comp = lf.comp;786 const comp = lf.comp;
785 const target = &comp.root_mod.resolved_target.result;787 const target = &comp.root_mod.resolved_target.result;
786 const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8);788 const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8);
787 const is_obj = comp.config.output_mode == .Obj;
788 const uav_val = uav.val;789 const uav_val = uav.val;
789 const uav_ty = Type.fromInterned(ip.typeOf(uav_val));790 const uav_ty = Type.fromInterned(ip.typeOf(uav_val));
790 const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn";791 const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn";
...@@ -804,21 +805,7 @@ fn lowerUavRef(...@@ -804,21 +805,7 @@ fn lowerUavRef(
804 dev.check(link.File.Tag.wasm.devFeature());805 dev.check(link.File.Tag.wasm.devFeature());
805 const wasm = lf.cast(.wasm).?;806 const wasm = lf.cast(.wasm).?;
806 assert(reloc_parent == .none);807 assert(reloc_parent == .none);
807 if (is_obj) {808 try wasm.addUavReloc(code.items.len, uav.val, uav.orig_ty, @intCast(offset));
808 try wasm.out_relocs.append(gpa, .{
809 .offset = @intCast(code.items.len),
810 .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(uav.val) },
811 .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64,
812 .addend = @intCast(offset),
813 });
814 } else {
815 try wasm.uav_fixups.ensureUnusedCapacity(gpa, 1);
816 wasm.uav_fixups.appendAssumeCapacity(.{
817 .uavs_exe_index = try wasm.refUavExe(uav.val, uav.orig_ty),
818 .offset = @intCast(code.items.len),
819 .addend = @intCast(offset),
820 });
821 }
822 code.appendNTimesAssumeCapacity(0, ptr_width_bytes);809 code.appendNTimesAssumeCapacity(0, ptr_width_bytes);
823 return;810 return;
824 },811 },
src/link.zig+1-2
...@@ -759,7 +759,6 @@ pub const File = struct {...@@ -759,7 +759,6 @@ pub const File = struct {
759 switch (base.tag) {759 switch (base.tag) {
760 .lld => unreachable,760 .lld => unreachable,
761 inline else => |tag| {761 inline else => |tag| {
762 if (tag == .wasm) @panic("MLUGG TODO");
763 if (tag == .spirv) @panic("MLUGG TODO");762 if (tag == .spirv) @panic("MLUGG TODO");
764 dev.check(tag.devFeature());763 dev.check(tag.devFeature());
765 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir, maybe_undef_air);764 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir, maybe_undef_air);
...@@ -1450,12 +1449,12 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {...@@ -1450,12 +1449,12 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
1450 const nav = zcu.funcInfo(func.func).owner_nav;1449 const nav = zcu.funcInfo(func.func).owner_nav;
1451 const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid));1450 const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid));
1452 defer pt.deactivate();1451 defer pt.deactivate();
1453 assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR
1454 switch (func.mir.status.load(.monotonic)) {1452 switch (func.mir.status.load(.monotonic)) {
1455 .pending => unreachable,1453 .pending => unreachable,
1456 .ready => {},1454 .ready => {},
1457 .failed => return,1455 .failed => return,
1458 }1456 }
1457 assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR
1459 const mir = &func.mir.value;1458 const mir = &func.mir.value;
1460 if (comp.bin_file) |lf| {1459 if (comp.bin_file) |lf| {
1461 lf.updateFunc(pt, func.func, mir, func.air) catch |err| switch (err) {1460 lf.updateFunc(pt, func.func, mir, func.air) catch |err| switch (err) {
src/link/Wasm.zig+134-59
...@@ -282,7 +282,7 @@ mir_instructions: std.MultiArrayList(Mir.Inst) = .{},...@@ -282,7 +282,7 @@ mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
282/// Corresponds to `mir_instructions`.282/// Corresponds to `mir_instructions`.
283mir_extra: std.ArrayListUnmanaged(u32) = .empty,283mir_extra: std.ArrayListUnmanaged(u32) = .empty,
284/// All local types for all Zcu functions.284/// All local types for all Zcu functions.
285all_zcu_locals: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,285mir_locals: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,
286286
287params_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,287params_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,
288returns_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,288returns_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,
...@@ -866,9 +866,24 @@ const ZcuDataStarts = struct {...@@ -866,9 +866,24 @@ const ZcuDataStarts = struct {
866};866};
867867
868pub const ZcuFunc = union {868pub const ZcuFunc = union {
869 function: CodeGen.Function,869 function: Function,
870 tag_name: TagName,870 tag_name: TagName,
871871
872 pub const Function = extern struct {
873 /// Index into `Wasm.mir_instructions`.
874 instructions_off: u32,
875 /// This is unused except for as a safety slice bound and could be removed.
876 instructions_len: u32,
877 /// Index into `Wasm.mir_extra`.
878 extra_off: u32,
879 /// This is unused except for as a safety slice bound and could be removed.
880 extra_len: u32,
881 /// Index into `Wasm.mir_locals`.
882 locals_off: u32,
883 locals_len: u32,
884 prologue: Mir.Prologue,
885 };
886
872 pub const TagName = extern struct {887 pub const TagName = extern struct {
873 symbol_name: String,888 symbol_name: String,
874 type_index: FunctionType.Index,889 type_index: FunctionType.Index,
...@@ -3107,7 +3122,7 @@ pub fn deinit(wasm: *Wasm) void {...@@ -3107,7 +3122,7 @@ pub fn deinit(wasm: *Wasm) void {
31073122
3108 wasm.mir_instructions.deinit(gpa);3123 wasm.mir_instructions.deinit(gpa);
3109 wasm.mir_extra.deinit(gpa);3124 wasm.mir_extra.deinit(gpa);
3110 wasm.all_zcu_locals.deinit(gpa);3125 wasm.mir_locals.deinit(gpa);
31113126
3112 if (wasm.dwarf) |*dwarf| dwarf.deinit();3127 if (wasm.dwarf) |*dwarf| dwarf.deinit();
31133128
...@@ -3167,33 +3182,96 @@ pub fn deinit(wasm: *Wasm) void {...@@ -3167,33 +3182,96 @@ pub fn deinit(wasm: *Wasm) void {
3167 wasm.missing_exports.deinit(gpa);3182 wasm.missing_exports.deinit(gpa);
3168}3183}
31693184
3170pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Air.Liveness) !void {3185pub fn updateFunc(
3186 wasm: *Wasm,
3187 pt: Zcu.PerThread,
3188 func_index: InternPool.Index,
3189 any_mir: *const codegen.AnyMir,
3190 maybe_undef_air: *const Air,
3191) !void {
3171 if (build_options.skip_non_native and builtin.object_format != .wasm) {3192 if (build_options.skip_non_native and builtin.object_format != .wasm) {
3172 @panic("Attempted to compile for object format that was disabled by build configuration");3193 @panic("Attempted to compile for object format that was disabled by build configuration");
3173 }3194 }
31743195
3175 dev.check(.wasm_backend);3196 dev.check(.wasm_backend);
3197 _ = maybe_undef_air; // we (correctly) do not need this
31763198
3199 // This linker implementation only works with codegen backend `.stage2_wasm`.
3200 const mir = &any_mir.wasm;
3177 const zcu = pt.zcu;3201 const zcu = pt.zcu;
3178 const gpa = zcu.gpa;3202 const gpa = zcu.gpa;
3179 try wasm.functions.ensureUnusedCapacity(gpa, 1);
3180 try wasm.zcu_funcs.ensureUnusedCapacity(gpa, 1);
3181
3182 const ip = &zcu.intern_pool;3203 const ip = &zcu.intern_pool;
3204 const is_obj = zcu.comp.config.output_mode == .Obj;
3205 const target = &zcu.comp.root_mod.resolved_target.result;
3183 const owner_nav = zcu.funcInfo(func_index).owner_nav;3206 const owner_nav = zcu.funcInfo(func_index).owner_nav;
3184 log.debug("updateFunc {}", .{ip.getNav(owner_nav).fqn.fmt(ip)});3207 log.debug("updateFunc {}", .{ip.getNav(owner_nav).fqn.fmt(ip)});
31853208
3209 // For Wasm, we do not lower the MIR to code just yet. That lowering happens during `flush`,
3210 // after garbage collection, which can affect function and global indexes, which affects the
3211 // LEB integer encoding, which affects the output binary size.
3212
3213 // However, we do move the MIR into a more efficient in-memory representation, where the arrays
3214 // for all functions are packed together rather than keeping them each in their own `Mir`.
3215 const mir_instructions_off: u32 = @intCast(wasm.mir_instructions.len);
3216 const mir_extra_off: u32 = @intCast(wasm.mir_extra.items.len);
3217 const mir_locals_off: u32 = @intCast(wasm.mir_locals.items.len);
3218 {
3219 // Copying MultiArrayList data is a little non-trivial. Resize, then memcpy both slices.
3220 const old_len = wasm.mir_instructions.len;
3221 try wasm.mir_instructions.resize(gpa, old_len + mir.instructions.len);
3222 const dest_slice = wasm.mir_instructions.slice().subslice(old_len, mir.instructions.len);
3223 const src_slice = mir.instructions;
3224 @memcpy(dest_slice.items(.tag), src_slice.items(.tag));
3225 @memcpy(dest_slice.items(.data), src_slice.items(.data));
3226 }
3227 try wasm.mir_extra.appendSlice(gpa, mir.extra);
3228 try wasm.mir_locals.appendSlice(gpa, mir.locals);
3229
3230 // We also need to populate some global state from `mir`.
3231 try wasm.zcu_indirect_function_set.ensureUnusedCapacity(gpa, mir.indirect_function_set.count());
3232 for (mir.indirect_function_set.keys()) |nav| wasm.zcu_indirect_function_set.putAssumeCapacity(nav, {});
3233 for (mir.func_tys.keys()) |func_ty| {
3234 const fn_info = zcu.typeToFunc(.fromInterned(func_ty)).?;
3235 _ = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target);
3236 }
3237 wasm.error_name_table_ref_count += mir.error_name_table_ref_count;
3238 // We need to populate UAV data. In theory, we can lower the UAV values while we fill `mir.uavs`.
3239 // However, lowering the data might cause *more* UAVs to be created, and mixing them up would be
3240 // a headache. So instead, just write `undefined` placeholder code and use the `ZcuDataStarts`.
3186 const zds: ZcuDataStarts = .init(wasm);3241 const zds: ZcuDataStarts = .init(wasm);
3242 for (mir.uavs.keys(), mir.uavs.values()) |uav_val, uav_align| {
3243 if (uav_align != .none) {
3244 const gop = try wasm.overaligned_uavs.getOrPut(gpa, uav_val);
3245 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(uav_align) else uav_align;
3246 }
3247 if (is_obj) {
3248 const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val);
3249 if (!gop.found_existing) gop.value_ptr.* = undefined; // `zds` handles lowering
3250 } else {
3251 const gop = try wasm.uavs_exe.getOrPut(gpa, uav_val);
3252 if (!gop.found_existing) gop.value_ptr.* = .{
3253 .code = undefined, // `zds` handles lowering
3254 .count = 0,
3255 };
3256 gop.value_ptr.count += 1;
3257 }
3258 }
3259 try zds.finish(wasm, pt); // actually generates the UAVs
3260
3261 try wasm.functions.ensureUnusedCapacity(gpa, 1);
3262 try wasm.zcu_funcs.ensureUnusedCapacity(gpa, 1);
31873263
3188 // This converts AIR to MIR but does not yet lower to wasm code.3264 // This converts AIR to MIR but does not yet lower to wasm code.
3189 // That lowering happens during `flush`, after garbage collection, which3265 wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = .{
3190 // can affect function and global indexes, which affects the LEB integer3266 .instructions_off = mir_instructions_off,
3191 // encoding, which affects the output binary size.3267 .instructions_len = @intCast(mir.instructions.len),
3192 const function = try CodeGen.function(wasm, pt, func_index, air, liveness);3268 .extra_off = mir_extra_off,
3193 wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = function });3269 .extra_len = @intCast(mir.extra.len),
3270 .locals_off = mir_locals_off,
3271 .locals_len = @intCast(mir.locals.len),
3272 .prologue = mir.prologue,
3273 } });
3194 wasm.functions.putAssumeCapacity(.pack(wasm, .{ .zcu_func = @enumFromInt(wasm.zcu_funcs.entries.len - 1) }), {});3274 wasm.functions.putAssumeCapacity(.pack(wasm, .{ .zcu_func = @enumFromInt(wasm.zcu_funcs.entries.len - 1) }), {});
3195
3196 try zds.finish(wasm, pt);
3197}3275}
31983276
3199// Generate code for the "Nav", storing it in memory to be later written to3277// Generate code for the "Nav", storing it in memory to be later written to
...@@ -3988,58 +4066,54 @@ pub fn symbolNameIndex(wasm: *Wasm, name: String) Allocator.Error!SymbolTableInd...@@ -3988,58 +4066,54 @@ pub fn symbolNameIndex(wasm: *Wasm, name: String) Allocator.Error!SymbolTableInd
3988 return @enumFromInt(gop.index);4066 return @enumFromInt(gop.index);
3989}4067}
39904068
3991pub fn refUavObj(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsObjIndex {4069pub fn addUavReloc(
3992 const comp = wasm.base.comp;4070 wasm: *Wasm,
3993 const zcu = comp.zcu.?;4071 reloc_offset: usize,
3994 const ip = &zcu.intern_pool;4072 uav_val: InternPool.Index,
3995 const gpa = comp.gpa;4073 orig_ptr_ty: InternPool.Index,
3996 assert(comp.config.output_mode == .Obj);4074 addend: u32,
39974075) !void {
3998 if (orig_ptr_ty != .none) {
3999 const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu);
4000 const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment;
4001 if (explicit_alignment.compare(.gt, abi_alignment)) {
4002 const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index);
4003 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment;
4004 }
4005 }
4006
4007 const gop = try wasm.uavs_obj.getOrPut(gpa, ip_index);
4008 if (!gop.found_existing) gop.value_ptr.* = .{
4009 // Lowering the value is delayed to avoid recursion.
4010 .code = undefined,
4011 .relocs = undefined,
4012 };
4013 return @enumFromInt(gop.index);
4014}
4015
4016pub fn refUavExe(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsExeIndex {
4017 const comp = wasm.base.comp;4076 const comp = wasm.base.comp;
4018 const zcu = comp.zcu.?;4077 const zcu = comp.zcu.?;
4019 const ip = &zcu.intern_pool;4078 const ip = &zcu.intern_pool;
4020 const gpa = comp.gpa;4079 const gpa = comp.gpa;
4021 assert(comp.config.output_mode != .Obj);
40224080
4023 if (orig_ptr_ty != .none) {4081 @"align": {
4024 const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu);4082 const ptr_type = ip.indexToKey(orig_ptr_ty).ptr_type;
4025 const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment;4083 const this_align = ptr_type.flags.alignment;
4026 if (explicit_alignment.compare(.gt, abi_alignment)) {4084 if (this_align == .none) break :@"align";
4027 const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index);4085 const abi_align = Zcu.Type.fromInterned(ptr_type.child).abiAlignment(zcu);
4028 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment;4086 if (this_align.compare(.lte, abi_align)) break :@"align";
4029 }4087 const gop = try wasm.overaligned_uavs.getOrPut(gpa, uav_val);
4030 }4088 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(this_align) else this_align;
40314089 }
4032 const gop = try wasm.uavs_exe.getOrPut(gpa, ip_index);4090
4033 if (gop.found_existing) {4091 if (comp.config.output_mode == .Obj) {
4034 gop.value_ptr.count += 1;4092 const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val);
4093 if (!gop.found_existing) gop.value_ptr.* = undefined; // to avoid recursion, `ZcuDataStarts` will lower the value later
4094 try wasm.out_relocs.append(gpa, .{
4095 .offset = @intCast(reloc_offset),
4096 .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(uav_val) },
4097 .tag = switch (wasm.pointerSize()) {
4098 32 => .memory_addr_i32,
4099 64 => .memory_addr_i64,
4100 else => unreachable,
4101 },
4102 .addend = @intCast(addend),
4103 });
4035 } else {4104 } else {
4036 gop.value_ptr.* = .{4105 const gop = try wasm.uavs_exe.getOrPut(gpa, uav_val);
4037 // Lowering the value is delayed to avoid recursion.4106 if (!gop.found_existing) gop.value_ptr.* = .{
4038 .code = undefined,4107 .code = undefined, // to avoid recursion, `ZcuDataStarts` will lower the value later
4039 .count = 1,4108 .count = 0,
4040 };4109 };
4110 gop.value_ptr.count += 1;
4111 try wasm.uav_fixups.append(gpa, .{
4112 .uavs_exe_index = @enumFromInt(gop.index),
4113 .offset = @intCast(reloc_offset),
4114 .addend = addend,
4115 });
4041 }4116 }
4042 return @enumFromInt(gop.index);
4043}4117}
40444118
4045pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {4119pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {
...@@ -4073,10 +4147,11 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {...@@ -4073,10 +4147,11 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {
4073}4147}
40744148
4075/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.4149/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.
4076pub fn uavAddr(wasm: *Wasm, uav_index: UavsExeIndex) u32 {4150pub fn uavAddr(wasm: *Wasm, ip_index: InternPool.Index) u32 {
4077 assert(wasm.flush_buffer.memory_layout_finished);4151 assert(wasm.flush_buffer.memory_layout_finished);
4078 const comp = wasm.base.comp;4152 const comp = wasm.base.comp;
4079 assert(comp.config.output_mode != .Obj);4153 assert(comp.config.output_mode != .Obj);
4154 const uav_index: UavsExeIndex = @enumFromInt(wasm.uavs_exe.getIndex(ip_index).?);
4080 const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index });4155 const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index });
4081 return wasm.flush_buffer.data_segments.get(ds_id).?;4156 return wasm.flush_buffer.data_segments.get(ds_id).?;
4082}4157}
src/link/Wasm/Flush.zig+16-1
...@@ -9,6 +9,7 @@ const Alignment = Wasm.Alignment;...@@ -9,6 +9,7 @@ const Alignment = Wasm.Alignment;
9const String = Wasm.String;9const String = Wasm.String;
10const Relocation = Wasm.Relocation;10const Relocation = Wasm.Relocation;
11const InternPool = @import("../../InternPool.zig");11const InternPool = @import("../../InternPool.zig");
12const Mir = @import("../../arch/wasm/Mir.zig");
1213
13const build_options = @import("build_options");14const build_options = @import("build_options");
1415
...@@ -868,7 +869,21 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -868,7 +869,21 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
868 .enum_type => {869 .enum_type => {
869 try emitTagNameFunction(wasm, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index);870 try emitTagNameFunction(wasm, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index);
870 },871 },
871 else => try i.value(wasm).function.lower(wasm, binary_bytes),872 else => {
873 const func = i.value(wasm).function;
874 const mir: Mir = .{
875 .instructions = wasm.mir_instructions.slice().subslice(func.instructions_off, func.instructions_len),
876 .extra = wasm.mir_extra.items[func.extra_off..][0..func.extra_len],
877 .locals = wasm.mir_locals.items[func.locals_off..][0..func.locals_len],
878 .prologue = func.prologue,
879 // These fields are unused by `lower`.
880 .uavs = undefined,
881 .indirect_function_set = undefined,
882 .func_tys = undefined,
883 .error_name_table_ref_count = undefined,
884 };
885 try mir.lower(wasm, binary_bytes);
886 },
872 }887 }
873 },888 },
874 };889 };
src/target.zig+1-1
...@@ -851,7 +851,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt...@@ -851,7 +851,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt
851 .separate_thread => switch (backend) {851 .separate_thread => switch (backend) {
852 .stage2_llvm => false,852 .stage2_llvm => false,
853 // MLUGG TODO853 // MLUGG TODO
854 .stage2_c => true,854 .stage2_c, .stage2_wasm => true,
855 else => false,855 else => false,
856 },856 },
857 };857 };