authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-07-24 15:48:12+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-08-11 11:08:00+02:00
logcc6f2b67c68ece7577e27e23846670d6566ad5f2
treedb8feb8b61013657965d2de7a5383947434425fc
parent7e6dbd63989ed4f62b9d12fb03119b0bec1bda84
signaturelock-open Commit is signed but in an unrecognized format.

wasm: `binOp` leave value on stack

Rather than always creating a new local and storing the result of a binary operation into said local, we now leave it on top of the stack. This allows for better codegen as we need less instructions, as well as less total amount of locals.

1 files changed, 84 insertions(+), 63 deletions(-)

src/arch/wasm/CodeGen.zig+84-63
......@@ -29,6 +29,8 @@ const errUnionErrorOffset = codegen.errUnionErrorOffset;
2929const WValue = union(enum) {
3030 /// May be referenced but is unused
3131 none: void,
32 /// The value lives on top of the stack
33 stack: void,
3234 /// Index of the local variable
3335 local: u32,
3436 /// An immediate 32bit value
......@@ -55,7 +57,7 @@ const WValue = union(enum) {
5557 /// In wasm function pointers are indexes into a function table,
5658 /// rather than an address in the data section.
5759 function_index: u32,
58 /// Offset from the bottom of the stack, with the offset
60 /// Offset from the bottom of the virtual stack, with the offset
5961 /// pointing to where the value lives.
6062 stack_offset: u32,
6163
......@@ -71,6 +73,21 @@ const WValue = union(enum) {
7173 else => return 0,
7274 }
7375 }
76
77 /// Promotes a `WValue` to a local when given value is on top of the stack.
78 /// When encountering a `local` or `stack_offset` this is essentially a no-op.
79 /// All other tags are illegal.
80 fn toLocal(self: WValue, gen: *Self, ty: Type) InnerError!WValue {
81 switch (self) {
82 .stack => {
83 const local = try gen.allocLocal(ty);
84 try gen.addLabel(.local_set, local.local);
85 return local;
86 },
87 .local, .stack_offset => return self,
88 else => unreachable,
89 }
90 }
7491};
7592
7693/// Wasm ops, but without input/output/signedness information
......@@ -774,7 +791,7 @@ fn genBlockType(ty: Type, target: std.Target) u8 {
774791/// Writes the bytecode depending on the given `WValue` in `val`
775792fn emitWValue(self: *Self, value: WValue) InnerError!void {
776793 switch (value) {
777 .none => {}, // no-op
794 .none, .stack => {}, // no-op
778795 .local => |idx| try self.addLabel(.local_get, idx),
779796 .imm32 => |val| try self.addImm32(@bitCast(i32, val)),
780797 .imm64 => |val| try self.addImm64(val),
......@@ -1892,6 +1909,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
18921909}
18931910
18941911fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
1912 assert(!(lhs != .stack and rhs == .stack));
18951913 switch (ty.zigTypeTag()) {
18961914 .ErrorUnion => {
18971915 const pl_ty = ty.errorUnionPayload();
......@@ -2070,10 +2088,14 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
20702088 const rhs = try self.resolveInst(bin_op.rhs);
20712089 const ty = self.air.typeOf(bin_op.lhs);
20722090
2073 return self.binOp(lhs, rhs, ty, op);
2091 const stack_value = try self.binOp(lhs, rhs, ty, op);
2092 return stack_value.toLocal(self, ty);
20742093}
20752094
2095/// Performs a binary operation on the given `WValue`'s
2096/// NOTE: THis leaves the value on top of the stack.
20762097fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2098 assert(!(lhs != .stack and rhs == .stack));
20772099 if (isByRef(ty, self.target)) {
20782100 if (ty.zigTypeTag() == .Int) {
20792101 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
20992121
21002122 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
21012123
2102 // save the result in a temporary
2103 const bin_local = try self.allocLocal(ty);
2104 try self.addLabel(.local_set, bin_local.local);
2105 return bin_local;
2124 return WValue{ .stack = {} };
21062125}
21072126
21082127fn 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
21342153 const rhs_high_bit = try self.load(rhs, Type.u64, 0);
21352154 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
21362155
2137 const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);
2138 const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op);
2156 const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64);
2157 const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
21392158
21402159 const lt = if (op == .add) blk: {
21412160 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
21432162 break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);
21442163 } else unreachable;
21452164 const tmp = try self.intcast(lt, Type.u32, Type.u64);
2146 const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op);
2165 const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64);
21472166
21482167 try self.store(result, high_op_res, Type.u64, 0);
21492168 try self.store(result, tmp_op, Type.u64, 8);
......@@ -2202,6 +2221,7 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
22022221 if (wasm_bits == bitsize) return operand;
22032222
22042223 if (wasm_bits == 128) {
2224 assert(operand != .stack);
22052225 const msb = try self.load(operand, Type.u64, 0);
22062226 const lsb = try self.load(operand, Type.u64, 8);
22072227
......@@ -2772,19 +2792,19 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27722792 switch (wasm_bits) {
27732793 32 => {
27742794 const bin_op = try self.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor);
2775 return self.wrapOperand(bin_op, operand_ty);
2795 return (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty);
27762796 },
27772797 64 => {
27782798 const bin_op = try self.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor);
2779 return self.wrapOperand(bin_op, operand_ty);
2799 return (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty);
27802800 },
27812801 128 => {
27822802 const result_ptr = try self.allocStack(operand_ty);
27832803 const msb = try self.load(operand, Type.u64, 0);
27842804 const lsb = try self.load(operand, Type.u64, 8);
27852805
2786 const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
2787 const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
2806 const msb_xor = try (try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty);
2807 const lsb_xor = try (try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty);
27882808 try self.store(result_ptr, msb_xor, Type.u64, 0);
27892809 try self.store(result_ptr, lsb_xor, Type.u64, 8);
27902810 return result_ptr;
......@@ -3215,7 +3235,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W
32153235
32163236 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value
32173237 if (wanted.isSignedInt()) {
3218 const shr = try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
3238 const shr = try (try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr)).toLocal(self, Type.i64);
32193239 try self.store(stack_ptr, shr, Type.u64, 8);
32203240 } else {
32213241 // Ensure memory of lsb is zero'd
......@@ -4320,7 +4340,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
43204340 break :blk try self.signAbsValue(rhs_op, lhs_ty);
43214341 } else rhs_op;
43224342
4323 const bin_op = try self.binOp(lhs, rhs, lhs_ty, op);
4343 const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty);
43244344 const result = if (wasm_bits != int_info.bits) blk: {
43254345 break :blk try self.wrapOperand(bin_op, lhs_ty);
43264346 } else bin_op;
......@@ -4330,7 +4350,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
43304350 if (wasm_bits == int_info.bits) {
43314351 const cmp_zero = try self.cmp(rhs, zero, lhs_ty, cmp_op);
43324352 const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt);
4333 break :blk try self.binOp(cmp_zero, lt, Type.u32, .xor); // result of cmp_zero and lt is always 32bit
4353 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
43344354 }
43354355 const abs = try self.signAbsValue(bin_op, lhs_ty);
43364356 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,
43604380 const rhs_high_bit = try self.load(rhs, Type.u64, 0);
43614381 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
43624382
4363 const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);
4364 const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op);
4383 const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64);
4384 const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
43654385
43664386 const lt = if (op == .add) blk: {
43674387 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,
43694389 break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);
43704390 } else unreachable;
43714391 const tmp = try self.intcast(lt, Type.u32, Type.u64);
4372 const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op);
4392 const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64);
43734393
43744394 const overflow_bit = if (is_signed) blk: {
4375 const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor);
43764395 const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
43774396 const to_wrap = if (op == .add) wrap: {
43784397 break :wrap try self.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
43794398 } else xor_low;
4399 const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor);
43804400 const wrap = try self.binOp(to_wrap, xor_op, Type.u64, .@"and");
43814401 break :blk try self.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed
43824402 } else blk: {
......@@ -4422,7 +4442,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
44224442 return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});
44234443 };
44244444
4425 const shl = try self.binOp(lhs, rhs, lhs_ty, .shl);
4445 const shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty);
44264446 const result = if (wasm_bits != int_info.bits) blk: {
44274447 break :blk try self.wrapOperand(shl, lhs_ty);
44284448 } else shl;
......@@ -4432,7 +4452,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
44324452 const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr);
44334453 break :blk try self.cmp(lhs, wrapped, lhs_ty, .neq);
44344454 } else blk: {
4435 const shr = try self.binOp(result, rhs, lhs_ty, .shr);
4455 const shr = try (try self.binOp(result, rhs, lhs_ty, .shr)).toLocal(self, lhs_ty);
44364456 break :blk try self.cmp(lhs, shr, lhs_ty, .neq);
44374457 };
44384458
......@@ -4478,7 +4498,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
44784498 const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64;
44794499 const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty);
44804500 const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty);
4481 const bin_op = try self.binOp(lhs_upcast, rhs_upcast, new_ty, .mul);
4501 const bin_op = try (try self.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(self, new_ty);
44824502 if (int_info.signedness == .unsigned) {
44834503 const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
44844504 const wrap = try self.intcast(shr, new_ty, lhs_ty);
......@@ -4488,7 +4508,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
44884508 break :blk try self.intcast(bin_op, new_ty, lhs_ty);
44894509 } else {
44904510 const down_cast = try self.intcast(bin_op, new_ty, lhs_ty);
4491 const shr = try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr);
4511 const shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty);
44924512
44934513 const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
44944514 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 {
45004520 } else if (int_info.signedness == .signed) blk: {
45014521 const lhs_abs = try self.signAbsValue(lhs, lhs_ty);
45024522 const rhs_abs = try self.signAbsValue(rhs, lhs_ty);
4503 const bin_op = try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul);
4523 const bin_op = try (try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(self, lhs_ty);
45044524 const mul_abs = try self.signAbsValue(bin_op, lhs_ty);
45054525 const cmp_op = try self.cmp(mul_abs, bin_op, lhs_ty, .neq);
45064526 try self.emitWValue(cmp_op);
45074527 try self.addLabel(.local_set, overflow_bit.local);
45084528 break :blk try self.wrapOperand(bin_op, lhs_ty);
45094529 } else blk: {
4510 const bin_op = try self.binOp(lhs, rhs, lhs_ty, .mul);
4530 const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(self, lhs_ty);
45114531 const shift_imm = if (wasm_bits == 32)
45124532 WValue{ .imm32 = int_info.bits }
45134533 else
......@@ -4587,7 +4607,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
45874607 }
45884608
45894609 const mul_result = try self.binOp(lhs, rhs, ty, .mul);
4590 return self.binOp(mul_result, addend, ty, .add);
4610 return (try self.binOp(mul_result, addend, ty, .add)).toLocal(self, ty);
45914611}
45924612
45934613fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -4663,16 +4683,16 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
46634683 32 => {
46644684 if (wasm_bits != int_info.bits) {
46654685 const val: u32 = @as(u32, 1) << @intCast(u5, int_info.bits);
4666 const bin_op = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or");
4667 try self.emitWValue(bin_op);
4686 // leave value on the stack
4687 _ = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or");
46684688 } else try self.emitWValue(operand);
46694689 try self.addTag(.i32_ctz);
46704690 },
46714691 64 => {
46724692 if (wasm_bits != int_info.bits) {
46734693 const val: u64 = @as(u64, 1) << @intCast(u6, int_info.bits);
4674 const bin_op = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or");
4675 try self.emitWValue(bin_op);
4694 // leave value on the stack
4695 _ = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or");
46764696 } else try self.emitWValue(operand);
46774697 try self.addTag(.i64_ctz);
46784698 try self.addTag(.i32_wrap_i64);
......@@ -4847,45 +4867,45 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
48474867 switch (int_info.bits) {
48484868 16 => {
48494869 const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);
4850 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and");
4851 const shr_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);
4870 const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and")).toLocal(self, ty);
4871 const shr_res = try (try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty);
48524872 const res = if (int_info.signedness == .signed) blk: {
48534873 break :blk try self.wrapOperand(shr_res, Type.u8);
48544874 } else shr_res;
4855 return self.binOp(lhs, res, ty, .@"or");
4875 return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty);
48564876 },
48574877 24 => {
48584878 const msb = try self.wrapOperand(operand, Type.u16);
48594879 const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr);
48604880
48614881 const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl);
4862 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and");
4863 const shr_res = try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr);
4882 const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and")).toLocal(self, Type.u16);
4883 const shr_res = try (try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty);
48644884
48654885 const res = if (int_info.signedness == .signed) blk: {
48664886 break :blk try self.wrapOperand(shr_res, Type.u8);
48674887 } else shr_res;
48684888 const lhs_tmp = try self.binOp(lhs, res, ty, .@"or");
4869 const lhs_result = try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr);
4889 const lhs_result = try (try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty);
48704890 const rhs_wrap = try self.wrapOperand(msb, Type.u8);
4871 const rhs_result = try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl);
4891 const rhs_result = try (try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl)).toLocal(self, ty);
48724892
48734893 const tmp = try self.binOp(lhs_result, rhs_result, ty, .@"or");
4874 return self.binOp(tmp, lsb, ty, .@"or");
4894 return (try self.binOp(tmp, lsb, ty, .@"or")).toLocal(self, ty);
48754895 },
48764896 32 => {
48774897 const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);
4878 const lhs = try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and");
4898 const lhs = try (try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(self, ty);
48794899 const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);
4880 const rhs = try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and");
4881 const tmp_or = try self.binOp(lhs, rhs, ty, .@"or");
4900 const rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty);
4901 const tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty);
48824902
4883 const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl);
4884 const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr);
4903 const shr = try (try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr)).toLocal(self, ty);
48854904 const res = if (int_info.signedness == .signed) blk: {
48864905 break :blk try self.wrapOperand(shr, Type.u16);
48874906 } else shr;
4888 return self.binOp(shl, res, ty, .@"or");
4907 const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl);
4908 return (try self.binOp(shl, res, ty, .@"or")).toLocal(self, ty);
48894909 },
48904910 else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),
48914911 }
......@@ -4902,7 +4922,7 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
49024922 if (ty.isSignedInt()) {
49034923 return self.divSigned(lhs, rhs, ty);
49044924 }
4905 return self.binOp(lhs, rhs, ty, .div);
4925 return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty);
49064926}
49074927
49084928fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -4914,7 +4934,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
49144934 const rhs = try self.resolveInst(bin_op.rhs);
49154935
49164936 if (ty.isUnsignedInt()) {
4917 return self.binOp(lhs, rhs, ty, .div);
4937 return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty);
49184938 } else if (ty.isSignedInt()) {
49194939 const int_bits = ty.intInfo(self.target).bits;
49204940 const wasm_bits = toWasmBits(int_bits) orelse {
......@@ -4927,9 +4947,6 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
49274947 break :blk try self.signAbsValue(rhs, ty);
49284948 } else rhs;
49294949
4930 const div_result = try self.binOp(lhs_res, rhs_res, ty, .div);
4931 const rem_result = try self.binOp(lhs_res, rhs_res, ty, .rem);
4932
49334950 const zero = switch (wasm_bits) {
49344951 32 => WValue{ .imm32 = 0 },
49354952 64 => WValue{ .imm64 = 0 },
......@@ -4938,7 +4955,10 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
49384955 const lhs_less_than_zero = try self.cmp(lhs_res, zero, ty, .lt);
49394956 const rhs_less_than_zero = try self.cmp(rhs_res, zero, ty, .lt);
49404957
4941 try self.emitWValue(div_result);
4958 const div_result = try self.allocLocal(ty);
4959 // leave on stack
4960 _ = try self.binOp(lhs_res, rhs_res, ty, .div);
4961 try self.addLabel(.local_tee, div_result.local);
49424962 try self.emitWValue(lhs_less_than_zero);
49434963 try self.emitWValue(rhs_less_than_zero);
49444964 switch (wasm_bits) {
......@@ -4953,7 +4973,8 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
49534973 else => unreachable,
49544974 }
49554975 try self.emitWValue(div_result);
4956 try self.emitWValue(rem_result);
4976 // leave value on the stack
4977 _ = try self.binOp(lhs_res, rhs_res, ty, .rem);
49574978 try self.addTag(.select);
49584979 } else {
49594980 const float_bits = ty.floatBits(self.target);
......@@ -5110,7 +5131,7 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
51105131 }
51115132
51125133 const wasm_bits = toWasmBits(int_info.bits).?;
5113 const bin_result = try self.binOp(lhs, rhs, ty, op);
5134 const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty);
51145135 if (wasm_bits != int_info.bits and op == .add) {
51155136 const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1);
51165137 const imm_val = switch (wasm_bits) {
......@@ -5161,7 +5182,7 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op
51615182 else => unreachable,
51625183 };
51635184
5164 const bin_result = try self.binOp(lhs, rhs, ty, op);
5185 const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty);
51655186 if (!is_wasm_bits) {
51665187 const cmp_result_lt = try self.cmp(bin_result, max_wvalue, ty, .lt);
51675188 try self.emitWValue(bin_result);
......@@ -5185,14 +5206,14 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op
51855206 };
51865207 const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt);
51875208 const cmp_zero_result = try self.cmp(rhs, zero, ty, if (op == .add) .lt else .gt);
5188 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.
51895209 const cmp_bin_zero_result = try self.cmp(bin_result, zero, ty, .lt);
51905210 try self.emitWValue(max_wvalue);
51915211 try self.emitWValue(min_wvalue);
51925212 try self.emitWValue(cmp_bin_zero_result);
51935213 try self.addTag(.select);
51945214 try self.emitWValue(bin_result);
5195 try self.emitWValue(xor);
5215 // leave on stack
5216 _ = try self.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor.
51965217 try self.addTag(.select);
51975218 try self.addLabel(.local_set, bin_result.local); // re-use local
51985219 return bin_result;
......@@ -5216,8 +5237,8 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
52165237 const result = try self.allocLocal(ty);
52175238
52185239 if (wasm_bits == int_info.bits) {
5219 const shl = try self.binOp(lhs, rhs, ty, .shl);
5220 const shr = try self.binOp(shl, rhs, ty, .shr);
5240 const shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty);
5241 const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);
52215242 const cmp_result = try self.cmp(lhs, shr, ty, .neq);
52225243
52235244 switch (wasm_bits) {
......@@ -5258,9 +5279,9 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
52585279 else => unreachable,
52595280 };
52605281
5261 const shl_res = try self.binOp(lhs, shift_value, ty, .shl);
5262 const shl = try self.binOp(shl_res, rhs, ty, .shl);
5263 const shr = try self.binOp(shl, rhs, ty, .shr);
5282 const shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty);
5283 const shl = try (try self.binOp(shl_res, rhs, ty, .shl)).toLocal(self, ty);
5284 const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);
52645285 const cmp_result = try self.cmp(shl_res, shr, ty, .neq);
52655286
52665287 switch (wasm_bits) {
......@@ -5294,7 +5315,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
52945315 try self.emitWValue(cmp_result);
52955316 try self.addTag(.select);
52965317 try self.addLabel(.local_set, result.local);
5297 const shift_result = try self.binOp(result, shift_value, ty, .shr);
5318 const shift_result = try (try self.binOp(result, shift_value, ty, .shr)).toLocal(self, ty);
52985319 if (is_signed) {
52995320 return self.wrapOperand(shift_result, ty);
53005321 }