authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-07-27 21:04:58+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-08-11 11:08:01+02:00
logb42ba7c3d411cde31ede290b3915150c3e8acfbb
treee30ba3182d14b7936c5245b543799ce64cb10362
parenta5e4fd7ef66bbd16ddad1a12d378eefcb740da1d
signaturelock-open Commit is signed but in an unrecognized format.

wasm: free unused locals

When a local is no longer referenced or used, free it so the local can be re-used by another instruction. This means we generate less locals. Freeing this local is a manual action and must only be used on temporaries or where we are sure the local is not referenced by a different AIR instruction, as that creates UB. We now also no longer store a `WValue` when its tag is set to `none` as those may never be referenced by any AIR instruction. An assertion is done to make sure we never store a reference to a `stack` value in our resolved instructions.

1 files changed, 151 insertions(+), 86 deletions(-)

src/arch/wasm/CodeGen.zig+151-86
...@@ -77,17 +77,34 @@ const WValue = union(enum) {...@@ -77,17 +77,34 @@ const WValue = union(enum) {
77 /// Promotes a `WValue` to a local when given value is on top of the stack.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.78 /// When encountering a `local` or `stack_offset` this is essentially a no-op.
79 /// All other tags are illegal.79 /// All other tags are illegal.
80 fn toLocal(self: WValue, gen: *Self, ty: Type) InnerError!WValue {80 fn toLocal(value: WValue, gen: *Self, ty: Type) InnerError!WValue {
81 switch (self) {81 switch (value) {
82 .stack => {82 .stack => {
83 const local = try gen.allocLocal(ty);83 const local = try gen.allocLocal(ty);
84 try gen.addLabel(.local_set, local.local);84 try gen.addLabel(.local_set, local.local);
85 return local;85 return local;
86 },86 },
87 .local, .stack_offset => return self,87 .local, .stack_offset => return value,
88 else => unreachable,88 else => unreachable,
89 }89 }
90 }90 }
91
92 /// Marks a local as no longer being referenced and essentially allows
93 /// us to re-use it somewhere else within the function.
94 /// The valtype of the local is deducted by using the index of the given.
95 fn free(value: *WValue, gen: *Self) void {
96 if (value.* != .local) return;
97 const local_value = value.local;
98 const index = local_value - gen.args.len - @boolToInt(gen.return_value != .none);
99 const valtype = @intToEnum(wasm.Valtype, gen.locals.items[index]);
100 switch (valtype) {
101 .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead
102 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,
103 .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return,
104 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,
105 }
106 value.* = WValue{ .none = {} };
107 }
91};108};
92109
93/// Wasm ops, but without input/output/signedness information110/// Wasm ops, but without input/output/signedness information
...@@ -829,27 +846,18 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue {...@@ -829,27 +846,18 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
829 },846 },
830 }847 }
831 // no local was free to be re-used, so allocate a new local instead848 // no local was free to be re-used, so allocate a new local instead
832 try self.locals.append(self.gpa, wasm.valtype(valtype));849 return self.ensureAllocLocal(ty);
850}
851
852/// Ensures a new local will be created. This is useful when it's useful
853/// to use a zero-initialized local.
854fn ensureAllocLocal(self: *Self, ty: Type) InnerError!WValue {
855 try self.locals.append(self.gpa, genValtype(ty, self.target));
833 const initial_index = self.local_index;856 const initial_index = self.local_index;
834 self.local_index += 1;857 self.local_index += 1;
835 return WValue{ .local = initial_index };858 return WValue{ .local = initial_index };
836}859}
837860
838/// Marks a local as no longer being referenced and essentially allows
839/// us to re-use it somewhere else within the function.
840/// The valtype of the local is deducted by using the index of the given.
841/// Asserts given `WValue` is a `local`.
842fn freeLocal(self: *Self, value: WValue) InnerError!WValue {
843 const index = value.local;
844 const valtype = wasm.valtype(self.locals.items[index]);
845 switch (valtype) {
846 .i32 => self.free_locals_i32.append(index) catch {}, // It's ok to fail any of those, a new local can be allocated instead
847 .i64 => self.free_locals_i64.append(index) catch {},
848 .f32 => self.free_locals_f32.append(index) catch {},
849 .f64 => self.free_locals_f64.append(index) catch {},
850 }
851}
852
853/// Generates a `wasm.Type` from a given function type.861/// Generates a `wasm.Type` from a given function type.
854/// Memory is owned by the caller.862/// Memory is owned by the caller.
855fn genFunctype(gpa: Allocator, cc: std.builtin.CallingConvention, params: []const Type, return_type: Type, target: std.Target) !wasm.Type {863fn genFunctype(gpa: Allocator, cc: std.builtin.CallingConvention, params: []const Type, return_type: Type, target: std.Target) !wasm.Type {
...@@ -1197,9 +1205,9 @@ fn initializeStack(self: *Self) !void {...@@ -1197,9 +1205,9 @@ fn initializeStack(self: *Self) !void {
1197 // Reserve a local to store the current stack pointer1205 // Reserve a local to store the current stack pointer
1198 // We can later use this local to set the stack pointer back to the value1206 // We can later use this local to set the stack pointer back to the value
1199 // we have stored here.1207 // we have stored here.
1200 self.initial_stack_value = try self.allocLocal(Type.usize);1208 self.initial_stack_value = try self.ensureAllocLocal(Type.usize);
1201 // Also reserve a local to store the bottom stack value1209 // Also reserve a local to store the bottom stack value
1202 self.bottom_stack_value = try self.allocLocal(Type.usize);1210 self.bottom_stack_value = try self.ensureAllocLocal(Type.usize);
1203}1211}
12041212
1205/// Reads the stack pointer from `Context.initial_stack_value` and writes it1213/// Reads the stack pointer from `Context.initial_stack_value` and writes it
...@@ -1330,7 +1338,9 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {...@@ -1330,7 +1338,9 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {
1330 else => {1338 else => {
1331 // TODO: We should probably lower this to a call to compiler_rt1339 // TODO: We should probably lower this to a call to compiler_rt
1332 // But for now, we implement it manually1340 // But for now, we implement it manually
1333 const offset = try self.allocLocal(Type.usize); // local for counter1341 var offset = try self.ensureAllocLocal(Type.usize); // local for counter
1342 defer offset.free(self);
1343
1334 // outer block to jump to when loop is done1344 // outer block to jump to when loop is done
1335 try self.startBlock(.block, wasm.block_empty);1345 try self.startBlock(.block, wasm.block_empty);
1336 try self.startBlock(.loop, wasm.block_empty);1346 try self.startBlock(.loop, wasm.block_empty);
...@@ -1467,7 +1477,7 @@ fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum...@@ -1467,7 +1477,7 @@ fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum
1467 // do not perform arithmetic when offset is 0.1477 // do not perform arithmetic when offset is 0.
1468 if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value;1478 if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value;
1469 const result_ptr: WValue = switch (action) {1479 const result_ptr: WValue = switch (action) {
1470 .new => try self.allocLocal(Type.usize),1480 .new => try self.ensureAllocLocal(Type.usize),
1471 .modify => ptr_value,1481 .modify => ptr_value,
1472 };1482 };
1473 try self.emitWValue(ptr_value);1483 try self.emitWValue(ptr_value);
...@@ -1715,7 +1725,10 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1715,7 +1725,10 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1715fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {1725fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1716 for (body) |inst| {1726 for (body) |inst| {
1717 const result = try self.genInst(inst);1727 const result = try self.genInst(inst);
1718 try self.values.putNoClobber(self.gpa, Air.indexToRef(inst), result);1728 if (result != .none) {
1729 assert(result != .stack); // not allowed to store stack values as we cannot keep track of where they are on the stack
1730 try self.values.putNoClobber(self.gpa, Air.indexToRef(inst), result);
1731 }
1719 }1732 }
1720}1733}
17211734
...@@ -2151,9 +2164,12 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr...@@ -2151,9 +2164,12 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr
2151 }2164 }
21522165
2153 const result = try self.allocStack(ty);2166 const result = try self.allocStack(ty);
2154 const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);2167 var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);
2155 const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);2168 defer lhs_high_bit.free(self);
2156 const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);2169 var rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);
2170 defer rhs_high_bit.free(self);
2171 var high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
2172 defer high_op_res.free(self);
21572173
2158 const lhs_low_bit = try self.load(lhs, Type.u64, 8);2174 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
2159 const rhs_low_bit = try self.load(rhs, Type.u64, 8);2175 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
...@@ -2165,7 +2181,8 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr...@@ -2165,7 +2181,8 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr
2165 break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);2181 break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);
2166 } else unreachable;2182 } else unreachable;
2167 const tmp = try self.intcast(lt, Type.u32, Type.u64);2183 const tmp = try self.intcast(lt, Type.u32, Type.u64);
2168 const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64);2184 var tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64);
2185 defer tmp_op.free(self);
21692186
2170 try self.store(result, high_op_res, Type.u64, 0);2187 try self.store(result, high_op_res, Type.u64, 0);
2171 try self.store(result, tmp_op, Type.u64, 8);2188 try self.store(result, tmp_op, Type.u64, 8);
...@@ -3208,25 +3225,22 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W...@@ -3208,25 +3225,22 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W
3208 } else if (wanted_bits == 128) {3225 } else if (wanted_bits == 128) {
3209 // for 128bit integers we store the integer in the virtual stack, rather than a local3226 // for 128bit integers we store the integer in the virtual stack, rather than a local
3210 const stack_ptr = try self.allocStack(wanted);3227 const stack_ptr = try self.allocStack(wanted);
3228 try self.emitWValue(stack_ptr);
32113229
3212 // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it3230 // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it
3213 // meaning less store operations are required.3231 // meaning less store operations are required.
3214 const lhs = if (op_bits == 32) blk: {3232 const lhs = if (op_bits == 32) blk: {
3215 const tmp = try self.intcast(3233 break :blk try self.intcast(operand, given, if (wanted.isSignedInt()) Type.i64 else Type.u64);
3216 operand,
3217 given,
3218 if (wanted.isSignedInt()) Type.i64 else Type.u64,
3219 );
3220 break :blk try tmp.toLocal(self, Type.u64);
3221 } else operand;3234 } else operand;
32223235
3223 // store msb first3236 // store msb first
3224 try self.store(stack_ptr, lhs, Type.u64, 0);3237 try self.store(.{ .stack = {} }, lhs, Type.u64, 0 + stack_ptr.offset());
32253238
3226 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value3239 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value
3227 if (wanted.isSignedInt()) {3240 if (wanted.isSignedInt()) {
3228 const shr = try (try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr)).toLocal(self, Type.i64);3241 try self.emitWValue(stack_ptr);
3229 try self.store(stack_ptr, shr, Type.u64, 8);3242 const shr = try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
3243 try self.store(.{ .stack = {} }, shr, Type.u64, 8 + stack_ptr.offset());
3230 } else {3244 } else {
3231 // Ensure memory of lsb is zero'd3245 // Ensure memory of lsb is zero'd
3232 try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);3246 try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
...@@ -3534,11 +3548,12 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3534,11 +3548,12 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3534 try self.addTag(.i32_mul);3548 try self.addTag(.i32_mul);
3535 try self.addTag(.i32_add);3549 try self.addTag(.i32_add);
35363550
3537 const result = try self.allocLocal(elem_ty);3551 var result = try self.allocLocal(elem_ty);
3538 try self.addLabel(.local_set, result.local);3552 try self.addLabel(.local_set, result.local);
3539 if (isByRef(elem_ty, self.target)) {3553 if (isByRef(elem_ty, self.target)) {
3540 return result;3554 return result;
3541 }3555 }
3556 defer result.free(self); // only free if it's not returned like above
35423557
3543 const elem_val = try self.load(result, elem_ty, 0);3558 const elem_val = try self.load(result, elem_ty, 0);
3544 return elem_val.toLocal(self, elem_ty);3559 return elem_val.toLocal(self, elem_ty);
...@@ -3656,7 +3671,7 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void...@@ -3656,7 +3671,7 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void
3656 else => {3671 else => {
3657 // TODO: We should probably lower this to a call to compiler_rt3672 // TODO: We should probably lower this to a call to compiler_rt
3658 // But for now, we implement it manually3673 // But for now, we implement it manually
3659 const offset = try self.allocLocal(Type.usize); // local for counter3674 const offset = try self.ensureAllocLocal(Type.usize); // local for counter
3660 // outer block to jump to when loop is done3675 // outer block to jump to when loop is done
3661 try self.startBlock(.block, wasm.block_empty);3676 try self.startBlock(.block, wasm.block_empty);
3662 try self.startBlock(.loop, wasm.block_empty);3677 try self.startBlock(.loop, wasm.block_empty);
...@@ -3713,12 +3728,14 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3713,12 +3728,14 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3713 try self.addTag(.i32_mul);3728 try self.addTag(.i32_mul);
3714 try self.addTag(.i32_add);3729 try self.addTag(.i32_add);
37153730
3716 const result = try self.allocLocal(Type.usize);3731 var result = try self.allocLocal(Type.usize);
3717 try self.addLabel(.local_set, result.local);3732 try self.addLabel(.local_set, result.local);
37183733
3719 if (isByRef(elem_ty, self.target)) {3734 if (isByRef(elem_ty, self.target)) {
3720 return result;3735 return result;
3721 }3736 }
3737 defer result.free(self); // only free if no longer needed and not returned like above
3738
3722 const elem_val = try self.load(result, elem_ty, 0);3739 const elem_val = try self.load(result, elem_ty, 0);
3723 return elem_val.toLocal(self, elem_ty);3740 return elem_val.toLocal(self, elem_ty);
3724}3741}
...@@ -3944,7 +3961,8 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std...@@ -3944,7 +3961,8 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
39443961
3945 // We store the final result in here that will be validated3962 // We store the final result in here that will be validated
3946 // if the optional is truly equal.3963 // if the optional is truly equal.
3947 const result = try self.allocLocal(Type.initTag(.i32));3964 var result = try self.ensureAllocLocal(Type.initTag(.i32));
3965 defer result.free(self);
39483966
3949 try self.startBlock(.block, wasm.block_empty);3967 try self.startBlock(.block, wasm.block_empty);
3950 _ = try self.isNull(lhs, operand_ty, .i32_eq);3968 _ = try self.isNull(lhs, operand_ty, .i32_eq);
...@@ -3965,8 +3983,6 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std...@@ -3965,8 +3983,6 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
3965 try self.emitWValue(result);3983 try self.emitWValue(result);
3966 try self.addImm32(0);3984 try self.addImm32(0);
3967 try self.addTag(if (op == .eq) .i32_ne else .i32_eq);3985 try self.addTag(if (op == .eq) .i32_ne else .i32_eq);
3968 try self.addLabel(.local_set, result.local);
3969 try self.emitWValue(result);
3970 return WValue{ .stack = {} };3986 return WValue{ .stack = {} };
3971}3987}
39723988
...@@ -3980,8 +3996,10 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma...@@ -3980,8 +3996,10 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma
3980 return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits});3996 return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits});
3981 }3997 }
39823998
3983 const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);3999 var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);
3984 const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);4000 defer lhs_high_bit.free(self);
4001 var rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);
4002 defer rhs_high_bit.free(self);
39854003
3986 switch (op) {4004 switch (op) {
3987 .eq, .neq => {4005 .eq, .neq => {
...@@ -4313,17 +4331,19 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W...@@ -4313,17 +4331,19 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
43134331
4314 // for signed integers, we first apply signed shifts by the difference in bits4332 // for signed integers, we first apply signed shifts by the difference in bits
4315 // to get the signed value, as we store it internally as 2's complement.4333 // to get the signed value, as we store it internally as 2's complement.
4316 const lhs = if (wasm_bits != int_info.bits and is_signed) blk: {4334 var lhs = if (wasm_bits != int_info.bits and is_signed) blk: {
4317 break :blk try (try self.signAbsValue(lhs_op, lhs_ty)).toLocal(self, lhs_ty);4335 break :blk try (try self.signAbsValue(lhs_op, lhs_ty)).toLocal(self, lhs_ty);
4318 } else lhs_op;4336 } else lhs_op;
4319 const rhs = if (wasm_bits != int_info.bits and is_signed) blk: {4337 var rhs = if (wasm_bits != int_info.bits and is_signed) blk: {
4320 break :blk try (try self.signAbsValue(rhs_op, lhs_ty)).toLocal(self, lhs_ty);4338 break :blk try (try self.signAbsValue(rhs_op, lhs_ty)).toLocal(self, lhs_ty);
4321 } else rhs_op;4339 } else rhs_op;
43224340
4323 const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty);4341 var 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: {4342 defer bin_op.free(self);
4343 var result = if (wasm_bits != int_info.bits) blk: {
4325 break :blk try (try self.wrapOperand(bin_op, lhs_ty)).toLocal(self, lhs_ty);4344 break :blk try (try self.wrapOperand(bin_op, lhs_ty)).toLocal(self, lhs_ty);
4326 } else bin_op;4345 } else bin_op;
4346 defer result.free(self); // no-op when wasm_bits == int_info.bits
43274347
4328 const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt;4348 const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt;
4329 const overflow_bit: WValue = if (is_signed) blk: {4349 const overflow_bit: WValue = if (is_signed) blk: {
...@@ -4338,13 +4358,23 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W...@@ -4338,13 +4358,23 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
4338 try self.cmp(bin_op, lhs, lhs_ty, cmp_op)4358 try self.cmp(bin_op, lhs, lhs_ty, cmp_op)
4339 else4359 else
4340 try self.cmp(bin_op, result, lhs_ty, .neq);4360 try self.cmp(bin_op, result, lhs_ty, .neq);
4341 const overflow_local = try overflow_bit.toLocal(self, Type.u32);4361 var overflow_local = try overflow_bit.toLocal(self, Type.u32);
4362 defer overflow_local.free(self);
43424363
4343 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));4364 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));
4344 try self.store(result_ptr, result, lhs_ty, 0);4365 try self.store(result_ptr, result, lhs_ty, 0);
4345 const offset = @intCast(u32, lhs_ty.abiSize(self.target));4366 const offset = @intCast(u32, lhs_ty.abiSize(self.target));
4346 try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset);4367 try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset);
43474368
4369 // in this case, we performed a signAbsValue which created a temporary local
4370 // so let's free this so it can be re-used instead.
4371 // In the other case we do not want to free it, because that would free the
4372 // resolved instructions which may be referenced by other instructions.
4373 if (wasm_bits != int_info.bits and is_signed) {
4374 lhs.free(self);
4375 rhs.free(self);
4376 }
4377
4348 return result_ptr;4378 return result_ptr;
4349}4379}
43504380
...@@ -4356,21 +4386,30 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type,...@@ -4356,21 +4386,30 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type,
4356 return self.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits});4386 return self.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits});
4357 }4387 }
43584388
4359 const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);4389 var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);
4360 const lhs_low_bit = try (try self.load(lhs, Type.u64, 8)).toLocal(self, Type.u64);4390 defer lhs_high_bit.free(self);
4361 const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);4391 var lhs_low_bit = try (try self.load(lhs, Type.u64, 8)).toLocal(self, Type.u64);
4362 const rhs_low_bit = try (try self.load(rhs, Type.u64, 8)).toLocal(self, Type.u64);4392 defer lhs_low_bit.free(self);
4393 var rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);
4394 defer rhs_high_bit.free(self);
4395 var rhs_low_bit = try (try self.load(rhs, Type.u64, 8)).toLocal(self, Type.u64);
4396 defer rhs_low_bit.free(self);
43634397
4364 const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64);4398 var low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64);
4365 const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);4399 defer low_op_res.free(self);
4400 var high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
4401 defer high_op_res.free(self);
43664402
4367 const lt = if (op == .add) blk: {4403 var lt = if (op == .add) blk: {
4368 break :blk try (try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32);4404 break :blk try (try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32);
4369 } else if (op == .sub) blk: {4405 } else if (op == .sub) blk: {
4370 break :blk try (try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32);4406 break :blk try (try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32);
4371 } else unreachable;4407 } else unreachable;
4372 const tmp = try (try self.intcast(lt, Type.u32, Type.u64)).toLocal(self, Type.u64);4408 defer lt.free(self);
4373 const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64);4409 var tmp = try (try self.intcast(lt, Type.u32, Type.u64)).toLocal(self, Type.u64);
4410 defer tmp.free(self);
4411 var tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64);
4412 defer tmp_op.free(self);
43744413
4375 const overflow_bit = if (is_signed) blk: {4414 const overflow_bit = if (is_signed) blk: {
4376 const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);4415 const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
...@@ -4392,7 +4431,8 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type,...@@ -4392,7 +4431,8 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type,
43924431
4393 break :blk WValue{ .stack = {} };4432 break :blk WValue{ .stack = {} };
4394 };4433 };
4395 const overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1));4434 var overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1));
4435 defer overflow_local.free(self);
43964436
4397 const result_ptr = try self.allocStack(result_ty);4437 const result_ptr = try self.allocStack(result_ty);
4398 try self.store(result_ptr, high_op_res, Type.u64, 0);4438 try self.store(result_ptr, high_op_res, Type.u64, 0);
...@@ -4419,10 +4459,12 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4419,10 +4459,12 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4419 return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});4459 return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});
4420 };4460 };
44214461
4422 const shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty);4462 var shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty);
4423 const result = if (wasm_bits != int_info.bits) blk: {4463 defer shl.free(self);
4464 var result = if (wasm_bits != int_info.bits) blk: {
4424 break :blk try (try self.wrapOperand(shl, lhs_ty)).toLocal(self, lhs_ty);4465 break :blk try (try self.wrapOperand(shl, lhs_ty)).toLocal(self, lhs_ty);
4425 } else shl;4466 } else shl;
4467 defer result.free(self); // it's a no-op to free the same local twice (when wasm_bits == int_info.bits)
44264468
4427 const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: {4469 const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: {
4428 // emit lhs to stack to we can keep 'wrapped' on the stack also4470 // emit lhs to stack to we can keep 'wrapped' on the stack also
...@@ -4431,10 +4473,12 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4431,10 +4473,12 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4431 const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr);4473 const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr);
4432 break :blk try self.cmp(.{ .stack = {} }, wrapped, lhs_ty, .neq);4474 break :blk try self.cmp(.{ .stack = {} }, wrapped, lhs_ty, .neq);
4433 } else blk: {4475 } else blk: {
4434 const shr = try (try self.binOp(result, rhs, lhs_ty, .shr)).toLocal(self, lhs_ty);4476 try self.emitWValue(lhs);
4435 break :blk try self.cmp(lhs, shr, lhs_ty, .neq);4477 const shr = try self.binOp(result, rhs, lhs_ty, .shr);
4478 break :blk try self.cmp(.{ .stack = {} }, shr, lhs_ty, .neq);
4436 };4479 };
4437 const overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1));4480 var overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1));
4481 defer overflow_local.free(self);
44384482
4439 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));4483 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));
4440 try self.store(result_ptr, result, lhs_ty, 0);4484 try self.store(result_ptr, result, lhs_ty, 0);
...@@ -4457,7 +4501,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4457,7 +4501,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
44574501
4458 // We store the bit if it's overflowed or not in this. As it's zero-initialized4502 // We store the bit if it's overflowed or not in this. As it's zero-initialized
4459 // we only need to update it if an overflow (or underflow) occurred.4503 // we only need to update it if an overflow (or underflow) occurred.
4460 const overflow_bit = try self.allocLocal(Type.initTag(.u1));4504 var overflow_bit = try self.ensureAllocLocal(Type.initTag(.u1));
4505 defer overflow_bit.free(self);
4506
4461 const int_info = lhs_ty.intInfo(self.target);4507 const int_info = lhs_ty.intInfo(self.target);
4462 const wasm_bits = toWasmBits(int_info.bits) orelse {4508 const wasm_bits = toWasmBits(int_info.bits) orelse {
4463 return self.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits});4509 return self.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits});
...@@ -4487,7 +4533,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4487,7 +4533,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4487 break :blk try self.intcast(bin_op, new_ty, lhs_ty);4533 break :blk try self.intcast(bin_op, new_ty, lhs_ty);
4488 } else {4534 } else {
4489 const down_cast = try (try self.intcast(bin_op, new_ty, lhs_ty)).toLocal(self, lhs_ty);4535 const down_cast = try (try self.intcast(bin_op, new_ty, lhs_ty)).toLocal(self, lhs_ty);
4490 const shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty);4536 var shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty);
4537 defer shr.free(self);
44914538
4492 const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);4539 const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
4493 const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty);4540 const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty);
...@@ -4504,7 +4551,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4504,7 +4551,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4504 try self.addLabel(.local_set, overflow_bit.local);4551 try self.addLabel(.local_set, overflow_bit.local);
4505 break :blk try self.wrapOperand(bin_op, lhs_ty);4552 break :blk try self.wrapOperand(bin_op, lhs_ty);
4506 } else blk: {4553 } else blk: {
4507 const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(self, lhs_ty);4554 var bin_op = try (try self.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(self, lhs_ty);
4555 defer bin_op.free(self);
4508 const shift_imm = if (wasm_bits == 32)4556 const shift_imm = if (wasm_bits == 32)
4509 WValue{ .imm32 = int_info.bits }4557 WValue{ .imm32 = int_info.bits }
4510 else4558 else
...@@ -4514,7 +4562,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4514,7 +4562,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4514 try self.addLabel(.local_set, overflow_bit.local);4562 try self.addLabel(.local_set, overflow_bit.local);
4515 break :blk try self.wrapOperand(bin_op, lhs_ty);4563 break :blk try self.wrapOperand(bin_op, lhs_ty);
4516 };4564 };
4517 const bin_op_local = try bin_op.toLocal(self, lhs_ty);4565 var bin_op_local = try bin_op.toLocal(self, lhs_ty);
4566 defer bin_op_local.free(self);
45184567
4519 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));4568 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));
4520 try self.store(result_ptr, bin_op_local, lhs_ty, 0);4569 try self.store(result_ptr, bin_op_local, lhs_ty, 0);
...@@ -4572,12 +4621,13 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4572,12 +4621,13 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4572 const lhs_ext = try self.fpext(lhs, ty, Type.f32);4621 const lhs_ext = try self.fpext(lhs, ty, Type.f32);
4573 const addend_ext = try self.fpext(addend, ty, Type.f32);4622 const addend_ext = try self.fpext(addend, ty, Type.f32);
4574 // call to compiler-rt `fn fmaf(f32, f32, f32) f32`4623 // call to compiler-rt `fn fmaf(f32, f32, f32) f32`
4575 const result = try self.callIntrinsic(4624 var result = try self.callIntrinsic(
4576 "fmaf",4625 "fmaf",
4577 &.{ Type.f32, Type.f32, Type.f32 },4626 &.{ Type.f32, Type.f32, Type.f32 },
4578 Type.f32,4627 Type.f32,
4579 &.{ rhs_ext, lhs_ext, addend_ext },4628 &.{ rhs_ext, lhs_ext, addend_ext },
4580 );4629 );
4630 defer result.free(self);
4581 return try (try self.fptrunc(result, Type.f32, ty)).toLocal(self, ty);4631 return try (try self.fptrunc(result, Type.f32, ty)).toLocal(self, ty);
4582 }4632 }
45834633
...@@ -4611,7 +4661,8 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4611,7 +4661,8 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4611 try self.addTag(.i32_wrap_i64);4661 try self.addTag(.i32_wrap_i64);
4612 },4662 },
4613 128 => {4663 128 => {
4614 const lsb = try (try self.load(operand, Type.u64, 8)).toLocal(self, Type.u64);4664 var lsb = try (try self.load(operand, Type.u64, 8)).toLocal(self, Type.u64);
4665 defer lsb.free(self);
46154666
4616 try self.emitWValue(lsb);4667 try self.emitWValue(lsb);
4617 try self.addTag(.i64_clz);4668 try self.addTag(.i64_clz);
...@@ -4671,7 +4722,8 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4671,7 +4722,8 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4671 try self.addTag(.i32_wrap_i64);4722 try self.addTag(.i32_wrap_i64);
4672 },4723 },
4673 128 => {4724 128 => {
4674 const msb = try (try self.load(operand, Type.u64, 0)).toLocal(self, Type.u64);4725 var msb = try (try self.load(operand, Type.u64, 0)).toLocal(self, Type.u64);
4726 defer msb.free(self);
46754727
4676 try self.emitWValue(msb);4728 try self.emitWValue(msb);
4677 try self.addTag(.i64_ctz);4729 try self.addTag(.i64_ctz);
...@@ -4847,7 +4899,8 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4847,7 +4899,8 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4847 return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty);4899 return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty);
4848 },4900 },
4849 24 => {4901 24 => {
4850 const msb = try (try self.wrapOperand(operand, Type.u16)).toLocal(self, Type.u16);4902 var msb = try (try self.wrapOperand(operand, Type.u16)).toLocal(self, Type.u16);
4903 defer msb.free(self);
48514904
4852 const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl);4905 const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl);
4853 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and");4906 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and");
...@@ -4867,10 +4920,13 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4867,10 +4920,13 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4867 },4920 },
4868 32 => {4921 32 => {
4869 const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);4922 const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);
4870 const lhs = try (try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(self, ty);4923 var lhs = try (try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(self, ty);
4924 defer lhs.free(self);
4871 const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);4925 const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);
4872 const rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty);4926 var rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty);
4873 const tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty);4927 defer rhs.free(self);
4928 var tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty);
4929 defer tmp_or.free(self);
48744930
4875 const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl);4931 const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl);
4876 const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr);4932 const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr);
...@@ -5097,7 +5153,8 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -5097,7 +5153,8 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
5097 }5153 }
50985154
5099 const wasm_bits = toWasmBits(int_info.bits).?;5155 const wasm_bits = toWasmBits(int_info.bits).?;
5100 const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty);5156 var bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty);
5157 defer bin_result.free(self);
5101 if (wasm_bits != int_info.bits and op == .add) {5158 if (wasm_bits != int_info.bits and op == .add) {
5102 const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1);5159 const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1);
5103 const imm_val = switch (wasm_bits) {5160 const imm_val = switch (wasm_bits) {
...@@ -5130,10 +5187,10 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op...@@ -5130,10 +5187,10 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op
5130 const wasm_bits = toWasmBits(int_info.bits).?;5187 const wasm_bits = toWasmBits(int_info.bits).?;
5131 const is_wasm_bits = wasm_bits == int_info.bits;5188 const is_wasm_bits = wasm_bits == int_info.bits;
51325189
5133 const lhs = if (!is_wasm_bits) lhs: {5190 var lhs = if (!is_wasm_bits) lhs: {
5134 break :lhs try (try self.signAbsValue(lhs_operand, ty)).toLocal(self, ty);5191 break :lhs try (try self.signAbsValue(lhs_operand, ty)).toLocal(self, ty);
5135 } else lhs_operand;5192 } else lhs_operand;
5136 const rhs = if (!is_wasm_bits) rhs: {5193 var rhs = if (!is_wasm_bits) rhs: {
5137 break :rhs try (try self.signAbsValue(rhs_operand, ty)).toLocal(self, ty);5194 break :rhs try (try self.signAbsValue(rhs_operand, ty)).toLocal(self, ty);
5138 } else rhs_operand;5195 } else rhs_operand;
51395196
...@@ -5150,8 +5207,11 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op...@@ -5150,8 +5207,11 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op
5150 else => unreachable,5207 else => unreachable,
5151 };5208 };
51525209
5153 const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty);5210 var bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty);
5154 if (!is_wasm_bits) {5211 if (!is_wasm_bits) {
5212 defer bin_result.free(self); // not returned in this branch
5213 defer lhs.free(self); // uses temporary local for absvalue
5214 defer rhs.free(self); // uses temporary local for absvalue
5155 try self.emitWValue(bin_result);5215 try self.emitWValue(bin_result);
5156 try self.emitWValue(max_wvalue);5216 try self.emitWValue(max_wvalue);
5157 _ = try self.cmp(bin_result, max_wvalue, ty, .lt);5217 _ = try self.cmp(bin_result, max_wvalue, ty, .lt);
...@@ -5202,8 +5262,10 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -5202,8 +5262,10 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
5202 const result = try self.allocLocal(ty);5262 const result = try self.allocLocal(ty);
52035263
5204 if (wasm_bits == int_info.bits) {5264 if (wasm_bits == int_info.bits) {
5205 const shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty);5265 var shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty);
5206 const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);5266 defer shl.free(self);
5267 var shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);
5268 defer shr.free(self);
52075269
5208 switch (wasm_bits) {5270 switch (wasm_bits) {
5209 32 => blk: {5271 32 => blk: {
...@@ -5241,9 +5303,12 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -5241,9 +5303,12 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
5241 else => unreachable,5303 else => unreachable,
5242 };5304 };
52435305
5244 const shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty);5306 var shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty);
5245 const shl = try (try self.binOp(shl_res, rhs, ty, .shl)).toLocal(self, ty);5307 defer shl_res.free(self);
5246 const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);5308 var shl = try (try self.binOp(shl_res, rhs, ty, .shl)).toLocal(self, ty);
5309 defer shl.free(self);
5310 var shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);
5311 defer shr.free(self);
52475312
5248 switch (wasm_bits) {5313 switch (wasm_bits) {
5249 32 => blk: {5314 32 => blk: {
...@@ -5278,7 +5343,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -5278,7 +5343,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
5278 if (is_signed) {5343 if (is_signed) {
5279 shift_result = try self.wrapOperand(shift_result, ty);5344 shift_result = try self.wrapOperand(shift_result, ty);
5280 }5345 }
5281 return try shift_result.toLocal(self, ty);5346 return shift_result.toLocal(self, ty);
5282 }5347 }
5283}5348}
52845349