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;...@@ -29,6 +29,8 @@ const errUnionErrorOffset = codegen.errUnionErrorOffset;
29const WValue = union(enum) {29const WValue = union(enum) {
30 /// May be referenced but is unused30 /// May be referenced but is unused
31 none: void,31 none: void,
32 /// The value lives on top of the stack
33 stack: void,
32 /// Index of the local variable34 /// Index of the local variable
33 local: u32,35 local: u32,
34 /// An immediate 32bit value36 /// An immediate 32bit value
...@@ -55,7 +57,7 @@ const WValue = union(enum) {...@@ -55,7 +57,7 @@ const WValue = union(enum) {
55 /// In wasm function pointers are indexes into a function table,57 /// In wasm function pointers are indexes into a function table,
56 /// rather than an address in the data section.58 /// rather than an address in the data section.
57 function_index: u32,59 function_index: u32,
58 /// Offset from the bottom of the stack, with the offset60 /// Offset from the bottom of the virtual stack, with the offset
59 /// pointing to where the value lives.61 /// pointing to where the value lives.
60 stack_offset: u32,62 stack_offset: u32,
6163
...@@ -71,6 +73,21 @@ const WValue = union(enum) {...@@ -71,6 +73,21 @@ const WValue = union(enum) {
71 else => return 0,73 else => return 0,
72 }74 }
73 }75 }
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 }
74};91};
7592
76/// Wasm ops, but without input/output/signedness information93/// Wasm ops, but without input/output/signedness information
...@@ -774,7 +791,7 @@ fn genBlockType(ty: Type, target: std.Target) u8 {...@@ -774,7 +791,7 @@ fn genBlockType(ty: Type, target: std.Target) u8 {
774/// Writes the bytecode depending on the given `WValue` in `val`791/// Writes the bytecode depending on the given `WValue` in `val`
775fn emitWValue(self: *Self, value: WValue) InnerError!void {792fn emitWValue(self: *Self, value: WValue) InnerError!void {
776 switch (value) {793 switch (value) {
777 .none => {}, // no-op794 .none, .stack => {}, // no-op
778 .local => |idx| try self.addLabel(.local_get, idx),795 .local => |idx| try self.addLabel(.local_get, idx),
779 .imm32 => |val| try self.addImm32(@bitCast(i32, val)),796 .imm32 => |val| try self.addImm32(@bitCast(i32, val)),
780 .imm64 => |val| try self.addImm64(val),797 .imm64 => |val| try self.addImm64(val),
...@@ -1892,6 +1909,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1892,6 +1909,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1892}1909}
18931910
1894fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {1911fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
1912 assert(!(lhs != .stack and rhs == .stack));
1895 switch (ty.zigTypeTag()) {1913 switch (ty.zigTypeTag()) {
1896 .ErrorUnion => {1914 .ErrorUnion => {
1897 const pl_ty = ty.errorUnionPayload();1915 const pl_ty = ty.errorUnionPayload();
...@@ -2070,10 +2088,14 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -2070,10 +2088,14 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
2070 const rhs = try self.resolveInst(bin_op.rhs);2088 const rhs = try self.resolveInst(bin_op.rhs);
2071 const ty = self.air.typeOf(bin_op.lhs);2089 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);
2074}2093}
20752094
2095/// Performs a binary operation on the given `WValue`'s
2096/// NOTE: THis leaves the value on top of the stack.
2076fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {2097fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2098 assert(!(lhs != .stack and rhs == .stack));
2077 if (isByRef(ty, self.target)) {2099 if (isByRef(ty, self.target)) {
2078 if (ty.zigTypeTag() == .Int) {2100 if (ty.zigTypeTag() == .Int) {
2079 return self.binOpBigInt(lhs, rhs, ty, op);2101 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...@@ -2099,10 +2121,7 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa
20992121
2100 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));2122 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
21012123
2102 // save the result in a temporary2124 return WValue{ .stack = {} };
2103 const bin_local = try self.allocLocal(ty);
2104 try self.addLabel(.local_set, bin_local.local);
2105 return bin_local;
2106}2125}
21072126
2108fn binOpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue {2127fn 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...@@ -2134,8 +2153,8 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr
2134 const rhs_high_bit = try self.load(rhs, Type.u64, 0);2153 const rhs_high_bit = try self.load(rhs, Type.u64, 0);
2135 const rhs_low_bit = try self.load(rhs, Type.u64, 8);2154 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);2156 const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64);
2138 const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op);2157 const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
21392158
2140 const lt = if (op == .add) blk: {2159 const lt = if (op == .add) blk: {
2141 break :blk try self.cmp(high_op_res, rhs_high_bit, Type.u64, .lt);2160 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...@@ -2143,7 +2162,7 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr
2143 break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);2162 break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);
2144 } else unreachable;2163 } else unreachable;
2145 const tmp = try self.intcast(lt, Type.u32, Type.u64);2164 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
2148 try self.store(result, high_op_res, Type.u64, 0);2167 try self.store(result, high_op_res, Type.u64, 0);
2149 try self.store(result, tmp_op, Type.u64, 8);2168 try self.store(result, tmp_op, Type.u64, 8);
...@@ -2202,6 +2221,7 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue {...@@ -2202,6 +2221,7 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
2202 if (wasm_bits == bitsize) return operand;2221 if (wasm_bits == bitsize) return operand;
22032222
2204 if (wasm_bits == 128) {2223 if (wasm_bits == 128) {
2224 assert(operand != .stack);
2205 const msb = try self.load(operand, Type.u64, 0);2225 const msb = try self.load(operand, Type.u64, 0);
2206 const lsb = try self.load(operand, Type.u64, 8);2226 const lsb = try self.load(operand, Type.u64, 8);
22072227
...@@ -2772,19 +2792,19 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2772,19 +2792,19 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2772 switch (wasm_bits) {2792 switch (wasm_bits) {
2773 32 => {2793 32 => {
2774 const bin_op = try self.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor);2794 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);
2776 },2796 },
2777 64 => {2797 64 => {
2778 const bin_op = try self.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor);2798 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);
2780 },2800 },
2781 128 => {2801 128 => {
2782 const result_ptr = try self.allocStack(operand_ty);2802 const result_ptr = try self.allocStack(operand_ty);
2783 const msb = try self.load(operand, Type.u64, 0);2803 const msb = try self.load(operand, Type.u64, 0);
2784 const lsb = try self.load(operand, Type.u64, 8);2804 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);2806 const msb_xor = try (try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty);
2787 const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);2807 const lsb_xor = try (try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty);
2788 try self.store(result_ptr, msb_xor, Type.u64, 0);2808 try self.store(result_ptr, msb_xor, Type.u64, 0);
2789 try self.store(result_ptr, lsb_xor, Type.u64, 8);2809 try self.store(result_ptr, lsb_xor, Type.u64, 8);
2790 return result_ptr;2810 return result_ptr;
...@@ -3215,7 +3235,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W...@@ -3215,7 +3235,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W
32153235
3216 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value3236 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value
3217 if (wanted.isSignedInt()) {3237 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);
3219 try self.store(stack_ptr, shr, Type.u64, 8);3239 try self.store(stack_ptr, shr, Type.u64, 8);
3220 } else {3240 } else {
3221 // Ensure memory of lsb is zero'd3241 // Ensure memory of lsb is zero'd
...@@ -4320,7 +4340,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W...@@ -4320,7 +4340,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
4320 break :blk try self.signAbsValue(rhs_op, lhs_ty);4340 break :blk try self.signAbsValue(rhs_op, lhs_ty);
4321 } else rhs_op;4341 } 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);
4324 const result = if (wasm_bits != int_info.bits) blk: {4344 const result = if (wasm_bits != int_info.bits) blk: {
4325 break :blk try self.wrapOperand(bin_op, lhs_ty);4345 break :blk try self.wrapOperand(bin_op, lhs_ty);
4326 } else bin_op;4346 } else bin_op;
...@@ -4330,7 +4350,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W...@@ -4330,7 +4350,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
4330 if (wasm_bits == int_info.bits) {4350 if (wasm_bits == int_info.bits) {
4331 const cmp_zero = try self.cmp(rhs, zero, lhs_ty, cmp_op);4351 const cmp_zero = try self.cmp(rhs, zero, lhs_ty, cmp_op);
4332 const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt);4352 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 32bit4353 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
4334 }4354 }
4335 const abs = try self.signAbsValue(bin_op, lhs_ty);4355 const abs = try self.signAbsValue(bin_op, lhs_ty);
4336 break :blk try self.cmp(abs, bin_op, lhs_ty, .neq);4356 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,...@@ -4360,8 +4380,8 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type,
4360 const rhs_high_bit = try self.load(rhs, Type.u64, 0);4380 const rhs_high_bit = try self.load(rhs, Type.u64, 0);
4361 const rhs_low_bit = try self.load(rhs, Type.u64, 8);4381 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);4383 const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64);
4364 const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op);4384 const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
43654385
4366 const lt = if (op == .add) blk: {4386 const lt = if (op == .add) blk: {
4367 break :blk try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt);4387 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,...@@ -4369,14 +4389,14 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type,
4369 break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);4389 break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);
4370 } else unreachable;4390 } else unreachable;
4371 const tmp = try self.intcast(lt, Type.u32, Type.u64);4391 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
4374 const overflow_bit = if (is_signed) blk: {4394 const overflow_bit = if (is_signed) blk: {
4375 const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor);
4376 const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);4395 const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
4377 const to_wrap = if (op == .add) wrap: {4396 const to_wrap = if (op == .add) wrap: {
4378 break :wrap try self.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);4397 break :wrap try self.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
4379 } else xor_low;4398 } else xor_low;
4399 const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor);
4380 const wrap = try self.binOp(to_wrap, xor_op, Type.u64, .@"and");4400 const wrap = try self.binOp(to_wrap, xor_op, Type.u64, .@"and");
4381 break :blk try self.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed4401 break :blk try self.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed
4382 } else blk: {4402 } else blk: {
...@@ -4422,7 +4442,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4422,7 +4442,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4422 return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});4442 return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});
4423 };4443 };
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);
4426 const result = if (wasm_bits != int_info.bits) blk: {4446 const result = if (wasm_bits != int_info.bits) blk: {
4427 break :blk try self.wrapOperand(shl, lhs_ty);4447 break :blk try self.wrapOperand(shl, lhs_ty);
4428 } else shl;4448 } else shl;
...@@ -4432,7 +4452,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4432,7 +4452,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4432 const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr);4452 const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr);
4433 break :blk try self.cmp(lhs, wrapped, lhs_ty, .neq);4453 break :blk try self.cmp(lhs, wrapped, lhs_ty, .neq);
4434 } else blk: {4454 } 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);
4436 break :blk try self.cmp(lhs, shr, lhs_ty, .neq);4456 break :blk try self.cmp(lhs, shr, lhs_ty, .neq);
4437 };4457 };
44384458
...@@ -4478,7 +4498,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4478,7 +4498,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4478 const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64;4498 const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64;
4479 const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty);4499 const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty);
4480 const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty);4500 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);
4482 if (int_info.signedness == .unsigned) {4502 if (int_info.signedness == .unsigned) {
4483 const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);4503 const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
4484 const wrap = try self.intcast(shr, new_ty, lhs_ty);4504 const wrap = try self.intcast(shr, new_ty, lhs_ty);
...@@ -4488,7 +4508,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4488,7 +4508,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4488 break :blk try self.intcast(bin_op, new_ty, lhs_ty);4508 break :blk try self.intcast(bin_op, new_ty, lhs_ty);
4489 } else {4509 } else {
4490 const down_cast = try self.intcast(bin_op, new_ty, lhs_ty);4510 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
4493 const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);4513 const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
4494 const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty);4514 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 {...@@ -4500,14 +4520,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4500 } else if (int_info.signedness == .signed) blk: {4520 } else if (int_info.signedness == .signed) blk: {
4501 const lhs_abs = try self.signAbsValue(lhs, lhs_ty);4521 const lhs_abs = try self.signAbsValue(lhs, lhs_ty);
4502 const rhs_abs = try self.signAbsValue(rhs, lhs_ty);4522 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);
4504 const mul_abs = try self.signAbsValue(bin_op, lhs_ty);4524 const mul_abs = try self.signAbsValue(bin_op, lhs_ty);
4505 const cmp_op = try self.cmp(mul_abs, bin_op, lhs_ty, .neq);4525 const cmp_op = try self.cmp(mul_abs, bin_op, lhs_ty, .neq);
4506 try self.emitWValue(cmp_op);4526 try self.emitWValue(cmp_op);
4507 try self.addLabel(.local_set, overflow_bit.local);4527 try self.addLabel(.local_set, overflow_bit.local);
4508 break :blk try self.wrapOperand(bin_op, lhs_ty);4528 break :blk try self.wrapOperand(bin_op, lhs_ty);
4509 } else blk: {4529 } 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);
4511 const shift_imm = if (wasm_bits == 32)4531 const shift_imm = if (wasm_bits == 32)
4512 WValue{ .imm32 = int_info.bits }4532 WValue{ .imm32 = int_info.bits }
4513 else4533 else
...@@ -4587,7 +4607,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4587,7 +4607,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4587 }4607 }
45884608
4589 const mul_result = try self.binOp(lhs, rhs, ty, .mul);4609 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);
4591}4611}
45924612
4593fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4613fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -4663,16 +4683,16 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4663,16 +4683,16 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4663 32 => {4683 32 => {
4664 if (wasm_bits != int_info.bits) {4684 if (wasm_bits != int_info.bits) {
4665 const val: u32 = @as(u32, 1) << @intCast(u5, int_info.bits);4685 const val: u32 = @as(u32, 1) << @intCast(u5, int_info.bits);
4666 const bin_op = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or");4686 // leave value on the stack
4667 try self.emitWValue(bin_op);4687 _ = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or");
4668 } else try self.emitWValue(operand);4688 } else try self.emitWValue(operand);
4669 try self.addTag(.i32_ctz);4689 try self.addTag(.i32_ctz);
4670 },4690 },
4671 64 => {4691 64 => {
4672 if (wasm_bits != int_info.bits) {4692 if (wasm_bits != int_info.bits) {
4673 const val: u64 = @as(u64, 1) << @intCast(u6, int_info.bits);4693 const val: u64 = @as(u64, 1) << @intCast(u6, int_info.bits);
4674 const bin_op = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or");4694 // leave value on the stack
4675 try self.emitWValue(bin_op);4695 _ = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or");
4676 } else try self.emitWValue(operand);4696 } else try self.emitWValue(operand);
4677 try self.addTag(.i64_ctz);4697 try self.addTag(.i64_ctz);
4678 try self.addTag(.i32_wrap_i64);4698 try self.addTag(.i32_wrap_i64);
...@@ -4847,45 +4867,45 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4847,45 +4867,45 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4847 switch (int_info.bits) {4867 switch (int_info.bits) {
4848 16 => {4868 16 => {
4849 const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);4869 const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);
4850 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and");4870 const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and")).toLocal(self, ty);
4851 const shr_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);4871 const shr_res = try (try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty);
4852 const res = if (int_info.signedness == .signed) blk: {4872 const res = if (int_info.signedness == .signed) blk: {
4853 break :blk try self.wrapOperand(shr_res, Type.u8);4873 break :blk try self.wrapOperand(shr_res, Type.u8);
4854 } else shr_res;4874 } else shr_res;
4855 return self.binOp(lhs, res, ty, .@"or");4875 return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty);
4856 },4876 },
4857 24 => {4877 24 => {
4858 const msb = try self.wrapOperand(operand, Type.u16);4878 const msb = try self.wrapOperand(operand, Type.u16);
4859 const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr);4879 const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr);
48604880
4861 const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl);4881 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");4882 const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and")).toLocal(self, Type.u16);
4863 const shr_res = try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr);4883 const shr_res = try (try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty);
48644884
4865 const res = if (int_info.signedness == .signed) blk: {4885 const res = if (int_info.signedness == .signed) blk: {
4866 break :blk try self.wrapOperand(shr_res, Type.u8);4886 break :blk try self.wrapOperand(shr_res, Type.u8);
4867 } else shr_res;4887 } else shr_res;
4868 const lhs_tmp = try self.binOp(lhs, res, ty, .@"or");4888 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);
4870 const rhs_wrap = try self.wrapOperand(msb, Type.u8);4890 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
4873 const tmp = try self.binOp(lhs_result, rhs_result, ty, .@"or");4893 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);
4875 },4895 },
4876 32 => {4896 32 => {
4877 const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);4897 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);
4879 const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);4899 const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);
4880 const rhs = try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and");4900 const rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty);
4881 const tmp_or = try self.binOp(lhs, rhs, ty, .@"or");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);4903 const shr = try (try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr)).toLocal(self, ty);
4884 const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr);
4885 const res = if (int_info.signedness == .signed) blk: {4904 const res = if (int_info.signedness == .signed) blk: {
4886 break :blk try self.wrapOperand(shr, Type.u16);4905 break :blk try self.wrapOperand(shr, Type.u16);
4887 } else shr;4906 } 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);
4889 },4909 },
4890 else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),4910 else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),
4891 }4911 }
...@@ -4902,7 +4922,7 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4902,7 +4922,7 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4902 if (ty.isSignedInt()) {4922 if (ty.isSignedInt()) {
4903 return self.divSigned(lhs, rhs, ty);4923 return self.divSigned(lhs, rhs, ty);
4904 }4924 }
4905 return self.binOp(lhs, rhs, ty, .div);4925 return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty);
4906}4926}
49074927
4908fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4928fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -4914,7 +4934,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4914,7 +4934,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4914 const rhs = try self.resolveInst(bin_op.rhs);4934 const rhs = try self.resolveInst(bin_op.rhs);
49154935
4916 if (ty.isUnsignedInt()) {4936 if (ty.isUnsignedInt()) {
4917 return self.binOp(lhs, rhs, ty, .div);4937 return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty);
4918 } else if (ty.isSignedInt()) {4938 } else if (ty.isSignedInt()) {
4919 const int_bits = ty.intInfo(self.target).bits;4939 const int_bits = ty.intInfo(self.target).bits;
4920 const wasm_bits = toWasmBits(int_bits) orelse {4940 const wasm_bits = toWasmBits(int_bits) orelse {
...@@ -4927,9 +4947,6 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4927,9 +4947,6 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4927 break :blk try self.signAbsValue(rhs, ty);4947 break :blk try self.signAbsValue(rhs, ty);
4928 } else rhs;4948 } 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
4933 const zero = switch (wasm_bits) {4950 const zero = switch (wasm_bits) {
4934 32 => WValue{ .imm32 = 0 },4951 32 => WValue{ .imm32 = 0 },
4935 64 => WValue{ .imm64 = 0 },4952 64 => WValue{ .imm64 = 0 },
...@@ -4938,7 +4955,10 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4938,7 +4955,10 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4938 const lhs_less_than_zero = try self.cmp(lhs_res, zero, ty, .lt);4955 const lhs_less_than_zero = try self.cmp(lhs_res, zero, ty, .lt);
4939 const rhs_less_than_zero = try self.cmp(rhs_res, zero, ty, .lt);4956 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);
4942 try self.emitWValue(lhs_less_than_zero);4962 try self.emitWValue(lhs_less_than_zero);
4943 try self.emitWValue(rhs_less_than_zero);4963 try self.emitWValue(rhs_less_than_zero);
4944 switch (wasm_bits) {4964 switch (wasm_bits) {
...@@ -4953,7 +4973,8 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4953,7 +4973,8 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4953 else => unreachable,4973 else => unreachable,
4954 }4974 }
4955 try self.emitWValue(div_result);4975 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);
4957 try self.addTag(.select);4978 try self.addTag(.select);
4958 } else {4979 } else {
4959 const float_bits = ty.floatBits(self.target);4980 const float_bits = ty.floatBits(self.target);
...@@ -5110,7 +5131,7 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -5110,7 +5131,7 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
5110 }5131 }
51115132
5112 const wasm_bits = toWasmBits(int_info.bits).?;5133 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);
5114 if (wasm_bits != int_info.bits and op == .add) {5135 if (wasm_bits != int_info.bits and op == .add) {
5115 const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1);5136 const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1);
5116 const imm_val = switch (wasm_bits) {5137 const imm_val = switch (wasm_bits) {
...@@ -5161,7 +5182,7 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op...@@ -5161,7 +5182,7 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op
5161 else => unreachable,5182 else => unreachable,
5162 };5183 };
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);
5165 if (!is_wasm_bits) {5186 if (!is_wasm_bits) {
5166 const cmp_result_lt = try self.cmp(bin_result, max_wvalue, ty, .lt);5187 const cmp_result_lt = try self.cmp(bin_result, max_wvalue, ty, .lt);
5167 try self.emitWValue(bin_result);5188 try self.emitWValue(bin_result);
...@@ -5185,14 +5206,14 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op...@@ -5185,14 +5206,14 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op
5185 };5206 };
5186 const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt);5207 const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt);
5187 const cmp_zero_result = try self.cmp(rhs, zero, ty, if (op == .add) .lt else .gt);5208 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.
5189 const cmp_bin_zero_result = try self.cmp(bin_result, zero, ty, .lt);5209 const cmp_bin_zero_result = try self.cmp(bin_result, zero, ty, .lt);
5190 try self.emitWValue(max_wvalue);5210 try self.emitWValue(max_wvalue);
5191 try self.emitWValue(min_wvalue);5211 try self.emitWValue(min_wvalue);
5192 try self.emitWValue(cmp_bin_zero_result);5212 try self.emitWValue(cmp_bin_zero_result);
5193 try self.addTag(.select);5213 try self.addTag(.select);
5194 try self.emitWValue(bin_result);5214 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.
5196 try self.addTag(.select);5217 try self.addTag(.select);
5197 try self.addLabel(.local_set, bin_result.local); // re-use local5218 try self.addLabel(.local_set, bin_result.local); // re-use local
5198 return bin_result;5219 return bin_result;
...@@ -5216,8 +5237,8 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -5216,8 +5237,8 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
5216 const result = try self.allocLocal(ty);5237 const result = try self.allocLocal(ty);
52175238
5218 if (wasm_bits == int_info.bits) {5239 if (wasm_bits == int_info.bits) {
5219 const shl = try self.binOp(lhs, rhs, ty, .shl);5240 const shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty);
5220 const shr = try self.binOp(shl, rhs, ty, .shr);5241 const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);
5221 const cmp_result = try self.cmp(lhs, shr, ty, .neq);5242 const cmp_result = try self.cmp(lhs, shr, ty, .neq);
52225243
5223 switch (wasm_bits) {5244 switch (wasm_bits) {
...@@ -5258,9 +5279,9 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -5258,9 +5279,9 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
5258 else => unreachable,5279 else => unreachable,
5259 };5280 };
52605281
5261 const shl_res = try self.binOp(lhs, shift_value, ty, .shl);5282 const shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty);
5262 const shl = try self.binOp(shl_res, rhs, ty, .shl);5283 const shl = try (try self.binOp(shl_res, rhs, ty, .shl)).toLocal(self, ty);
5263 const shr = try self.binOp(shl, rhs, ty, .shr);5284 const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);
5264 const cmp_result = try self.cmp(shl_res, shr, ty, .neq);5285 const cmp_result = try self.cmp(shl_res, shr, ty, .neq);
52655286
5266 switch (wasm_bits) {5287 switch (wasm_bits) {
...@@ -5294,7 +5315,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -5294,7 +5315,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
5294 try self.emitWValue(cmp_result);5315 try self.emitWValue(cmp_result);
5295 try self.addTag(.select);5316 try self.addTag(.select);
5296 try self.addLabel(.local_set, result.local);5317 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);
5298 if (is_signed) {5319 if (is_signed) {
5299 return self.wrapOperand(shift_result, ty);5320 return self.wrapOperand(shift_result, ty);
5300 }5321 }