authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-04-16 15:10:45+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-04-22 21:16:23+02:00
logd4ceb12ae9d409dbd52c1f5c96312a1e6ad7d6bc
tree2431e92ce3e64e0b404a4d7e5a6cb833c298e342
parent27a41413f71b9b2d2fb15ea14a96983b4ba9bd2e
signature Commit is signed but in an unrecognized format.

wasm: implement `error_set_has_value`

This implements the safety check for error casts. The instruction generates a jump table with 2 possibilities. The operand is used as an index into the jump table. For cases where the value does not exist within the error set, it will generate a jump to the 'false' block. For cases where it does exist, it will generate a jump to the 'true' block. By calculating the highest and lowest value we can keep the jump table smaller, as it doesn't need to contain an index into the entire error set.

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

src/Module.zig+1-1
......@@ -6626,7 +6626,7 @@ pub fn backendSupportsFeature(mod: Module, feature: Feature) bool {
66266626 .safety_check_formatted => mod.comp.bin_file.options.use_llvm,
66276627 .error_return_trace => mod.comp.bin_file.options.use_llvm,
66286628 .is_named_enum_value => mod.comp.bin_file.options.use_llvm,
6629 .error_set_has_value => mod.comp.bin_file.options.use_llvm,
6629 .error_set_has_value => mod.comp.bin_file.options.use_llvm or mod.comp.bin_file.options.target.isWasm(),
66306630 .field_reordering => mod.comp.bin_file.options.use_llvm,
66316631 };
66326632}
src/arch/wasm/CodeGen.zig+84-1
......@@ -1946,6 +1946,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19461946 .ret_addr => func.airRetAddr(inst),
19471947 .tag_name => func.airTagName(inst),
19481948
1949 .error_set_has_value => func.airErrorSetHasValue(inst),
1950
19491951 .mul_sat,
19501952 .mod,
19511953 .assembly,
......@@ -1967,7 +1969,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19671969 .set_err_return_trace,
19681970 .save_err_return_trace_index,
19691971 .is_named_enum_value,
1970 .error_set_has_value,
19711972 .addrspace_cast,
19721973 .vector_store_elem,
19731974 .c_va_arg,
......@@ -6514,3 +6515,85 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
65146515 const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty}, slice_ty, func.target);
65156516 return func.bin_file.createFunction(func_name, func_type, &body_list, &relocs);
65166517}
6518
6519fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6520 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});
6522
6523 const operand = try func.resolveInst(ty_op.operand);
6524 const error_set_ty = func.air.getRefType(ty_op.ty);
6525 const result = try func.allocLocal(Type.bool);
6526
6527 const names = error_set_ty.errorSetNames();
6528 var values = try std.ArrayList(u32).initCapacity(func.gpa, names.len);
6529 defer values.deinit();
6530
6531 const module = func.bin_file.base.options.module.?;
6532 var lowest: ?u32 = null;
6533 var highest: ?u32 = null;
6534 for (names) |name| {
6535 const err_int = module.global_error_set.get(name).?;
6536 if (lowest) |*l| {
6537 if (err_int < l.*) {
6538 l.* = err_int;
6539 }
6540 } else {
6541 lowest = err_int;
6542 }
6543 if (highest) |*h| {
6544 if (err_int > h.*) {
6545 highest = err_int;
6546 }
6547 } else {
6548 highest = err_int;
6549 }
6550
6551 values.appendAssumeCapacity(err_int);
6552 }
6553
6554 // start block for 'true' branch
6555 try func.startBlock(.block, wasm.block_empty);
6556 // start block for 'false' branch
6557 try func.startBlock(.block, wasm.block_empty);
6558 // block for the jump table itself
6559 try func.startBlock(.block, wasm.block_empty);
6560
6561 // lower operand to determine jump table target
6562 try func.emitWValue(operand);
6563 try func.addImm32(@intCast(i32, lowest.?));
6564 try func.addTag(.i32_sub);
6565
6566 // Account for default branch so always add '1'
6567 const depth = @intCast(u32, highest.? - lowest.? + 1);
6568 const jump_table: Mir.JumpTable = .{ .length = depth };
6569 const table_extra_index = try func.addExtra(jump_table);
6570 try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
6571 try func.mir_extra.ensureUnusedCapacity(func.gpa, depth);
6572
6573 var value: u32 = lowest.?;
6574 while (value <= highest.?) : (value += 1) {
6575 const idx: u32 = blk: {
6576 for (values.items) |val| {
6577 if (val == value) break :blk 1;
6578 }
6579 break :blk 0;
6580 };
6581 func.mir_extra.appendAssumeCapacity(idx);
6582 }
6583 try func.endBlock();
6584
6585 // 'false' branch (i.e. error set does not have value
6586 // ensure we set local to 0 in case the local was re-used.
6587 try func.addImm32(0);
6588 try func.addLabel(.local_set, result.local.value);
6589 try func.addLabel(.br, 1);
6590 try func.endBlock();
6591
6592 // 'true' branch
6593 try func.addImm32(1);
6594 try func.addLabel(.local_set, result.local.value);
6595 try func.addLabel(.br, 0);
6596 try func.endBlock();
6597
6598 return func.finishAir(inst, result, &.{ty_op.operand});
6599}