authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-04-17 20:05:24+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-04-22 21:57:38+02:00
log6c1ab376ddcdbb05610487e5b813d42ff37da40d
tree759a47fde287b33ae3e40b88a83dc8723b98b379
parentd4ceb12ae9d409dbd52c1f5c96312a1e6ad7d6bc
signature Commit is signed but in an unrecognized format.

wasm: store `__zig_lt_errors_len` in linear data

Rather than using a function call to verify if an error fits within the global error set's length, we now store the error set' size in the .rodata segment of the linear memory and load that value onto the stack to check with the integer value.

2 files changed, 43 insertions(+), 45 deletions(-)

src/arch/wasm/CodeGen.zig+6-6
......@@ -3339,13 +3339,14 @@ fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
33393339fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
33403340 const un_op = func.air.instructions.items(.data)[inst].un_op;
33413341 const operand = try func.resolveInst(un_op);
3342 const sym_index = try func.bin_file.getGlobalSymbol("__zig_lt_errors_len", null);
3342 const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null);
3343 const errors_len = WValue{ .memory = sym_index };
33433344
33443345 try func.emitWValue(operand);
3345 try func.addLabel(.call, sym_index);
3346 const result = try func.allocLocal(Type.bool);
3347 try func.addLabel(.local_set, result.local.value);
3348 return func.finishAir(inst, result, &.{un_op});
3346 const errors_len_val = try func.load(errors_len, Type.err_int, 0);
3347 const result = try func.cmp(.stack, errors_len_val, Type.err_int, .lt);
3348
3349 return func.finishAir(inst, try result.toLocal(func, Type.bool), &.{un_op});
33493350}
33503351
33513352fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -6518,7 +6519,6 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
65186519
65196520fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
65206521 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
6521 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
65226522
65236523 const operand = try func.resolveInst(ty_op.operand);
65246524 const error_set_ty = func.air.getRefType(ty_op.ty);
src/link/Wasm.zig+37-39
......@@ -1209,9 +1209,10 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
12091209 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
12101210 }
12111211 }
1212 if (wasm.undefs.fetchSwapRemove("__zig_lt_errors_len")) |kv| {
1213 const loc = try wasm.createSyntheticSymbol("__zig_lt_errors_len", .function);
1212 if (wasm.undefs.fetchSwapRemove("__zig_errors_len")) |kv| {
1213 const loc = try wasm.createSyntheticSymbol("__zig_errors_len", .data);
12141214 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
1215 _ = wasm.resolved_symbols.swapRemove(kv.value);
12151216 }
12161217}
12171218
......@@ -2189,44 +2190,41 @@ fn setupInitFunctions(wasm: *Wasm) !void {
21892190 std.sort.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan);
21902191}
21912192
2192/// Generates the function which verifies if an integer value is less than the
2193/// amount of error values. This will only be generated if the symbol exists.
2194fn setupLtErrorsLenFunction(wasm: *Wasm) !void {
2195 if (wasm.findGlobalSymbol("__zig_lt_errors_len") == null) return;
2196 const errors_len = wasm.base.options.module.?.global_error_set.count();
2197
2198 var body_list = std.ArrayList(u8).init(wasm.base.allocator);
2199 defer body_list.deinit();
2200 const writer = body_list.writer();
2201
2202 {
2203 // generates bytecode for the following function:
2204 // fn (index: u16) bool {
2205 // return index < errors_len;
2206 // }
2207
2208 // no locals
2209 try leb.writeULEB128(writer, @as(u32, 0));
2210
2211 // get argument
2212 try writer.writeByte(std.wasm.opcode(.local_get));
2213 try leb.writeULEB128(writer, @as(u32, 0));
2214
2215 // get error length
2216 try writer.writeByte(std.wasm.opcode(.i32_const));
2217 try leb.writeULEB128(writer, @intCast(u32, errors_len));
2218
2219 try writer.writeByte(std.wasm.opcode(.i32_lt_u));
2220
2221 // stack values are implicit return values so keep the value
2222 // on the stack and end the function.
2193/// Generates an atom containing the global error set' size.
2194/// This will only be generated if the symbol exists.
2195fn setupErrorsLen(wasm: *Wasm) !void {
2196 const loc = wasm.findGlobalSymbol("__zig_errors_len") orelse return;
22232197
2224 // end function
2225 try writer.writeByte(std.wasm.opcode(.end));
2226 }
2198 const errors_len = wasm.base.options.module.?.global_error_set.count();
2199 // overwrite existing atom if it already exists (maybe the error set has increased)
2200 // if not, allcoate a new atom.
2201 const atom_index = if (wasm.symbol_atom.get(loc)) |index| blk: {
2202 const atom = wasm.getAtomPtr(index);
2203 if (atom.next) |next_atom_index| {
2204 const next_atom = wasm.getAtomPtr(next_atom_index);
2205 next_atom.prev = atom.prev;
2206 atom.next = null;
2207 }
2208 if (atom.prev) |prev_index| {
2209 const prev_atom = wasm.getAtomPtr(prev_index);
2210 prev_atom.next = atom.next;
2211 atom.prev = null;
2212 }
2213 atom.deinit(wasm);
2214 break :blk index;
2215 } else new_atom: {
2216 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
2217 try wasm.symbol_atom.put(wasm.base.allocator, loc, atom_index);
2218 try wasm.managed_atoms.append(wasm.base.allocator, undefined);
2219 break :new_atom atom_index;
2220 };
2221 const atom = wasm.getAtomPtr(atom_index);
2222 atom.* = Atom.empty;
2223 atom.sym_index = loc.index;
2224 atom.size = 2;
2225 try atom.code.writer(wasm.base.allocator).writeIntLittle(u16, @intCast(u16, errors_len));
22272226
2228 const func_type: std.wasm.Type = .{ .params = &.{std.wasm.Valtype.i32}, .returns = &.{std.wasm.Valtype.i32} };
2229 try wasm.createSyntheticFunction("__zig_lt_errors_len", func_type, &body_list);
2227 try wasm.parseAtom(atom_index, .{ .data = .read_only });
22302228}
22312229
22322230/// Creates a function body for the `__wasm_call_ctors` symbol.
......@@ -3361,6 +3359,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
33613359 // So we can rebuild the binary file on each incremental update
33623360 defer wasm.resetState();
33633361 try wasm.setupInitFunctions();
3362 try wasm.setupErrorsLen();
33643363 try wasm.setupStart();
33653364 try wasm.setupImports();
33663365 if (wasm.base.options.module) |mod| {
......@@ -3423,7 +3422,6 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
34233422 try wasm.setupInitMemoryFunction();
34243423 try wasm.setupTLSRelocationsFunction();
34253424 try wasm.initializeTLSFunction();
3426 try wasm.setupLtErrorsLenFunction();
34273425 try wasm.setupExports();
34283426 try wasm.writeToFile(enabled_features, emit_features_count, arena);
34293427}