authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-18 15:02:00-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:35-08:00
log2d899e9a9f0864385939955902e0ba5f322176fc
treee2bb5f12a7056e36576be6e07694f7c945889a10
parent06303448942c387aa4b9d564bcc918ad27e3940a

wasm codegen: fix wrong union field for locals


1 files changed, 47 insertions(+), 51 deletions(-)

src/arch/wasm/CodeGen.zig+47-51
......@@ -190,7 +190,7 @@ const WValue = union(enum) {
190190 switch (value) {
191191 .stack => {
192192 const new_local = try gen.allocLocal(ty);
193 try gen.addLabel(.local_set, new_local.local.value);
193 try gen.addLocal(.local_set, new_local.local.value);
194194 return new_local;
195195 },
196196 .local, .stack_offset => return value,
......@@ -237,9 +237,6 @@ const Op = enum {
237237 call_indirect,
238238 drop,
239239 select,
240 local_get,
241 local_set,
242 local_tee,
243240 global_get,
244241 global_set,
245242 load,
......@@ -318,9 +315,6 @@ fn buildOpcode(args: OpcodeBuildArguments) std.wasm.Opcode {
318315 .call_indirect => unreachable,
319316 .drop => unreachable,
320317 .select => unreachable,
321 .local_get => unreachable,
322 .local_set => unreachable,
323 .local_tee => unreachable,
324318 .global_get => unreachable,
325319 .global_set => unreachable,
326320
......@@ -681,13 +675,11 @@ test "Wasm - buildOpcode" {
681675 // Make sure buildOpcode is referenced, and test some examples
682676 const i32_const = buildOpcode(.{ .op = .@"const", .valtype1 = .i32 });
683677 const end = buildOpcode(.{ .op = .end });
684 const local_get = buildOpcode(.{ .op = .local_get });
685678 const i64_extend32_s = buildOpcode(.{ .op = .extend, .valtype1 = .i64, .width = 32, .signedness = .signed });
686679 const f64_reinterpret_i64 = buildOpcode(.{ .op = .reinterpret, .valtype1 = .f64, .valtype2 = .i64 });
687680
688681 try testing.expectEqual(@as(std.wasm.Opcode, .i32_const), i32_const);
689682 try testing.expectEqual(@as(std.wasm.Opcode, .end), end);
690 try testing.expectEqual(@as(std.wasm.Opcode, .local_get), local_get);
691683 try testing.expectEqual(@as(std.wasm.Opcode, .i64_extend32_s), i64_extend32_s);
692684 try testing.expectEqual(@as(std.wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64);
693685}
......@@ -876,6 +868,10 @@ fn addLabel(cg: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void
876868 try cg.addInst(.{ .tag = tag, .data = .{ .label = label } });
877869}
878870
871fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void {
872 try cg.addInst(.{ .tag = tag, .data = .{ .local = local } });
873}
874
879875/// Accepts an unsigned 32bit integer rather than a signed integer to
880876/// prevent us from having to bitcast multiple times as most values
881877/// within codegen are represented as unsigned rather than signed.
......@@ -1015,7 +1011,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
10151011 switch (value) {
10161012 .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?)
10171013 .none, .stack => {}, // no-op
1018 .local => |idx| try cg.addLabel(.local_get, idx.value),
1014 .local => |idx| try cg.addLocal(.local_get, idx.value),
10191015 .imm32 => |val| try cg.addImm32(val),
10201016 .imm64 => |val| try cg.addImm64(val),
10211017 .imm128 => |val| try cg.addImm128(val),
......@@ -1063,7 +1059,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
10631059 });
10641060 }
10651061 },
1066 .stack_offset => try cg.addLabel(.local_get, cg.bottom_stack_value.local.value), // caller must ensure to address the offset
1062 .stack_offset => try cg.addLocal(.local_get, cg.bottom_stack_value.local.value), // caller must ensure to address the offset
10671063 }
10681064}
10691065
......@@ -1624,7 +1620,7 @@ fn memcpy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
16241620 .wasm32 => try cg.addImm32(0),
16251621 .wasm64 => try cg.addImm64(0),
16261622 }
1627 try cg.addLabel(.local_set, offset.local.value);
1623 try cg.addLocal(.local_set, offset.local.value);
16281624
16291625 // outer block to jump to when loop is done
16301626 try cg.startBlock(.block, std.wasm.block_empty);
......@@ -1682,7 +1678,7 @@ fn memcpy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
16821678 try cg.addTag(.i64_add);
16831679 },
16841680 }
1685 try cg.addLabel(.local_set, offset.local.value);
1681 try cg.addLocal(.local_set, offset.local.value);
16861682 try cg.addLabel(.br, 0); // jump to start of loop
16871683 }
16881684 try cg.endBlock(); // close off loop block
......@@ -1801,7 +1797,7 @@ fn buildPointerOffset(cg: *CodeGen, ptr_value: WValue, offset: u64, action: enum
18011797 },
18021798 }
18031799 }
1804 try cg.addLabel(.local_set, result_ptr.local.value);
1800 try cg.addLocal(.local_set, result_ptr.local.value);
18051801 return result_ptr;
18061802}
18071803
......@@ -2228,14 +2224,14 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie
22282224 // TODO: Make this less fragile and optimize
22292225 } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_watc and ret_ty.zigTypeTag(zcu) == .@"struct" or ret_ty.zigTypeTag(zcu) == .@"union") {
22302226 const result_local = try cg.allocLocal(ret_ty);
2231 try cg.addLabel(.local_set, result_local.local.value);
2227 try cg.addLocal(.local_set, result_local.local.value);
22322228 const scalar_type = abi.scalarType(ret_ty, zcu);
22332229 const result = try cg.allocStack(scalar_type);
22342230 try cg.store(result, result_local, scalar_type, 0);
22352231 break :result_value result;
22362232 } else {
22372233 const result_local = try cg.allocLocal(ret_ty);
2238 try cg.addLabel(.local_set, result_local.local.value);
2234 try cg.addLocal(.local_set, result_local.local.value);
22392235 break :result_value result_local;
22402236 }
22412237 };
......@@ -2817,7 +2813,7 @@ fn airAbs(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
28172813
28182814 var tmp = try cg.allocLocal(ty);
28192815 defer tmp.free(cg);
2820 try cg.addLabel(.local_tee, tmp.local.value);
2816 try cg.addLocal(.local_tee, tmp.local.value);
28212817
28222818 try cg.emitWValue(operand);
28232819 try cg.addTag(.i32_xor);
......@@ -2833,7 +2829,7 @@ fn airAbs(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
28332829
28342830 var tmp = try cg.allocLocal(ty);
28352831 defer tmp.free(cg);
2836 try cg.addLabel(.local_tee, tmp.local.value);
2832 try cg.addLocal(.local_tee, tmp.local.value);
28372833
28382834 try cg.emitWValue(operand);
28392835 try cg.addTag(.i64_xor);
......@@ -2852,7 +2848,7 @@ fn airAbs(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
28522848
28532849 var tmp = try cg.allocLocal(Type.u64);
28542850 defer tmp.free(cg);
2855 try cg.addLabel(.local_tee, tmp.local.value);
2851 try cg.addLocal(.local_tee, tmp.local.value);
28562852 try cg.store(.stack, .stack, Type.u64, mask.offset() + 0);
28572853 try cg.emitWValue(tmp);
28582854 try cg.store(.stack, .stack, Type.u64, mask.offset() + 8);
......@@ -3590,7 +3586,7 @@ fn airBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
35903586 try cg.lowerToStack(operand);
35913587
35923588 if (block.value != .none) {
3593 try cg.addLabel(.local_set, block.value.local.value);
3589 try cg.addLocal(.local_set, block.value.local.value);
35943590 }
35953591 }
35963592
......@@ -3625,7 +3621,7 @@ fn airNot(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
36253621 try cg.emitWValue(operand);
36263622 try cg.addTag(.i32_eqz);
36273623 const not_tmp = try cg.allocLocal(operand_ty);
3628 try cg.addLabel(.local_set, not_tmp.local.value);
3624 try cg.addLocal(.local_set, not_tmp.local.value);
36293625 break :result not_tmp;
36303626 } else {
36313627 const int_info = operand_ty.intInfo(zcu);
......@@ -4788,7 +4784,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
47884784 try cg.addTag(.i64_mul);
47894785 },
47904786 }
4791 try cg.addLabel(.local_set, new_len.local.value);
4787 try cg.addLocal(.local_set, new_len.local.value);
47924788 break :blk new_len;
47934789 } else len,
47944790 };
......@@ -4805,7 +4801,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
48054801 .wasm32 => try cg.addTag(.i32_add),
48064802 .wasm64 => try cg.addTag(.i64_add),
48074803 }
4808 try cg.addLabel(.local_set, end_ptr.local.value);
4804 try cg.addLocal(.local_set, end_ptr.local.value);
48094805
48104806 // outer block to jump to when loop is done
48114807 try cg.startBlock(.block, std.wasm.block_empty);
......@@ -4835,7 +4831,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
48354831 try cg.addTag(.i64_add);
48364832 },
48374833 }
4838 try cg.addLabel(.local_set, new_ptr.local.value);
4834 try cg.addLocal(.local_set, new_ptr.local.value);
48394835
48404836 // end of loop
48414837 try cg.addLabel(.br, 0); // jump to start of loop
......@@ -5216,7 +5212,7 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52165212 try cg.addImm32(0)
52175213 else
52185214 try cg.addImm64(0);
5219 try cg.addLabel(.local_set, result.local.value);
5215 try cg.addLocal(.local_set, result.local.value);
52205216
52215217 var current_bit: u16 = 0;
52225218 for (elements, 0..) |elem, elem_index| {
......@@ -5243,7 +5239,7 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52435239 } else extended_val;
52445240 // we ignore the result as we keep it on the stack to assign it directly to `result`
52455241 _ = try cg.binOp(.stack, shifted, backing_type, .@"or");
5246 try cg.addLabel(.local_set, result.local.value);
5242 try cg.addLocal(.local_set, result.local.value);
52475243 current_bit += value_bit_size;
52485244 }
52495245 break :result_value result;
......@@ -5399,7 +5395,7 @@ fn cmpOptionals(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: st
53995395 try cg.addLabel(.br_if, 0);
54005396
54015397 try cg.addImm32(1);
5402 try cg.addLabel(.local_set, result.local.value);
5398 try cg.addLocal(.local_set, result.local.value);
54035399 try cg.endBlock();
54045400
54055401 try cg.emitWValue(result);
......@@ -5642,10 +5638,10 @@ fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56425638
56435639 const result = if (field_offset != 0) result: {
56445640 const base = try cg.buildPointerOffset(field_ptr, 0, .new);
5645 try cg.addLabel(.local_get, base.local.value);
5641 try cg.addLocal(.local_get, base.local.value);
56465642 try cg.addImm32(@intCast(field_offset));
56475643 try cg.addTag(.i32_sub);
5648 try cg.addLabel(.local_set, base.local.value);
5644 try cg.addLocal(.local_set, base.local.value);
56495645 break :result base;
56505646 } else cg.reuseOperand(extra.field_ptr, field_ptr);
56515647
......@@ -5676,7 +5672,7 @@ fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56765672 try cg.emitWValue(slice_len);
56775673 try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });
56785674 try cg.addTag(.i32_mul);
5679 try cg.addLabel(.local_set, slice_len.local.value);
5675 try cg.addLocal(.local_set, slice_len.local.value);
56805676 }
56815677 break :blk slice_len;
56825678 },
......@@ -5827,7 +5823,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58275823 } else {
58285824 var tmp = try cg.allocLocal(Type.u64);
58295825 defer tmp.free(cg);
5830 try cg.addLabel(.local_tee, tmp.local.value);
5826 try cg.addLocal(.local_tee, tmp.local.value);
58315827 try cg.emitWValue(.{ .imm64 = 128 - bits });
58325828 if (ty.isSignedInt(zcu)) {
58335829 try cg.addTag(.i64_shr_s);
......@@ -5835,7 +5831,7 @@ fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58355831 try cg.addTag(.i64_shr_u);
58365832 }
58375833 try cg.store(.stack, .stack, Type.u64, result.offset() + 8);
5838 try cg.addLabel(.local_get, tmp.local.value);
5834 try cg.addLocal(.local_get, tmp.local.value);
58395835 try cg.emitWValue(.{ .imm64 = bits - 64 });
58405836 try cg.addTag(.i64_shl);
58415837 try cg.addTag(.i64_or);
......@@ -6042,7 +6038,7 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
60426038 const res = try (try cg.trunc(bin_op, ty, new_ty)).toLocal(cg, ty);
60436039 const res_upcast = try cg.intcast(res, ty, new_ty);
60446040 _ = try cg.cmp(res_upcast, bin_op, new_ty, .neq);
6045 try cg.addLabel(.local_set, overflow_bit.local.value);
6041 try cg.addLocal(.local_set, overflow_bit.local.value);
60466042 break :blk res;
60476043 } else if (wasm_bits == 64) blk: {
60486044 const new_ty = if (int_info.signedness == .signed) Type.i128 else Type.u128;
......@@ -6052,7 +6048,7 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
60526048 const res = try (try cg.trunc(bin_op, ty, new_ty)).toLocal(cg, ty);
60536049 const res_upcast = try cg.intcast(res, ty, new_ty);
60546050 _ = try cg.cmp(res_upcast, bin_op, new_ty, .neq);
6055 try cg.addLabel(.local_set, overflow_bit.local.value);
6051 try cg.addLocal(.local_set, overflow_bit.local.value);
60566052 break :blk res;
60576053 } else if (int_info.bits == 128 and int_info.signedness == .unsigned) blk: {
60586054 var lhs_lsb = try (try cg.load(lhs, Type.u64, 0)).toLocal(cg, Type.u64);
......@@ -6105,7 +6101,7 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61056101
61066102 // result for overflow bit
61076103 _ = try cg.binOp(cond_2, add_overflow, Type.bool, .@"or");
6108 try cg.addLabel(.local_set, overflow_bit.local.value);
6104 try cg.addLocal(.local_set, overflow_bit.local.value);
61096105
61106106 const tmp_result = try cg.allocStack(Type.u128);
61116107 try cg.emitWValue(tmp_result);
......@@ -6122,7 +6118,7 @@ fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61226118 &.{ lhs, rhs, overflow_ret },
61236119 );
61246120 _ = try cg.load(overflow_ret, Type.i32, 0);
6125 try cg.addLabel(.local_set, overflow_bit.local.value);
6121 try cg.addLocal(.local_set, overflow_bit.local.value);
61266122 break :blk res;
61276123 } else return cg.fail("TODO: @mulWithOverflow for {}", .{ty.fmt(pt)});
61286124 var bin_op_local = try mul.toLocal(cg, ty);
......@@ -6543,7 +6539,7 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
65436539 // tee leaves the value on the stack and stores it in a local.
65446540 const quotient = try cg.allocLocal(ty);
65456541 _ = try cg.binOp(lhs, rhs, ty, .div);
6546 try cg.addLabel(.local_tee, quotient.local.value);
6542 try cg.addLocal(.local_tee, quotient.local.value);
65476543
65486544 // select takes a 32 bit value as the condition, so in the 64 bit case we use eqz to narrow
65496545 // the 64 bit value we want to use as the condition to 32 bits.
......@@ -6704,7 +6700,7 @@ fn airSatMul(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
67046700
67056701 var tmp = try cg.allocLocal(upcast_ty);
67066702 defer tmp.free(cg);
6707 try cg.addLabel(.local_set, tmp.local.value);
6703 try cg.addLocal(.local_set, tmp.local.value);
67086704
67096705 const imm_min: WValue = .{ .imm64 = ~@as(u64, 0) << @intCast(int_info.bits - 1) };
67106706 try cg.emitWValue(tmp);
......@@ -6850,13 +6846,13 @@ fn signedSat(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErro
68506846 try cg.emitWValue(max_wvalue);
68516847 _ = try cg.cmp(bin_result, max_wvalue, ext_ty, .lt);
68526848 try cg.addTag(.select);
6853 try cg.addLabel(.local_set, bin_result.local.value); // re-use local
6849 try cg.addLocal(.local_set, bin_result.local.value); // re-use local
68546850
68556851 try cg.emitWValue(bin_result);
68566852 try cg.emitWValue(min_wvalue);
68576853 _ = try cg.cmp(bin_result, min_wvalue, ext_ty, .gt);
68586854 try cg.addTag(.select);
6859 try cg.addLabel(.local_set, bin_result.local.value); // re-use local
6855 try cg.addLocal(.local_set, bin_result.local.value); // re-use local
68606856 return (try cg.wrapOperand(bin_result, ty)).toLocal(cg, ty);
68616857 } else {
68626858 const zero: WValue = switch (wasm_bits) {
......@@ -6874,7 +6870,7 @@ fn signedSat(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErro
68746870 const cmp_bin_result = try cg.cmp(bin_result, lhs, ty, .lt);
68756871 _ = try cg.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor.
68766872 try cg.addTag(.select);
6877 try cg.addLabel(.local_set, bin_result.local.value); // re-use local
6873 try cg.addLocal(.local_set, bin_result.local.value); // re-use local
68786874 return bin_result;
68796875 }
68806876}
......@@ -6928,7 +6924,7 @@ fn airShlSat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
69286924 try cg.emitWValue(shl);
69296925 _ = try cg.cmp(lhs, shr, ty, .neq);
69306926 try cg.addTag(.select);
6931 try cg.addLabel(.local_set, result.local.value);
6927 try cg.addLocal(.local_set, result.local.value);
69326928 } else {
69336929 const shift_size = wasm_bits - int_info.bits;
69346930 const shift_value: WValue = switch (wasm_bits) {
......@@ -6973,12 +6969,12 @@ fn airShlSat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
69736969 try cg.emitWValue(shl);
69746970 _ = try cg.cmp(shl_res, shr, ext_ty, .neq);
69756971 try cg.addTag(.select);
6976 try cg.addLabel(.local_set, result.local.value);
6972 try cg.addLocal(.local_set, result.local.value);
69776973 var shift_result = try cg.binOp(result, shift_value, ext_ty, .shr);
69786974 if (is_signed) {
69796975 shift_result = try cg.wrapOperand(shift_result, ty);
69806976 }
6981 try cg.addLabel(.local_set, result.local.value);
6977 try cg.addLocal(.local_set, result.local.value);
69826978 }
69836979
69846980 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
......@@ -7114,13 +7110,13 @@ fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71147110 // 'false' branch (i.e. error set does not have value
71157111 // ensure we set local to 0 in case the local was re-used.
71167112 try cg.addImm32(0);
7117 try cg.addLabel(.local_set, result.local.value);
7113 try cg.addLocal(.local_set, result.local.value);
71187114 try cg.addLabel(.br, 1);
71197115 try cg.endBlock();
71207116
71217117 // 'true' branch
71227118 try cg.addImm32(1);
7123 try cg.addLabel(.local_set, result.local.value);
7119 try cg.addLocal(.local_set, result.local.value);
71247120 try cg.addLabel(.br, 0);
71257121 try cg.endBlock();
71267122
......@@ -7161,9 +7157,9 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71617157 .offset = ptr_operand.offset(),
71627158 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
71637159 });
7164 try cg.addLabel(.local_tee, val_local.local.value);
7160 try cg.addLocal(.local_tee, val_local.local.value);
71657161 _ = try cg.cmp(.stack, expected_val, ty, .eq);
7166 try cg.addLabel(.local_set, cmp_result.local.value);
7162 try cg.addLocal(.local_set, cmp_result.local.value);
71677163 break :val val_local;
71687164 } else val: {
71697165 if (ty.abiSize(zcu) > 8) {
......@@ -7175,7 +7171,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71757171 try cg.lowerToStack(new_val);
71767172 try cg.emitWValue(ptr_val);
71777173 _ = try cg.cmp(ptr_val, expected_val, ty, .eq);
7178 try cg.addLabel(.local_tee, cmp_result.local.value);
7174 try cg.addLocal(.local_tee, cmp_result.local.value);
71797175 try cg.addTag(.select);
71807176 try cg.store(.stack, .stack, ty, 0);
71817177
......@@ -7285,11 +7281,11 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
72857281 },
72867282 );
72877283 const select_res = try cg.allocLocal(ty);
7288 try cg.addLabel(.local_tee, select_res.local.value);
7284 try cg.addLocal(.local_tee, select_res.local.value);
72897285 _ = try cg.cmp(.stack, value, ty, .neq); // leave on stack so we can use it for br_if
72907286
72917287 try cg.emitWValue(select_res);
7292 try cg.addLabel(.local_set, value.local.value);
7288 try cg.addLocal(.local_set, value.local.value);
72937289
72947290 try cg.addLabel(.br_if, 0);
72957291 try cg.endBlock();