| ... | ... | @@ -77,17 +77,34 @@ const WValue = union(enum) { |
| 77 | 77 | /// Promotes a `WValue` to a local when given value is on top of the stack. |
| 78 | 78 | /// When encountering a `local` or `stack_offset` this is essentially a no-op. |
| 79 | 79 | /// All other tags are illegal. |
| 80 | | fn toLocal(self: WValue, gen: *Self, ty: Type) InnerError!WValue { |
| 81 | | switch (self) { |
| 80 | fn toLocal(value: WValue, gen: *Self, ty: Type) InnerError!WValue { |
| 81 | switch (value) { |
| 82 | 82 | .stack => { |
| 83 | 83 | const local = try gen.allocLocal(ty); |
| 84 | 84 | try gen.addLabel(.local_set, local.local); |
| 85 | 85 | return local; |
| 86 | 86 | }, |
| 87 | | .local, .stack_offset => return self, |
| 87 | .local, .stack_offset => return value, |
| 88 | 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 | }; |
| 92 | 109 | |
| 93 | 110 | /// Wasm ops, but without input/output/signedness information |
| ... | ... | @@ -829,27 +846,18 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue { |
| 829 | 846 | }, |
| 830 | 847 | } |
| 831 | 848 | // 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. |
| 854 | fn ensureAllocLocal(self: *Self, ty: Type) InnerError!WValue { |
| 855 | try self.locals.append(self.gpa, genValtype(ty, self.target)); |
| 833 | 856 | const initial_index = self.local_index; |
| 834 | 857 | self.local_index += 1; |
| 835 | 858 | return WValue{ .local = initial_index }; |
| 836 | 859 | } |
| 837 | 860 | |
| 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`. |
| 842 | | fn 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 | 861 | /// Generates a `wasm.Type` from a given function type. |
| 854 | 862 | /// Memory is owned by the caller. |
| 855 | 863 | fn 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 | 1205 | // Reserve a local to store the current stack pointer |
| 1198 | 1206 | // We can later use this local to set the stack pointer back to the value |
| 1199 | 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 | 1209 | // 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 | } |
| 1204 | 1212 | |
| 1205 | 1213 | /// 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 | 1338 | else => { |
| 1331 | 1339 | // TODO: We should probably lower this to a call to compiler_rt |
| 1332 | 1340 | // But for now, we implement it manually |
| 1333 | | const offset = try self.allocLocal(Type.usize); // local for counter |
| 1341 | var offset = try self.ensureAllocLocal(Type.usize); // local for counter |
| 1342 | defer offset.free(self); |
| 1343 | |
| 1334 | 1344 | // outer block to jump to when loop is done |
| 1335 | 1345 | try self.startBlock(.block, wasm.block_empty); |
| 1336 | 1346 | try self.startBlock(.loop, wasm.block_empty); |
| ... | ... | @@ -1467,7 +1477,7 @@ fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum |
| 1467 | 1477 | // do not perform arithmetic when offset is 0. |
| 1468 | 1478 | if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value; |
| 1469 | 1479 | const result_ptr: WValue = switch (action) { |
| 1470 | | .new => try self.allocLocal(Type.usize), |
| 1480 | .new => try self.ensureAllocLocal(Type.usize), |
| 1471 | 1481 | .modify => ptr_value, |
| 1472 | 1482 | }; |
| 1473 | 1483 | try self.emitWValue(ptr_value); |
| ... | ... | @@ -1715,7 +1725,10 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1715 | 1725 | fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 1716 | 1726 | for (body) |inst| { |
| 1717 | 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 | } |
| 1721 | 1734 | |
| ... | ... | @@ -2151,9 +2164,12 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr |
| 2151 | 2164 | } |
| 2152 | 2165 | |
| 2153 | 2166 | const result = try self.allocStack(ty); |
| 2154 | | const 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); |
| 2156 | | const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); |
| 2167 | var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); |
| 2168 | defer lhs_high_bit.free(self); |
| 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); |
| 2157 | 2173 | |
| 2158 | 2174 | const lhs_low_bit = try self.load(lhs, Type.u64, 8); |
| 2159 | 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 | 2181 | break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); |
| 2166 | 2182 | } else unreachable; |
| 2167 | 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); |
| 2169 | 2186 | |
| 2170 | 2187 | try self.store(result, high_op_res, Type.u64, 0); |
| 2171 | 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 | 3225 | } else if (wanted_bits == 128) { |
| 3209 | 3226 | // for 128bit integers we store the integer in the virtual stack, rather than a local |
| 3210 | 3227 | const stack_ptr = try self.allocStack(wanted); |
| 3228 | try self.emitWValue(stack_ptr); |
| 3211 | 3229 | |
| 3212 | 3230 | // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it |
| 3213 | 3231 | // meaning less store operations are required. |
| 3214 | 3232 | const lhs = if (op_bits == 32) blk: { |
| 3215 | | const tmp = try self.intcast( |
| 3216 | | operand, |
| 3217 | | given, |
| 3218 | | if (wanted.isSignedInt()) Type.i64 else Type.u64, |
| 3219 | | ); |
| 3220 | | break :blk try tmp.toLocal(self, Type.u64); |
| 3233 | break :blk try self.intcast(operand, given, if (wanted.isSignedInt()) Type.i64 else Type.u64); |
| 3221 | 3234 | } else operand; |
| 3222 | 3235 | |
| 3223 | 3236 | // 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()); |
| 3225 | 3238 | |
| 3226 | 3239 | // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value |
| 3227 | 3240 | if (wanted.isSignedInt()) { |
| 3228 | | const shr = try (try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr)).toLocal(self, Type.i64); |
| 3229 | | try self.store(stack_ptr, shr, Type.u64, 8); |
| 3241 | try self.emitWValue(stack_ptr); |
| 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 | 3244 | } else { |
| 3231 | 3245 | // Ensure memory of lsb is zero'd |
| 3232 | 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 | 3548 | try self.addTag(.i32_mul); |
| 3535 | 3549 | try self.addTag(.i32_add); |
| 3536 | 3550 | |
| 3537 | | const result = try self.allocLocal(elem_ty); |
| 3551 | var result = try self.allocLocal(elem_ty); |
| 3538 | 3552 | try self.addLabel(.local_set, result.local); |
| 3539 | 3553 | if (isByRef(elem_ty, self.target)) { |
| 3540 | 3554 | return result; |
| 3541 | 3555 | } |
| 3556 | defer result.free(self); // only free if it's not returned like above |
| 3542 | 3557 | |
| 3543 | 3558 | const elem_val = try self.load(result, elem_ty, 0); |
| 3544 | 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 | 3671 | else => { |
| 3657 | 3672 | // TODO: We should probably lower this to a call to compiler_rt |
| 3658 | 3673 | // But for now, we implement it manually |
| 3659 | | const offset = try self.allocLocal(Type.usize); // local for counter |
| 3674 | const offset = try self.ensureAllocLocal(Type.usize); // local for counter |
| 3660 | 3675 | // outer block to jump to when loop is done |
| 3661 | 3676 | try self.startBlock(.block, wasm.block_empty); |
| 3662 | 3677 | try self.startBlock(.loop, wasm.block_empty); |
| ... | ... | @@ -3713,12 +3728,14 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3713 | 3728 | try self.addTag(.i32_mul); |
| 3714 | 3729 | try self.addTag(.i32_add); |
| 3715 | 3730 | |
| 3716 | | const result = try self.allocLocal(Type.usize); |
| 3731 | var result = try self.allocLocal(Type.usize); |
| 3717 | 3732 | try self.addLabel(.local_set, result.local); |
| 3718 | 3733 | |
| 3719 | 3734 | if (isByRef(elem_ty, self.target)) { |
| 3720 | 3735 | return result; |
| 3721 | 3736 | } |
| 3737 | defer result.free(self); // only free if no longer needed and not returned like above |
| 3738 | |
| 3722 | 3739 | const elem_val = try self.load(result, elem_ty, 0); |
| 3723 | 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 | 3961 | |
| 3945 | 3962 | // We store the final result in here that will be validated |
| 3946 | 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); |
| 3948 | 3966 | |
| 3949 | 3967 | try self.startBlock(.block, wasm.block_empty); |
| 3950 | 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 | 3983 | try self.emitWValue(result); |
| 3966 | 3984 | try self.addImm32(0); |
| 3967 | 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 | 3986 | return WValue{ .stack = {} }; |
| 3971 | 3987 | } |
| 3972 | 3988 | |
| ... | ... | @@ -3980,8 +3996,10 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma |
| 3980 | 3996 | return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits}); |
| 3981 | 3997 | } |
| 3982 | 3998 | |
| 3983 | | const 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); |
| 3999 | var lhs_high_bit = try (try self.load(lhs, 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); |
| 3985 | 4003 | |
| 3986 | 4004 | switch (op) { |
| 3987 | 4005 | .eq, .neq => { |
| ... | ... | @@ -4313,17 +4331,19 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W |
| 4313 | 4331 | |
| 4314 | 4332 | // for signed integers, we first apply signed shifts by the difference in bits |
| 4315 | 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 | 4335 | break :blk try (try self.signAbsValue(lhs_op, lhs_ty)).toLocal(self, lhs_ty); |
| 4318 | 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 | 4338 | break :blk try (try self.signAbsValue(rhs_op, lhs_ty)).toLocal(self, lhs_ty); |
| 4321 | 4339 | } else rhs_op; |
| 4322 | 4340 | |
| 4323 | | 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: { |
| 4341 | var bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty); |
| 4342 | defer bin_op.free(self); |
| 4343 | var result = if (wasm_bits != int_info.bits) blk: { |
| 4325 | 4344 | break :blk try (try self.wrapOperand(bin_op, lhs_ty)).toLocal(self, lhs_ty); |
| 4326 | 4345 | } else bin_op; |
| 4346 | defer result.free(self); // no-op when wasm_bits == int_info.bits |
| 4327 | 4347 | |
| 4328 | 4348 | const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt; |
| 4329 | 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 | 4358 | try self.cmp(bin_op, lhs, lhs_ty, cmp_op) |
| 4339 | 4359 | else |
| 4340 | 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); |
| 4342 | 4363 | |
| 4343 | 4364 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |
| 4344 | 4365 | try self.store(result_ptr, result, lhs_ty, 0); |
| 4345 | 4366 | const offset = @intCast(u32, lhs_ty.abiSize(self.target)); |
| 4346 | 4367 | try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset); |
| 4347 | 4368 | |
| 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 | 4378 | return result_ptr; |
| 4349 | 4379 | } |
| 4350 | 4380 | |
| ... | ... | @@ -4356,21 +4386,30 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, |
| 4356 | 4386 | return self.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits}); |
| 4357 | 4387 | } |
| 4358 | 4388 | |
| 4359 | | const 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); |
| 4361 | | const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); |
| 4362 | | const rhs_low_bit = try (try self.load(rhs, Type.u64, 8)).toLocal(self, Type.u64); |
| 4389 | var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); |
| 4390 | defer lhs_high_bit.free(self); |
| 4391 | var lhs_low_bit = try (try self.load(lhs, 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); |
| 4363 | 4397 | |
| 4364 | | const 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); |
| 4398 | var low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_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); |
| 4366 | 4402 | |
| 4367 | | const lt = if (op == .add) blk: { |
| 4403 | var lt = if (op == .add) blk: { |
| 4368 | 4404 | break :blk try (try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32); |
| 4369 | 4405 | } else if (op == .sub) blk: { |
| 4370 | 4406 | break :blk try (try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32); |
| 4371 | 4407 | } else unreachable; |
| 4372 | | const tmp = try (try self.intcast(lt, Type.u32, Type.u64)).toLocal(self, Type.u64); |
| 4373 | | const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); |
| 4408 | defer lt.free(self); |
| 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); |
| 4374 | 4413 | |
| 4375 | 4414 | const overflow_bit = if (is_signed) blk: { |
| 4376 | 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 | 4431 | |
| 4393 | 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); |
| 4396 | 4436 | |
| 4397 | 4437 | const result_ptr = try self.allocStack(result_ty); |
| 4398 | 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 | 4459 | return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits}); |
| 4420 | 4460 | }; |
| 4421 | 4461 | |
| 4422 | | const shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty); |
| 4423 | | const result = if (wasm_bits != int_info.bits) blk: { |
| 4462 | var shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty); |
| 4463 | defer shl.free(self); |
| 4464 | var result = if (wasm_bits != int_info.bits) blk: { |
| 4424 | 4465 | break :blk try (try self.wrapOperand(shl, lhs_ty)).toLocal(self, lhs_ty); |
| 4425 | 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) |
| 4426 | 4468 | |
| 4427 | 4469 | const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: { |
| 4428 | 4470 | // 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 | 4473 | const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr); |
| 4432 | 4474 | break :blk try self.cmp(.{ .stack = {} }, wrapped, lhs_ty, .neq); |
| 4433 | 4475 | } else blk: { |
| 4434 | | const shr = try (try self.binOp(result, rhs, lhs_ty, .shr)).toLocal(self, lhs_ty); |
| 4435 | | break :blk try self.cmp(lhs, shr, lhs_ty, .neq); |
| 4476 | try self.emitWValue(lhs); |
| 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); |
| 4438 | 4482 | |
| 4439 | 4483 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |
| 4440 | 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 | 4501 | |
| 4458 | 4502 | // We store the bit if it's overflowed or not in this. As it's zero-initialized |
| 4459 | 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 | 4507 | const int_info = lhs_ty.intInfo(self.target); |
| 4462 | 4508 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 4463 | 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 | 4533 | break :blk try self.intcast(bin_op, new_ty, lhs_ty); |
| 4488 | 4534 | } else { |
| 4489 | 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); |
| 4491 | 4538 | |
| 4492 | 4539 | const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); |
| 4493 | 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 | 4551 | try self.addLabel(.local_set, overflow_bit.local); |
| 4505 | 4552 | break :blk try self.wrapOperand(bin_op, lhs_ty); |
| 4506 | 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 | 4556 | const shift_imm = if (wasm_bits == 32) |
| 4509 | 4557 | WValue{ .imm32 = int_info.bits } |
| 4510 | 4558 | else |
| ... | ... | @@ -4514,7 +4562,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4514 | 4562 | try self.addLabel(.local_set, overflow_bit.local); |
| 4515 | 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); |
| 4518 | 4567 | |
| 4519 | 4568 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |
| 4520 | 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 | 4621 | const lhs_ext = try self.fpext(lhs, ty, Type.f32); |
| 4573 | 4622 | const addend_ext = try self.fpext(addend, ty, Type.f32); |
| 4574 | 4623 | // call to compiler-rt `fn fmaf(f32, f32, f32) f32` |
| 4575 | | const result = try self.callIntrinsic( |
| 4624 | var result = try self.callIntrinsic( |
| 4576 | 4625 | "fmaf", |
| 4577 | 4626 | &.{ Type.f32, Type.f32, Type.f32 }, |
| 4578 | 4627 | Type.f32, |
| 4579 | 4628 | &.{ rhs_ext, lhs_ext, addend_ext }, |
| 4580 | 4629 | ); |
| 4630 | defer result.free(self); |
| 4581 | 4631 | return try (try self.fptrunc(result, Type.f32, ty)).toLocal(self, ty); |
| 4582 | 4632 | } |
| 4583 | 4633 | |
| ... | ... | @@ -4611,7 +4661,8 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4611 | 4661 | try self.addTag(.i32_wrap_i64); |
| 4612 | 4662 | }, |
| 4613 | 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); |
| 4615 | 4666 | |
| 4616 | 4667 | try self.emitWValue(lsb); |
| 4617 | 4668 | try self.addTag(.i64_clz); |
| ... | ... | @@ -4671,7 +4722,8 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4671 | 4722 | try self.addTag(.i32_wrap_i64); |
| 4672 | 4723 | }, |
| 4673 | 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); |
| 4675 | 4727 | |
| 4676 | 4728 | try self.emitWValue(msb); |
| 4677 | 4729 | try self.addTag(.i64_ctz); |
| ... | ... | @@ -4847,7 +4899,8 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4847 | 4899 | return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty); |
| 4848 | 4900 | }, |
| 4849 | 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); |
| 4851 | 4904 | |
| 4852 | 4905 | const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl); |
| 4853 | 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 | 4920 | }, |
| 4868 | 4921 | 32 => { |
| 4869 | 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 | 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); |
| 4873 | | const tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty); |
| 4926 | var rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).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); |
| 4874 | 4930 | |
| 4875 | 4931 | const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); |
| 4876 | 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 | 5153 | } |
| 5098 | 5154 | |
| 5099 | 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 | 5158 | if (wasm_bits != int_info.bits and op == .add) { |
| 5102 | 5159 | const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1); |
| 5103 | 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 | 5187 | const wasm_bits = toWasmBits(int_info.bits).?; |
| 5131 | 5188 | const is_wasm_bits = wasm_bits == int_info.bits; |
| 5132 | 5189 | |
| 5133 | | const lhs = if (!is_wasm_bits) lhs: { |
| 5190 | var lhs = if (!is_wasm_bits) lhs: { |
| 5134 | 5191 | break :lhs try (try self.signAbsValue(lhs_operand, ty)).toLocal(self, ty); |
| 5135 | 5192 | } else lhs_operand; |
| 5136 | | const rhs = if (!is_wasm_bits) rhs: { |
| 5193 | var rhs = if (!is_wasm_bits) rhs: { |
| 5137 | 5194 | break :rhs try (try self.signAbsValue(rhs_operand, ty)).toLocal(self, ty); |
| 5138 | 5195 | } else rhs_operand; |
| 5139 | 5196 | |
| ... | ... | @@ -5150,8 +5207,11 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op |
| 5150 | 5207 | else => unreachable, |
| 5151 | 5208 | }; |
| 5152 | 5209 | |
| 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 | 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 | 5215 | try self.emitWValue(bin_result); |
| 5156 | 5216 | try self.emitWValue(max_wvalue); |
| 5157 | 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 | 5262 | const result = try self.allocLocal(ty); |
| 5203 | 5263 | |
| 5204 | 5264 | if (wasm_bits == int_info.bits) { |
| 5205 | | const 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); |
| 5265 | var shl = try (try self.binOp(lhs, rhs, ty, .shl)).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); |
| 5207 | 5269 | |
| 5208 | 5270 | switch (wasm_bits) { |
| 5209 | 5271 | 32 => blk: { |
| ... | ... | @@ -5241,9 +5303,12 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 5241 | 5303 | else => unreachable, |
| 5242 | 5304 | }; |
| 5243 | 5305 | |
| 5244 | | const 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); |
| 5246 | | const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); |
| 5306 | var shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty); |
| 5307 | defer shl_res.free(self); |
| 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); |
| 5247 | 5312 | |
| 5248 | 5313 | switch (wasm_bits) { |
| 5249 | 5314 | 32 => blk: { |
| ... | ... | @@ -5278,7 +5343,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 5278 | 5343 | if (is_signed) { |
| 5279 | 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 | } |
| 5284 | 5349 | |