authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-04-15 16:17:25+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-04-22 21:16:23+02:00
logc4b295bb6e9fcb1d02122cb70c95019c81ae543e
treed83d44b17acddd5be0affc7cc368e479ae4f9acd
parent21aa55d34e432b677f71cbb377aa89c8cdbd1483
signature Commit is signed but in an unrecognized format.

wasm: implement `cmp_lt_errors_len` instruction

Creates a global undefined symbol when this instruction is called. The linker will then resolve it as a lazy symbol, ensuring it is only generated when the symbol was created. In `flush` it will then generate the function as only then, all errors are known and we can generate the function body. This logic allows us to re-use the same functionality of linker-synthetic-functions.

2 files changed, 51 insertions(+), 2 deletions(-)

src/arch/wasm/CodeGen.zig+6-2
...@@ -3338,9 +3338,13 @@ fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3338,9 +3338,13 @@ fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3338fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3338fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3339 const un_op = func.air.instructions.items(.data)[inst].un_op;3339 const un_op = func.air.instructions.items(.data)[inst].un_op;
3340 const operand = try func.resolveInst(un_op);3340 const operand = try func.resolveInst(un_op);
3341 const sym_index = try func.bin_file.getGlobalSymbol("__zig_lt_errors_len", null);
33413342
3342 _ = operand;3343 try func.emitWValue(operand);
3343 return func.fail("TODO implement airCmpLtErrorsLen for wasm", .{});3344 try func.addLabel(.call, sym_index);
3345 const result = try func.allocLocal(Type.bool);
3346 try func.addLabel(.local_set, result.local.value);
3347 return func.finishAir(inst, result, &.{un_op});
3344}3348}
33453349
3346fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3350fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
src/link/Wasm.zig+45
...@@ -1209,6 +1209,10 @@ fn resolveLazySymbols(wasm: *Wasm) !void {...@@ -1209,6 +1209,10 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
1209 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);1209 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
1210 }1210 }
1211 }1211 }
1212 if (wasm.undefs.fetchSwapRemove("__zig_lt_errors_len")) |kv| {
1213 const loc = try wasm.createSyntheticSymbol("__zig_lt_errors_len", .function);
1214 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
1215 }
1212}1216}
12131217
1214// Tries to find a global symbol by its name. Returns null when not found,1218// Tries to find a global symbol by its name. Returns null when not found,
...@@ -2185,6 +2189,46 @@ fn setupInitFunctions(wasm: *Wasm) !void {...@@ -2185,6 +2189,46 @@ fn setupInitFunctions(wasm: *Wasm) !void {
2185 std.sort.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan);2189 std.sort.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan);
2186}2190}
21872191
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.
2223
2224 // end function
2225 try writer.writeByte(std.wasm.opcode(.end));
2226 }
2227
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);
2230}
2231
2188/// Creates a function body for the `__wasm_call_ctors` symbol.2232/// Creates a function body for the `__wasm_call_ctors` symbol.
2189/// Loops over all constructors found in `init_funcs` and calls them2233/// Loops over all constructors found in `init_funcs` and calls them
2190/// respectively based on their priority which was sorted by `setupInitFunctions`.2234/// respectively based on their priority which was sorted by `setupInitFunctions`.
...@@ -3379,6 +3423,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -3379,6 +3423,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
3379 try wasm.setupInitMemoryFunction();3423 try wasm.setupInitMemoryFunction();
3380 try wasm.setupTLSRelocationsFunction();3424 try wasm.setupTLSRelocationsFunction();
3381 try wasm.initializeTLSFunction();3425 try wasm.initializeTLSFunction();
3426 try wasm.setupLtErrorsLenFunction();
3382 try wasm.setupExports();3427 try wasm.setupExports();
3383 try wasm.writeToFile(enabled_features, emit_features_count, arena);3428 try wasm.writeToFile(enabled_features, emit_features_count, arena);
3384}3429}