authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-07 14:24:18+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-07 14:24:18+02:00
log0c51e703f19d04edbfa26b7243b9bc125b5489a6
tree4eae12c1dfc0f05527ea83345ca418ca43ab0ed4
parent4df65fc26485fe32976561990393a35eb52ddfc6
signature Commit is signed but in an unrecognized format.

wasm: `@addWithOverflow` for bitsize 32


2 files changed, 60 insertions(+), 23 deletions(-)

src/arch/wasm/CodeGen.zig+59-23
......@@ -1818,11 +1818,11 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
18181818 try self.emitWValue(rhs);
18191819 }
18201820 const valtype = typeToValtype(ty, self.target);
1821 const abi_size = @intCast(u8, ty.bitSize(self.target));
1821 const abi_size = @intCast(u8, ty.abiSize(self.target));
18221822
18231823 const opcode = buildOpcode(.{
18241824 .valtype1 = valtype,
1825 .width = abi_size * 8, // use bitsize instead of byte size
1825 .width = abi_size * 8,
18261826 .op = .store,
18271827 });
18281828
......@@ -1853,10 +1853,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
18531853 // load local's value from memory by its stack position
18541854 try self.emitWValue(operand);
18551855
1856 const abi_size = @intCast(u8, ty.bitSize(self.target));
1856 const abi_size = @intCast(u8, ty.abiSize(self.target));
18571857 const opcode = buildOpcode(.{
18581858 .valtype1 = typeToValtype(ty, self.target),
1859 .width = abi_size * 8, // use bitsize instead of byte size
1859 .width = abi_size * 8,
18601860 .op = .load,
18611861 .signedness = .unsigned,
18621862 });
......@@ -2106,7 +2106,7 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index)
21062106/// Converts a signed integer to its 2's complement form and returns
21072107/// an unsigned integer instead.
21082108/// Asserts bitsize <= 64
2109fn convertTo2Complement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(@TypeOf(value)).Int.bits) {
2109fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(@TypeOf(value)).Int.bits) {
21102110 const T = @TypeOf(value);
21112111 comptime assert(@typeInfo(T) == .Int);
21122112 comptime assert(@typeInfo(T).Int.signedness == .signed);
......@@ -2137,7 +2137,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
21372137 const int_info = ty.intInfo(self.target);
21382138 switch (int_info.signedness) {
21392139 .signed => switch (int_info.bits) {
2140 0...32 => return WValue{ .imm32 = @intCast(u32, convertTo2Complement(
2140 0...32 => return WValue{ .imm32 = @intCast(u32, toTwosComplement(
21412141 val.toSignedInt(),
21422142 @intCast(u6, int_info.bits),
21432143 )) },
......@@ -2856,29 +2856,36 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28562856 const ty = self.air.getRefType(ty_op.ty);
28572857 const operand = try self.resolveInst(ty_op.operand);
28582858 const ref_ty = self.air.typeOf(ty_op.operand);
2859 const ref_info = ref_ty.intInfo(self.target);
2860 const wanted_info = ty.intInfo(self.target);
2859 if (ty.abiSize(self.target) > 8 or ref_ty.abiSize(self.target) > 8) {
2860 return self.fail("todo Wasm intcast for bitsize > 64", .{});
2861 }
2862 return self.intcast(operand, ty, ref_ty);
2863}
28612864
2862 const op_bits = toWasmBits(ref_info.bits) orelse
2863 return self.fail("TODO: Wasm intcast integer types of bitsize: {d}", .{ref_info.bits});
2864 const wanted_bits = toWasmBits(wanted_info.bits) orelse
2865 return self.fail("TODO: Wasm intcast integer types of bitsize: {d}", .{wanted_info.bits});
2865/// Upcasts or downcasts an integer based on the given and wanted types,
2866/// and stores the result in a new operand.
2867/// Asserts type's bitsize <= 64
2868fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
2869 const given_info = given.intInfo(self.target);
2870 const wanted_info = wanted.intInfo(self.target);
2871 assert(given_info.bits <= 64);
2872 assert(wanted_info.bits <= 64);
28662873
2867 // hot path
2874 const op_bits = toWasmBits(given_info.bits).?;
2875 const wanted_bits = toWasmBits(wanted_info.bits).?;
28682876 if (op_bits == wanted_bits) return operand;
28692877
2878 try self.emitWValue(operand);
28702879 if (op_bits > 32 and wanted_bits == 32) {
2871 try self.emitWValue(operand);
28722880 try self.addTag(.i32_wrap_i64);
28732881 } else if (op_bits == 32 and wanted_bits > 32) {
2874 try self.emitWValue(operand);
2875 try self.addTag(switch (ref_info.signedness) {
2882 try self.addTag(switch (wanted_info.signedness) {
28762883 .signed => .i64_extend_i32_s,
28772884 .unsigned => .i64_extend_i32_u,
28782885 });
28792886 } else unreachable;
28802887
2881 const result = try self.allocLocal(ty);
2888 const result = try self.allocLocal(wanted);
28822889 try self.addLabel(.local_set, result.local);
28832890 return result;
28842891}
......@@ -3983,7 +3990,7 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue
39833990 const cmp_res = try self.cmp(lhs, rhs, lhs_ty, .lt);
39843991 try self.emitWValue(cmp_res);
39853992 try self.addLabel(.local_set, overflow_bit.local);
3986 } else if (int_info.signedness == .signed and op != .shl) {
3993 } else if (int_info.signedness == .signed and op != .shl and op != .mul) {
39873994 // for overflow, we first check if lhs is > 0 (or lhs < 0 in case of subtraction). If not, we will not overflow.
39883995 // We first create an outer block, where we handle overflow.
39893996 // Then we create an inner block, where underflow is handled.
......@@ -4032,9 +4039,36 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue
40324039 try self.addLabel(.local_set, tmp_val.local);
40334040 break :blk tmp_val;
40344041 } else if (op == .mul) blk: {
4035 if (int_info.signedness == .signed) {
4036 const shift_val = convertTo2Complement(-@intCast(i17, int_info.bits), @intCast(u7, int_info.bits));
4037 const shift_imm = if (wasm_bits == 32) WValue{ .imm32 = shift_val } else WValue{ .imm64 = shift_val };
4042 // for 32 & 64 bitsize we calculate overflow
4043 // differently.
4044 if (int_info.bits == 32) {
4045 const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64;
4046 const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty);
4047 const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty);
4048 const bin_op = try self.binOp(lhs_upcast, rhs_upcast, new_ty, op);
4049 if (int_info.signedness == .unsigned) {
4050 const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
4051 const wrap = try self.intcast(shr, new_ty, lhs_ty);
4052 const cmp_res = try self.cmp(wrap, zero, lhs_ty, .neq);
4053 try self.emitWValue(cmp_res);
4054 try self.addLabel(.local_set, overflow_bit.local);
4055 break :blk try self.intcast(bin_op, new_ty, lhs_ty);
4056 } else {
4057 const down_cast = try self.intcast(bin_op, new_ty, lhs_ty);
4058 const shr = try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr);
4059
4060 const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
4061 const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty);
4062 const cmp_res = try self.cmp(down_shr_res, shr, lhs_ty, .neq);
4063 try self.emitWValue(cmp_res);
4064 try self.addLabel(.local_set, overflow_bit.local);
4065 break :blk down_cast;
4066 }
4067 } else if (int_info.signedness == .signed) {
4068 const shift_imm = if (wasm_bits == 32)
4069 WValue{ .imm32 = wasm_bits - int_info.bits }
4070 else
4071 WValue{ .imm64 = wasm_bits - int_info.bits };
40384072
40394073 const lhs_shl = try self.binOp(lhs, shift_imm, lhs_ty, .shl);
40404074 const lhs_shr = try self.binOp(lhs_shl, shift_imm, lhs_ty, .shr);
......@@ -4051,8 +4085,10 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue
40514085 break :blk try self.wrapOperand(bin_op, lhs_ty);
40524086 } else {
40534087 const bin_op = try self.binOp(lhs, rhs, lhs_ty, op);
4054 const shift_imm = if (wasm_bits == 32) WValue{ .imm32 = int_info.bits } else WValue{ .imm64 = int_info.bits };
4055 // const zero = if (wasm_bits == 32) WValue{ .imm32 = 0 } else WValue{ .imm64 = 0 };
4088 const shift_imm = if (wasm_bits == 32)
4089 WValue{ .imm32 = int_info.bits }
4090 else
4091 WValue{ .imm64 = int_info.bits };
40564092 const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr);
40574093 const cmp_op = try self.cmp(shr, zero, lhs_ty, .neq);
40584094 try self.emitWValue(cmp_op);
src/type.zig+1
......@@ -5952,6 +5952,7 @@ pub const Type = extern union {
59525952 pub const @"u64" = initTag(.u64);
59535953
59545954 pub const @"i32" = initTag(.i32);
5955 pub const @"i64" = initTag(.i64);
59555956
59565957 pub const @"f16" = initTag(.f16);
59575958 pub const @"f32" = initTag(.f32);