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 {
135135 self.* = undefined;
136136 }
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
138154 /// This function is used in the debugger pretty formatters in tools/ to fetch the
139155 /// child field order and entry type to facilitate fancy debug printing for this type.
140156 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 {
35003500 // TODO handle the union safety field
35013501 //addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.data)));
35023502 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));
35043504 addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_bytes.items));
35053505 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");
33const Allocator = std.mem.Allocator;
44const assert = std.debug.assert;
55const testing = std.testing;
6const leb = std.leb;
76const mem = std.mem;
87const log = std.log.scoped(.codegen);
98
......@@ -18,12 +17,10 @@ const Compilation = @import("../../Compilation.zig");
1817const link = @import("../../link.zig");
1918const Air = @import("../../Air.zig");
2019const Mir = @import("Mir.zig");
21const Emit = @import("Emit.zig");
2220const abi = @import("abi.zig");
2321const Alignment = InternPool.Alignment;
2422const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
2523const errUnionErrorOffset = codegen.errUnionErrorOffset;
26const Wasm = link.File.Wasm;
2724
2825const target_util = @import("../../target.zig");
2926const libcFloatPrefix = target_util.libcFloatPrefix;
......@@ -78,17 +75,24 @@ simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty,
7875/// The Target we're emitting (used to call intInfo)
7976target: *const std.Target,
8077ptr_size: enum { wasm32, wasm64 },
81wasm: *link.File.Wasm,
8278pt: Zcu.PerThread,
8379/// List of MIR Instructions
84mir_instructions: *std.MultiArrayList(Mir.Inst),
80mir_instructions: std.MultiArrayList(Mir.Inst),
8581/// Contains extra data for MIR
86mir_extra: *std.ArrayListUnmanaged(u32),
87start_mir_extra_off: u32,
88start_locals_off: u32,
82mir_extra: std.ArrayListUnmanaged(u32),
8983/// List of all locals' types generated throughout this declaration
9084/// 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,
9296/// When a function is executing, we store the the current stack pointer's value within this local.
9397/// This value is then used to restore the stack pointer to the original value at the return of the function.
9498initial_stack_value: WValue = .none,
......@@ -219,7 +223,7 @@ const WValue = union(enum) {
219223 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.
220224
221225 const index = local_value - reserved;
222 const valtype = gen.locals.items[gen.start_locals_off + index];
226 const valtype = gen.mir_locals.items[index];
223227 switch (valtype) {
224228 .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
225229 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,
......@@ -716,6 +720,12 @@ pub fn deinit(cg: *CodeGen) void {
716720 cg.free_locals_f32.deinit(gpa);
717721 cg.free_locals_f64.deinit(gpa);
718722 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);
719729 cg.* = undefined;
720730}
721731
......@@ -876,7 +886,7 @@ fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
876886}
877887
878888fn 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);
880890 try cg.mir_extra.append(cg.gpa, @intFromEnum(opcode));
881891 try cg.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });
882892}
......@@ -889,10 +899,6 @@ fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void
889899 try cg.addInst(.{ .tag = tag, .data = .{ .local = local } });
890900}
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
896902/// Accepts an unsigned 32bit integer rather than a signed integer to
897903/// prevent us from having to bitcast multiple times as most values
898904/// within codegen are represented as unsigned rather than signed.
......@@ -911,7 +917,7 @@ fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void {
911917/// Accepts the index into the list of 128bit-immediates
912918fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void {
913919 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);
915921 // tag + 128bit value
916922 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 5);
917923 cg.mir_extra.appendAssumeCapacity(@intFromEnum(std.wasm.SimdOpcode.v128_const));
......@@ -956,15 +962,13 @@ fn addExtra(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
956962/// Returns the index into `mir_extra`
957963fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
958964 const fields = std.meta.fields(@TypeOf(extra));
959 const result = cg.extraLen();
965 const result: u32 = @intCast(cg.mir_extra.items.len);
960966 inline for (fields) |field| {
961967 cg.mir_extra.appendAssumeCapacity(switch (field.type) {
962968 u32 => @field(extra, field.name),
963969 i32 => @bitCast(@field(extra, field.name)),
964970 InternPool.Index,
965971 InternPool.Nav.Index,
966 Wasm.UavsObjIndex,
967 Wasm.UavsExeIndex,
968972 => @intFromEnum(@field(extra, field.name)),
969973 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
970974 });
......@@ -1034,18 +1038,12 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
10341038 .float32 => |val| try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
10351039 .float64 => |val| try cg.addFloat64(val),
10361040 .nav_ref => |nav_ref| {
1037 const wasm = cg.wasm;
1038 const comp = wasm.base.comp;
1039 const zcu = comp.zcu.?;
1041 const zcu = cg.pt.zcu;
10401042 const ip = &zcu.intern_pool;
10411043 if (ip.getNav(nav_ref.nav_index).isFn(ip)) {
10421044 assert(nav_ref.offset == 0);
1043 const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, nav_ref.nav_index);
1044 if (!gop.found_existing) gop.value_ptr.* = {};
1045 try cg.addInst(.{
1046 .tag = .func_ref,
1047 .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) },
1048 });
1045 try cg.mir_indirect_function_set.put(cg.gpa, nav_ref.nav_index, {});
1046 try cg.addInst(.{ .tag = .func_ref, .data = .{ .nav_index = nav_ref.nav_index } });
10491047 } else if (nav_ref.offset == 0) {
10501048 try cg.addInst(.{ .tag = .nav_ref, .data = .{ .nav_index = nav_ref.nav_index } });
10511049 } else {
......@@ -1061,41 +1059,37 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
10611059 }
10621060 },
10631061 .uav_ref => |uav| {
1064 const wasm = cg.wasm;
1065 const comp = wasm.base.comp;
1066 const is_obj = comp.config.output_mode == .Obj;
1067 const zcu = comp.zcu.?;
1062 const zcu = cg.pt.zcu;
10681063 const ip = &zcu.intern_pool;
1069 if (ip.isFunctionType(ip.typeOf(uav.ip_index))) {
1070 assert(uav.offset == 0);
1071 const owner_nav = ip.toFunc(uav.ip_index).owner_nav;
1072 const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, owner_nav);
1073 if (!gop.found_existing) gop.value_ptr.* = {};
1074 try cg.addInst(.{
1075 .tag = .func_ref,
1076 .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) },
1077 });
1078 } else if (uav.offset == 0) {
1064 assert(!ip.isFunctionType(ip.typeOf(uav.ip_index)));
1065 const gop = try cg.mir_uavs.getOrPut(cg.gpa, uav.ip_index);
1066 const this_align: Alignment = a: {
1067 if (uav.orig_ptr_ty == .none) break :a .none;
1068 const ptr_type = ip.indexToKey(uav.orig_ptr_ty).ptr_type;
1069 const this_align = ptr_type.flags.alignment;
1070 if (this_align == .none) break :a .none;
1071 const abi_align = Type.fromInterned(ptr_type.child).abiAlignment(zcu);
1072 if (this_align.compare(.lte, abi_align)) break :a .none;
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) {
10791082 try cg.addInst(.{
10801083 .tag = .uav_ref,
1081 .data = if (is_obj) .{
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 },
1084 .data = .{ .ip_index = uav.ip_index },
10861085 });
10871086 } else {
10881087 try cg.addInst(.{
10891088 .tag = .uav_ref_off,
1090 .data = .{
1091 .payload = if (is_obj) try cg.addExtra(Mir.UavRefOffObj{
1092 .uav_obj = try wasm.refUavObj(uav.ip_index, uav.orig_ptr_ty),
1093 .offset = uav.offset,
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 },
1089 .data = .{ .payload = try cg.addExtra(@as(Mir.UavRefOff, .{
1090 .value = uav.ip_index,
1091 .offset = uav.offset,
1092 })) },
10991093 });
11001094 }
11011095 },
......@@ -1157,106 +1151,12 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
11571151/// to use a zero-initialized local.
11581152fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
11591153 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));
11611155 const initial_index = cg.local_index;
11621156 cg.local_index += 1;
11631157 return .{ .local = .{ .value = initial_index, .references = 1 } };
11641158}
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
12601160pub const Error = error{
12611161 OutOfMemory,
12621162 /// Compiler was asked to operate on a number larger than supported.
......@@ -1265,13 +1165,16 @@ pub const Error = error{
12651165 CodegenFail,
12661166};
12671167
1268pub fn function(
1269 wasm: *Wasm,
1168pub fn generate(
1169 bin_file: *link.File,
12701170 pt: Zcu.PerThread,
1171 src_loc: Zcu.LazySrcLoc,
12711172 func_index: InternPool.Index,
1272 air: Air,
1273 liveness: Air.Liveness,
1274) Error!Function {
1173 air: *const Air,
1174 liveness: *const Air.Liveness,
1175) Error!Mir {
1176 _ = src_loc;
1177 _ = bin_file;
12751178 const zcu = pt.zcu;
12761179 const gpa = zcu.gpa;
12771180 const cg = zcu.funcInfo(func_index);
......@@ -1279,10 +1182,8 @@ pub fn function(
12791182 const target = &file_scope.mod.?.resolved_target.result;
12801183 const fn_ty = zcu.navValue(cg.owner_nav).typeOf(zcu);
12811184 const fn_info = zcu.typeToFunc(fn_ty).?;
1282 const ip = &zcu.intern_pool;
1283 const fn_ty_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target);
1284 const returns = fn_ty_index.ptr(wasm).returns.slice(wasm);
1285 const any_returns = returns.len != 0;
1185 const ret_ty: Type = .fromInterned(fn_info.return_type);
1186 const any_returns = !firstParamSRet(fn_info.cc, ret_ty, zcu, target) and ret_ty.hasRuntimeBitsIgnoreComptime(zcu);
12861187
12871188 var cc_result = try resolveCallingConventionValues(zcu, fn_ty, target);
12881189 defer cc_result.deinit(gpa);
......@@ -1290,8 +1191,8 @@ pub fn function(
12901191 var code_gen: CodeGen = .{
12911192 .gpa = gpa,
12921193 .pt = pt,
1293 .air = air,
1294 .liveness = liveness,
1194 .air = air.*,
1195 .liveness = liveness.*,
12951196 .owner_nav = cg.owner_nav,
12961197 .target = target,
12971198 .ptr_size = switch (target.cpu.arch) {
......@@ -1299,31 +1200,33 @@ pub fn function(
12991200 .wasm64 => .wasm64,
13001201 else => unreachable,
13011202 },
1302 .wasm = wasm,
13031203 .func_index = func_index,
13041204 .args = cc_result.args,
13051205 .return_value = cc_result.return_value,
13061206 .local_index = cc_result.local_index,
1307 .mir_instructions = &wasm.mir_instructions,
1308 .mir_extra = &wasm.mir_extra,
1309 .locals = &wasm.all_zcu_locals,
1310 .start_mir_extra_off = @intCast(wasm.mir_extra.items.len),
1311 .start_locals_off = @intCast(wasm.all_zcu_locals.items.len),
1207 .mir_instructions = .empty,
1208 .mir_extra = .empty,
1209 .mir_locals = .empty,
1210 .mir_uavs = .empty,
1211 .mir_indirect_function_set = .empty,
1212 .mir_func_tys = .empty,
1213 .error_name_table_ref_count = 0,
13121214 };
13131215 defer code_gen.deinit();
13141216
1315 return functionInner(&code_gen, any_returns) catch |err| switch (err) {
1316 error.CodegenFail => return error.CodegenFail,
1217 try code_gen.mir_func_tys.putNoClobber(gpa, fn_ty.toIntern(), {});
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,
13171224 else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}),
13181225 };
13191226}
13201227
1321fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function {
1322 const wasm = cg.wasm;
1228fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir {
13231229 const zcu = cg.pt.zcu;
1324
1325 const start_mir_off: u32 = @intCast(wasm.mir_instructions.len);
1326
13271230 try cg.branches.append(cg.gpa, .{});
13281231 // clean up outer branch
13291232 defer {
......@@ -1347,20 +1250,25 @@ fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function {
13471250 try cg.addTag(.end);
13481251 try cg.addTag(.dbg_epilogue_begin);
13491252
1350 return .{
1351 .mir_off = start_mir_off,
1352 .mir_len = @intCast(wasm.mir_instructions.len - start_mir_off),
1353 .mir_extra_off = cg.start_mir_extra_off,
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),
1253 var mir: Mir = .{
1254 .instructions = cg.mir_instructions.toOwnedSlice(),
1255 .extra = &.{}, // fallible so assigned after errdefer
1256 .locals = &.{}, // fallible so assigned after errdefer
13571257 .prologue = if (cg.initial_stack_value == .none) .none else .{
13581258 .sp_local = cg.initial_stack_value.local.value,
13591259 .flags = .{ .stack_alignment = cg.stack_alignment },
13601260 .stack_size = cg.stack_size,
13611261 .bottom_stack_local = cg.bottom_stack_value.local.value,
13621262 },
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,
13631267 };
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;
13641272}
13651273
13661274const CallWValues = struct {
......@@ -2220,7 +2128,6 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
22202128}
22212129
22222130fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {
2223 const wasm = cg.wasm;
22242131 if (modifier == .always_tail) return cg.fail("TODO implement tail calls for wasm", .{});
22252132 const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
22262133 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
22772184 const operand = try cg.resolveInst(pl_op.operand);
22782185 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);
2281 try cg.addFuncTy(.call_indirect, fn_type_index);
2187 try cg.mir_func_tys.put(cg.gpa, fn_ty.toIntern(), {});
2188 try cg.addInst(.{
2189 .tag = .call_indirect,
2190 .data = .{ .ip_index = fn_ty.toIntern() },
2191 });
22822192 }
22832193
22842194 const result_value = result_value: {
......@@ -2449,7 +2359,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr
24492359 try cg.emitWValue(lhs);
24502360 try cg.lowerToStack(rhs);
24512361 // TODO: Add helper functions for simd opcodes
2452 const extra_index = cg.extraLen();
2362 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
24532363 // stores as := opcode, offset, alignment (opcode::memarg)
24542364 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
24552365 @intFromEnum(std.wasm.SimdOpcode.v128_store),
......@@ -2574,7 +2484,7 @@ fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue
25742484
25752485 if (ty.zigTypeTag(zcu) == .vector) {
25762486 // TODO: Add helper functions for simd opcodes
2577 const extra_index = cg.extraLen();
2487 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
25782488 // stores as := opcode, offset, alignment (opcode::memarg)
25792489 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
25802490 @intFromEnum(std.wasm.SimdOpcode.v128_load),
......@@ -4971,7 +4881,7 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
49714881
49724882 try cg.emitWValue(array);
49734883
4974 const extra_index = cg.extraLen();
4884 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
49754885 try cg.mir_extra.appendSlice(cg.gpa, &operands);
49764886 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 {
51235033 else => break :blk, // Cannot make use of simd-instructions
51245034 };
51255035 try cg.emitWValue(operand);
5126 const extra_index: u32 = cg.extraLen();
5036 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
51275037 // stores as := opcode, offset, alignment (opcode::memarg)
51285038 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
51295039 opcode,
......@@ -5142,7 +5052,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
51425052 else => break :blk, // Cannot make use of simd-instructions
51435053 };
51445054 try cg.emitWValue(operand);
5145 const extra_index = cg.extraLen();
5055 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
51465056 try cg.mir_extra.append(cg.gpa, opcode);
51475057 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
51485058 return cg.finishAir(inst, .stack, &.{ty_op.operand});
......@@ -5246,7 +5156,7 @@ fn airShuffleTwo(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52465156 }
52475157 try cg.emitWValue(operand_a);
52485158 try cg.emitWValue(operand_b);
5249 const extra_index = cg.extraLen();
5159 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
52505160 try cg.mir_extra.appendSlice(cg.gpa, &.{
52515161 @intFromEnum(std.wasm.SimdOpcode.i8x16_shuffle),
52525162 @bitCast(lane_map[0..4].*),
......@@ -6016,9 +5926,8 @@ fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
60165926 const name_ty = Type.slice_const_u8_sentinel_0;
60175927 const abi_size = name_ty.abiSize(pt.zcu);
60185928
6019 cg.wasm.error_name_table_ref_count += 1;
6020
60215929 // Lowers to a i32.const or i64.const with the error table memory address.
5930 cg.error_name_table_ref_count += 1;
60225931 try cg.addTag(.error_name_table_ref);
60235932 try cg.emitWValue(operand);
60245933 switch (cg.ptr_size) {
......@@ -6046,7 +5955,7 @@ fn airPtrSliceFieldPtr(cg: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerErr
60465955
60475956/// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits
60485957fn intZeroValue(cg: *CodeGen, ty: Type) InnerError!WValue {
6049 const zcu = cg.wasm.base.comp.zcu.?;
5958 const zcu = cg.pt.zcu;
60505959 const int_info = ty.intInfo(zcu);
60515960 const wasm_bits = toWasmBits(int_info.bits) orelse {
60525961 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 {
76737582 },
76747583 };
76757584}
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 {
3131 const target = &comp.root_mod.resolved_target.result;
3232 const is_wasm32 = target.cpu.arch == .wasm32;
3333
34 const tags = mir.instruction_tags;
35 const datas = mir.instruction_datas;
34 const tags = mir.instructions.items(.tag);
35 const datas = mir.instructions.items(.data);
3636 var inst: u32 = 0;
3737
3838 loop: switch (tags[inst]) {
......@@ -50,18 +50,19 @@ pub fn lowerToCode(emit: *Emit) Error!void {
5050 },
5151 .uav_ref => {
5252 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);
5454 } 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);
5656 }
5757 inst += 1;
5858 continue :loop tags[inst];
5959 },
6060 .uav_ref_off => {
61 const extra = mir.extraData(Mir.UavRefOff, datas[inst].payload).data;
6162 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);
6364 } 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);
6566 }
6667 inst += 1;
6768 continue :loop tags[inst];
......@@ -77,11 +78,14 @@ pub fn lowerToCode(emit: *Emit) Error!void {
7778 continue :loop tags[inst];
7879 },
7980 .func_ref => {
81 const indirect_func_idx: Wasm.ZcuIndirectFunctionSetIndex = @enumFromInt(
82 wasm.zcu_indirect_function_set.getIndex(datas[inst].nav_index).?,
83 );
8084 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
8185 if (is_obj) {
8286 @panic("TODO");
8387 } 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;
8589 }
8690 inst += 1;
8791 continue :loop tags[inst];
......@@ -101,6 +105,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
101105 continue :loop tags[inst];
102106 },
103107 .error_name_table_ref => {
108 wasm.error_name_table_ref_count += 1;
104109 try code.ensureUnusedCapacity(gpa, 11);
105110 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
106111 code.appendAssumeCapacity(@intFromEnum(opcode));
......@@ -176,7 +181,13 @@ pub fn lowerToCode(emit: *Emit) Error!void {
176181
177182 .call_indirect => {
178183 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 ).?;
180191 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect));
181192 if (is_obj) {
182193 try wasm.out_relocs.append(gpa, .{
......@@ -912,7 +923,7 @@ fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {
912923 leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable;
913924}
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 {
916927 const comp = wasm.base.comp;
917928 const gpa = comp.gpa;
918929 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
922933
923934 try wasm.out_relocs.append(gpa, .{
924935 .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) },
926937 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
927 .addend = data.offset,
938 .addend = offset,
928939 });
929940 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
930941}
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 {
933944 const comp = wasm.base.comp;
934945 const gpa = comp.gpa;
935946 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
937948 try code.ensureUnusedCapacity(gpa, 11);
938949 code.appendAssumeCapacity(@intFromEnum(opcode));
939950
940 const addr = wasm.uavAddr(data.uav_exe);
941 leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable;
951 const addr = wasm.uavAddr(value);
952 leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + offset))) catch unreachable;
942953}
943954
944955fn 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 @@
99const Mir = @This();
1010const InternPool = @import("../../InternPool.zig");
1111const Wasm = @import("../../link/Wasm.zig");
12const Emit = @import("Emit.zig");
13const Alignment = InternPool.Alignment;
1214
1315const builtin = @import("builtin");
1416const std = @import("std");
1517const assert = std.debug.assert;
18const leb = std.leb;
1619
17instruction_tags: []const Inst.Tag,
18instruction_datas: []const Inst.Data,
20instructions: std.MultiArrayList(Inst).Slice,
1921/// A slice of indexes where the meaning of the data is determined by the
2022/// `Inst.Tag` value.
2123extra: []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
2360pub const Inst = struct {
2461 /// The opcode that represents this instruction
......@@ -80,7 +117,7 @@ pub const Inst = struct {
80117 /// Lowers to an i32_const which is the index of the function in the
81118 /// table section.
82119 ///
83 /// Uses `indirect_function_table_index`.
120 /// Uses `nav_index`.
84121 func_ref,
85122 /// Inserts debug information about the current line and column
86123 /// of the source code
......@@ -123,7 +160,7 @@ pub const Inst = struct {
123160 /// Calls a function pointer by its function signature
124161 /// and index into the function table.
125162 ///
126 /// Uses `func_ty`
163 /// Uses `ip_index`; the `InternPool.Index` is the function type.
127164 call_indirect,
128165 /// Calls a function by its index.
129166 ///
......@@ -611,11 +648,7 @@ pub const Inst = struct {
611648
612649 ip_index: InternPool.Index,
613650 nav_index: InternPool.Nav.Index,
614 func_ty: Wasm.FunctionType.Index,
615651 intrinsic: Intrinsic,
616 uav_obj: Wasm.UavsObjIndex,
617 uav_exe: Wasm.UavsExeIndex,
618 indirect_function_table_index: Wasm.ZcuIndirectFunctionSetIndex,
619652
620653 comptime {
621654 switch (builtin.mode) {
......@@ -626,10 +659,66 @@ pub const Inst = struct {
626659 };
627660};
628661
629pub fn deinit(self: *Mir, gpa: std.mem.Allocator) void {
630 self.instructions.deinit(gpa);
631 gpa.free(self.extra);
632 self.* = undefined;
662pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
663 mir.instructions.deinit(gpa);
664 gpa.free(mir.extra);
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();
633722}
634723
635724pub 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
643732 Wasm.UavsObjIndex,
644733 Wasm.UavsExeIndex,
645734 InternPool.Nav.Index,
735 InternPool.Index,
646736 => @enumFromInt(self.extra[i]),
647737 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
648738 };
......@@ -695,13 +785,8 @@ pub const MemArg = struct {
695785 alignment: u32,
696786};
697787
698pub const UavRefOffObj = struct {
699 uav_obj: Wasm.UavsObjIndex,
700 offset: i32,
701};
702
703pub const UavRefOffExe = struct {
704 uav_exe: Wasm.UavsExeIndex,
788pub const UavRefOff = struct {
789 value: InternPool.Index,
705790 offset: i32,
706791};
707792
src/codegen.zig+3-16
......@@ -123,6 +123,7 @@ pub const AnyMir = union {
123123 .stage2_riscv64,
124124 .stage2_sparc64,
125125 .stage2_x86_64,
126 .stage2_wasm,
126127 .stage2_c,
127128 => |backend_ct| @field(mir, tag(backend_ct)).deinit(gpa),
128129 }
......@@ -153,6 +154,7 @@ pub fn generateFunction(
153154 .stage2_riscv64,
154155 .stage2_sparc64,
155156 .stage2_x86_64,
157 .stage2_wasm,
156158 .stage2_c,
157159 => |backend| {
158160 dev.check(devFeatureForBackend(backend));
......@@ -784,7 +786,6 @@ fn lowerUavRef(
784786 const comp = lf.comp;
785787 const target = &comp.root_mod.resolved_target.result;
786788 const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8);
787 const is_obj = comp.config.output_mode == .Obj;
788789 const uav_val = uav.val;
789790 const uav_ty = Type.fromInterned(ip.typeOf(uav_val));
790791 const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn";
......@@ -804,21 +805,7 @@ fn lowerUavRef(
804805 dev.check(link.File.Tag.wasm.devFeature());
805806 const wasm = lf.cast(.wasm).?;
806807 assert(reloc_parent == .none);
807 if (is_obj) {
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 }
808 try wasm.addUavReloc(code.items.len, uav.val, uav.orig_ty, @intCast(offset));
822809 code.appendNTimesAssumeCapacity(0, ptr_width_bytes);
823810 return;
824811 },
src/link.zig+1-2
......@@ -759,7 +759,6 @@ pub const File = struct {
759759 switch (base.tag) {
760760 .lld => unreachable,
761761 inline else => |tag| {
762 if (tag == .wasm) @panic("MLUGG TODO");
763762 if (tag == .spirv) @panic("MLUGG TODO");
764763 dev.check(tag.devFeature());
765764 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 {
14501449 const nav = zcu.funcInfo(func.func).owner_nav;
14511450 const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid));
14521451 defer pt.deactivate();
1453 assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR
14541452 switch (func.mir.status.load(.monotonic)) {
14551453 .pending => unreachable,
14561454 .ready => {},
14571455 .failed => return,
14581456 }
1457 assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR
14591458 const mir = &func.mir.value;
14601459 if (comp.bin_file) |lf| {
14611460 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) = .{},
282282/// Corresponds to `mir_instructions`.
283283mir_extra: std.ArrayListUnmanaged(u32) = .empty,
284284/// 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
287287params_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,
288288returns_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty,
......@@ -866,9 +866,24 @@ const ZcuDataStarts = struct {
866866};
867867
868868pub const ZcuFunc = union {
869 function: CodeGen.Function,
869 function: Function,
870870 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
872887 pub const TagName = extern struct {
873888 symbol_name: String,
874889 type_index: FunctionType.Index,
......@@ -3107,7 +3122,7 @@ pub fn deinit(wasm: *Wasm) void {
31073122
31083123 wasm.mir_instructions.deinit(gpa);
31093124 wasm.mir_extra.deinit(gpa);
3110 wasm.all_zcu_locals.deinit(gpa);
3125 wasm.mir_locals.deinit(gpa);
31113126
31123127 if (wasm.dwarf) |*dwarf| dwarf.deinit();
31133128
......@@ -3167,33 +3182,96 @@ pub fn deinit(wasm: *Wasm) void {
31673182 wasm.missing_exports.deinit(gpa);
31683183}
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 {
31713192 if (build_options.skip_non_native and builtin.object_format != .wasm) {
31723193 @panic("Attempted to compile for object format that was disabled by build configuration");
31733194 }
31743195
31753196 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;
31773201 const zcu = pt.zcu;
31783202 const gpa = zcu.gpa;
3179 try wasm.functions.ensureUnusedCapacity(gpa, 1);
3180 try wasm.zcu_funcs.ensureUnusedCapacity(gpa, 1);
3181
31823203 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;
31833206 const owner_nav = zcu.funcInfo(func_index).owner_nav;
31843207 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`.
31863241 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
31883264 // This converts AIR to MIR but does not yet lower to wasm code.
3189 // That lowering happens during `flush`, after garbage collection, which
3190 // can affect function and global indexes, which affects the LEB integer
3191 // encoding, which affects the output binary size.
3192 const function = try CodeGen.function(wasm, pt, func_index, air, liveness);
3193 wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = function });
3265 wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = .{
3266 .instructions_off = mir_instructions_off,
3267 .instructions_len = @intCast(mir.instructions.len),
3268 .extra_off = mir_extra_off,
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 } });
31943274 wasm.functions.putAssumeCapacity(.pack(wasm, .{ .zcu_func = @enumFromInt(wasm.zcu_funcs.entries.len - 1) }), {});
3195
3196 try zds.finish(wasm, pt);
31973275}
31983276
31993277// 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
39884066 return @enumFromInt(gop.index);
39894067}
39904068
3991pub fn refUavObj(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsObjIndex {
3992 const comp = wasm.base.comp;
3993 const zcu = comp.zcu.?;
3994 const ip = &zcu.intern_pool;
3995 const gpa = comp.gpa;
3996 assert(comp.config.output_mode == .Obj);
3997
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 {
4069pub fn addUavReloc(
4070 wasm: *Wasm,
4071 reloc_offset: usize,
4072 uav_val: InternPool.Index,
4073 orig_ptr_ty: InternPool.Index,
4074 addend: u32,
4075) !void {
40174076 const comp = wasm.base.comp;
40184077 const zcu = comp.zcu.?;
40194078 const ip = &zcu.intern_pool;
40204079 const gpa = comp.gpa;
4021 assert(comp.config.output_mode != .Obj);
40224080
4023 if (orig_ptr_ty != .none) {
4024 const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu);
4025 const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment;
4026 if (explicit_alignment.compare(.gt, abi_alignment)) {
4027 const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index);
4028 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment;
4029 }
4030 }
4031
4032 const gop = try wasm.uavs_exe.getOrPut(gpa, ip_index);
4033 if (gop.found_existing) {
4034 gop.value_ptr.count += 1;
4081 @"align": {
4082 const ptr_type = ip.indexToKey(orig_ptr_ty).ptr_type;
4083 const this_align = ptr_type.flags.alignment;
4084 if (this_align == .none) break :@"align";
4085 const abi_align = Zcu.Type.fromInterned(ptr_type.child).abiAlignment(zcu);
4086 if (this_align.compare(.lte, abi_align)) break :@"align";
4087 const gop = try wasm.overaligned_uavs.getOrPut(gpa, uav_val);
4088 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(this_align) else this_align;
4089 }
4090
4091 if (comp.config.output_mode == .Obj) {
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 });
40354104 } else {
4036 gop.value_ptr.* = .{
4037 // Lowering the value is delayed to avoid recursion.
4038 .code = undefined,
4039 .count = 1,
4105 const gop = try wasm.uavs_exe.getOrPut(gpa, uav_val);
4106 if (!gop.found_existing) gop.value_ptr.* = .{
4107 .code = undefined, // to avoid recursion, `ZcuDataStarts` will lower the value later
4108 .count = 0,
40404109 };
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 });
40414116 }
4042 return @enumFromInt(gop.index);
40434117}
40444118
40454119pub 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 {
40734147}
40744148
40754149/// 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 {
40774151 assert(wasm.flush_buffer.memory_layout_finished);
40784152 const comp = wasm.base.comp;
40794153 assert(comp.config.output_mode != .Obj);
4154 const uav_index: UavsExeIndex = @enumFromInt(wasm.uavs_exe.getIndex(ip_index).?);
40804155 const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index });
40814156 return wasm.flush_buffer.data_segments.get(ds_id).?;
40824157}
src/link/Wasm/Flush.zig+16-1
......@@ -9,6 +9,7 @@ const Alignment = Wasm.Alignment;
99const String = Wasm.String;
1010const Relocation = Wasm.Relocation;
1111const InternPool = @import("../../InternPool.zig");
12const Mir = @import("../../arch/wasm/Mir.zig");
1213
1314const build_options = @import("build_options");
1415
......@@ -868,7 +869,21 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
868869 .enum_type => {
869870 try emitTagNameFunction(wasm, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index);
870871 },
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 },
872887 }
873888 },
874889 };
src/target.zig+1-1
......@@ -851,7 +851,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt
851851 .separate_thread => switch (backend) {
852852 .stage2_llvm => false,
853853 // MLUGG TODO
854 .stage2_c => true,
854 .stage2_c, .stage2_wasm => true,
855855 else => false,
856856 },
857857 };