diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 2048a456d7..cbe4b55861 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -29,6 +29,8 @@ const errUnionErrorOffset = codegen.errUnionErrorOffset; const WValue = union(enum) { /// May be referenced but is unused none: void, + /// The value lives on top of the stack + stack: void, /// Index of the local variable local: u32, /// An immediate 32bit value @@ -55,7 +57,7 @@ const WValue = union(enum) { /// In wasm function pointers are indexes into a function table, /// rather than an address in the data section. function_index: u32, - /// Offset from the bottom of the stack, with the offset + /// Offset from the bottom of the virtual stack, with the offset /// pointing to where the value lives. stack_offset: u32, @@ -71,6 +73,21 @@ const WValue = union(enum) { else => return 0, } } + + /// Promotes a `WValue` to a local when given value is on top of the stack. + /// When encountering a `local` or `stack_offset` this is essentially a no-op. + /// All other tags are illegal. + fn toLocal(self: WValue, gen: *Self, ty: Type) InnerError!WValue { + switch (self) { + .stack => { + const local = try gen.allocLocal(ty); + try gen.addLabel(.local_set, local.local); + return local; + }, + .local, .stack_offset => return self, + else => unreachable, + } + } }; /// Wasm ops, but without input/output/signedness information @@ -774,7 +791,7 @@ fn genBlockType(ty: Type, target: std.Target) u8 { /// Writes the bytecode depending on the given `WValue` in `val` fn emitWValue(self: *Self, value: WValue) InnerError!void { switch (value) { - .none => {}, // no-op + .none, .stack => {}, // no-op .local => |idx| try self.addLabel(.local_get, idx), .imm32 => |val| try self.addImm32(@bitCast(i32, val)), .imm64 => |val| try self.addImm64(val), @@ -1892,6 +1909,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { + assert(!(lhs != .stack and rhs == .stack)); switch (ty.zigTypeTag()) { .ErrorUnion => { const pl_ty = ty.errorUnionPayload(); @@ -2070,10 +2088,14 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const rhs = try self.resolveInst(bin_op.rhs); const ty = self.air.typeOf(bin_op.lhs); - return self.binOp(lhs, rhs, ty, op); + const stack_value = try self.binOp(lhs, rhs, ty, op); + return stack_value.toLocal(self, ty); } +/// Performs a binary operation on the given `WValue`'s +/// NOTE: THis leaves the value on top of the stack. fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { + assert(!(lhs != .stack and rhs == .stack)); if (isByRef(ty, self.target)) { if (ty.zigTypeTag() == .Int) { return self.binOpBigInt(lhs, rhs, ty, op); @@ -2099,10 +2121,7 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - // save the result in a temporary - const bin_local = try self.allocLocal(ty); - try self.addLabel(.local_set, bin_local.local); - return bin_local; + return WValue{ .stack = {} }; } fn binOpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue { @@ -2134,8 +2153,8 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr const rhs_high_bit = try self.load(rhs, Type.u64, 0); const rhs_low_bit = try self.load(rhs, Type.u64, 8); - const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); - const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); + const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64); + const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); const lt = if (op == .add) blk: { break :blk try self.cmp(high_op_res, rhs_high_bit, Type.u64, .lt); @@ -2143,7 +2162,7 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); } else unreachable; const tmp = try self.intcast(lt, Type.u32, Type.u64); - const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); + const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); try self.store(result, high_op_res, Type.u64, 0); try self.store(result, tmp_op, Type.u64, 8); @@ -2202,6 +2221,7 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { if (wasm_bits == bitsize) return operand; if (wasm_bits == 128) { + assert(operand != .stack); const msb = try self.load(operand, Type.u64, 0); const lsb = try self.load(operand, Type.u64, 8); @@ -2772,19 +2792,19 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { switch (wasm_bits) { 32 => { const bin_op = try self.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor); - return self.wrapOperand(bin_op, operand_ty); + return (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty); }, 64 => { const bin_op = try self.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor); - return self.wrapOperand(bin_op, operand_ty); + return (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty); }, 128 => { const result_ptr = try self.allocStack(operand_ty); const msb = try self.load(operand, Type.u64, 0); const lsb = try self.load(operand, Type.u64, 8); - const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); - const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); + const msb_xor = try (try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty); + const lsb_xor = try (try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty); try self.store(result_ptr, msb_xor, Type.u64, 0); try self.store(result_ptr, lsb_xor, Type.u64, 8); return result_ptr; @@ -3215,7 +3235,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value if (wanted.isSignedInt()) { - const shr = try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr); + const shr = try (try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr)).toLocal(self, Type.i64); try self.store(stack_ptr, shr, Type.u64, 8); } else { // Ensure memory of lsb is zero'd @@ -4320,7 +4340,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W break :blk try self.signAbsValue(rhs_op, lhs_ty); } else rhs_op; - const bin_op = try self.binOp(lhs, rhs, lhs_ty, op); + const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty); const result = if (wasm_bits != int_info.bits) blk: { break :blk try self.wrapOperand(bin_op, lhs_ty); } else bin_op; @@ -4330,7 +4350,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W if (wasm_bits == int_info.bits) { const cmp_zero = try self.cmp(rhs, zero, lhs_ty, cmp_op); const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt); - break :blk try self.binOp(cmp_zero, lt, Type.u32, .xor); // result of cmp_zero and lt is always 32bit + break :blk try (try self.binOp(cmp_zero, lt, Type.u32, .xor)).toLocal(self, Type.u32); // result of cmp_zero and lt is always 32bit } const abs = try self.signAbsValue(bin_op, lhs_ty); break :blk try self.cmp(abs, bin_op, lhs_ty, .neq); @@ -4360,8 +4380,8 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, const rhs_high_bit = try self.load(rhs, Type.u64, 0); const rhs_low_bit = try self.load(rhs, Type.u64, 8); - const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); - const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); + const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64); + const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); const lt = if (op == .add) blk: { break :blk try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt); @@ -4369,14 +4389,14 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); } else unreachable; const tmp = try self.intcast(lt, Type.u32, Type.u64); - const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); + const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); const overflow_bit = if (is_signed) blk: { - const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor); const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor); const to_wrap = if (op == .add) wrap: { break :wrap try self.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); } else xor_low; + const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor); const wrap = try self.binOp(to_wrap, xor_op, Type.u64, .@"and"); break :blk try self.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed } else blk: { @@ -4422,7 +4442,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits}); }; - const shl = try self.binOp(lhs, rhs, lhs_ty, .shl); + const shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty); const result = if (wasm_bits != int_info.bits) blk: { break :blk try self.wrapOperand(shl, lhs_ty); } else shl; @@ -4432,7 +4452,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr); break :blk try self.cmp(lhs, wrapped, lhs_ty, .neq); } else blk: { - const shr = try self.binOp(result, rhs, lhs_ty, .shr); + const shr = try (try self.binOp(result, rhs, lhs_ty, .shr)).toLocal(self, lhs_ty); break :blk try self.cmp(lhs, shr, lhs_ty, .neq); }; @@ -4478,7 +4498,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64; const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty); const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty); - const bin_op = try self.binOp(lhs_upcast, rhs_upcast, new_ty, .mul); + const bin_op = try (try self.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(self, new_ty); if (int_info.signedness == .unsigned) { const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); const wrap = try self.intcast(shr, new_ty, lhs_ty); @@ -4488,7 +4508,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { break :blk try self.intcast(bin_op, new_ty, lhs_ty); } else { const down_cast = try self.intcast(bin_op, new_ty, lhs_ty); - const shr = try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr); + const shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty); const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty); @@ -4500,14 +4520,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } else if (int_info.signedness == .signed) blk: { const lhs_abs = try self.signAbsValue(lhs, lhs_ty); const rhs_abs = try self.signAbsValue(rhs, lhs_ty); - const bin_op = try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul); + const bin_op = try (try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(self, lhs_ty); const mul_abs = try self.signAbsValue(bin_op, lhs_ty); const cmp_op = try self.cmp(mul_abs, bin_op, lhs_ty, .neq); try self.emitWValue(cmp_op); try self.addLabel(.local_set, overflow_bit.local); break :blk try self.wrapOperand(bin_op, lhs_ty); } else blk: { - const bin_op = try self.binOp(lhs, rhs, lhs_ty, .mul); + const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(self, lhs_ty); const shift_imm = if (wasm_bits == 32) WValue{ .imm32 = int_info.bits } else @@ -4587,7 +4607,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } const mul_result = try self.binOp(lhs, rhs, ty, .mul); - return self.binOp(mul_result, addend, ty, .add); + return (try self.binOp(mul_result, addend, ty, .add)).toLocal(self, ty); } fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -4663,16 +4683,16 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { 32 => { if (wasm_bits != int_info.bits) { const val: u32 = @as(u32, 1) << @intCast(u5, int_info.bits); - const bin_op = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or"); - try self.emitWValue(bin_op); + // leave value on the stack + _ = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or"); } else try self.emitWValue(operand); try self.addTag(.i32_ctz); }, 64 => { if (wasm_bits != int_info.bits) { const val: u64 = @as(u64, 1) << @intCast(u6, int_info.bits); - const bin_op = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or"); - try self.emitWValue(bin_op); + // leave value on the stack + _ = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or"); } else try self.emitWValue(operand); try self.addTag(.i64_ctz); try self.addTag(.i32_wrap_i64); @@ -4847,45 +4867,45 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { switch (int_info.bits) { 16 => { const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); - const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and"); - const shr_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr); + const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and")).toLocal(self, ty); + const shr_res = try (try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); const res = if (int_info.signedness == .signed) blk: { break :blk try self.wrapOperand(shr_res, Type.u8); } else shr_res; - return self.binOp(lhs, res, ty, .@"or"); + return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty); }, 24 => { const msb = try self.wrapOperand(operand, Type.u16); const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr); const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl); - const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and"); - const shr_res = try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr); + const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and")).toLocal(self, Type.u16); + const shr_res = try (try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); const res = if (int_info.signedness == .signed) blk: { break :blk try self.wrapOperand(shr_res, Type.u8); } else shr_res; const lhs_tmp = try self.binOp(lhs, res, ty, .@"or"); - const lhs_result = try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr); + const lhs_result = try (try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); const rhs_wrap = try self.wrapOperand(msb, Type.u8); - const rhs_result = try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl); + const rhs_result = try (try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl)).toLocal(self, ty); const tmp = try self.binOp(lhs_result, rhs_result, ty, .@"or"); - return self.binOp(tmp, lsb, ty, .@"or"); + return (try self.binOp(tmp, lsb, ty, .@"or")).toLocal(self, ty); }, 32 => { const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); - const lhs = try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and"); + const lhs = try (try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(self, ty); const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr); - const rhs = try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and"); - const tmp_or = try self.binOp(lhs, rhs, ty, .@"or"); + const rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty); + const tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty); - const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); - const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr); + const shr = try (try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr)).toLocal(self, ty); const res = if (int_info.signedness == .signed) blk: { break :blk try self.wrapOperand(shr, Type.u16); } else shr; - return self.binOp(shl, res, ty, .@"or"); + const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); + return (try self.binOp(shl, res, ty, .@"or")).toLocal(self, ty); }, else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}), } @@ -4902,7 +4922,7 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (ty.isSignedInt()) { return self.divSigned(lhs, rhs, ty); } - return self.binOp(lhs, rhs, ty, .div); + return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty); } fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -4914,7 +4934,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const rhs = try self.resolveInst(bin_op.rhs); if (ty.isUnsignedInt()) { - return self.binOp(lhs, rhs, ty, .div); + return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty); } else if (ty.isSignedInt()) { const int_bits = ty.intInfo(self.target).bits; const wasm_bits = toWasmBits(int_bits) orelse { @@ -4927,9 +4947,6 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { break :blk try self.signAbsValue(rhs, ty); } else rhs; - const div_result = try self.binOp(lhs_res, rhs_res, ty, .div); - const rem_result = try self.binOp(lhs_res, rhs_res, ty, .rem); - const zero = switch (wasm_bits) { 32 => WValue{ .imm32 = 0 }, 64 => WValue{ .imm64 = 0 }, @@ -4938,7 +4955,10 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const lhs_less_than_zero = try self.cmp(lhs_res, zero, ty, .lt); const rhs_less_than_zero = try self.cmp(rhs_res, zero, ty, .lt); - try self.emitWValue(div_result); + const div_result = try self.allocLocal(ty); + // leave on stack + _ = try self.binOp(lhs_res, rhs_res, ty, .div); + try self.addLabel(.local_tee, div_result.local); try self.emitWValue(lhs_less_than_zero); try self.emitWValue(rhs_less_than_zero); switch (wasm_bits) { @@ -4953,7 +4973,8 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { else => unreachable, } try self.emitWValue(div_result); - try self.emitWValue(rem_result); + // leave value on the stack + _ = try self.binOp(lhs_res, rhs_res, ty, .rem); try self.addTag(.select); } else { const float_bits = ty.floatBits(self.target); @@ -5110,7 +5131,7 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { } const wasm_bits = toWasmBits(int_info.bits).?; - const bin_result = try self.binOp(lhs, rhs, ty, op); + const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty); if (wasm_bits != int_info.bits and op == .add) { const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1); const imm_val = switch (wasm_bits) { @@ -5161,7 +5182,7 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op else => unreachable, }; - const bin_result = try self.binOp(lhs, rhs, ty, op); + const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty); if (!is_wasm_bits) { const cmp_result_lt = try self.cmp(bin_result, max_wvalue, ty, .lt); try self.emitWValue(bin_result); @@ -5185,14 +5206,14 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op }; const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt); const cmp_zero_result = try self.cmp(rhs, zero, ty, if (op == .add) .lt else .gt); - const xor = try self.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor. const cmp_bin_zero_result = try self.cmp(bin_result, zero, ty, .lt); try self.emitWValue(max_wvalue); try self.emitWValue(min_wvalue); try self.emitWValue(cmp_bin_zero_result); try self.addTag(.select); try self.emitWValue(bin_result); - try self.emitWValue(xor); + // leave on stack + _ = try self.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor. try self.addTag(.select); try self.addLabel(.local_set, bin_result.local); // re-use local return bin_result; @@ -5216,8 +5237,8 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const result = try self.allocLocal(ty); if (wasm_bits == int_info.bits) { - const shl = try self.binOp(lhs, rhs, ty, .shl); - const shr = try self.binOp(shl, rhs, ty, .shr); + const shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty); + const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); const cmp_result = try self.cmp(lhs, shr, ty, .neq); switch (wasm_bits) { @@ -5258,9 +5279,9 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { else => unreachable, }; - const shl_res = try self.binOp(lhs, shift_value, ty, .shl); - const shl = try self.binOp(shl_res, rhs, ty, .shl); - const shr = try self.binOp(shl, rhs, ty, .shr); + const shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty); + const shl = try (try self.binOp(shl_res, rhs, ty, .shl)).toLocal(self, ty); + const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); const cmp_result = try self.cmp(shl_res, shr, ty, .neq); switch (wasm_bits) { @@ -5294,7 +5315,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.emitWValue(cmp_result); try self.addTag(.select); try self.addLabel(.local_set, result.local); - const shift_result = try self.binOp(result, shift_value, ty, .shr); + const shift_result = try (try self.binOp(result, shift_value, ty, .shr)).toLocal(self, ty); if (is_signed) { return self.wrapOperand(shift_result, ty); }