authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-10 19:23:55+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-18 07:43:33+02:00
logc0ad0606df9a5bb657de70cd42229e20a91c51fc
tree394af602a003316ff6ebb86979886cc52916de93
parentf3517a1aa6058e9b09d57e81ecc81f27f086d163

wasm: Support 128bit integer coercion

The Wasm backend now correctly supports coercing a smaller integer into a 128bit integer. Regardless of signedness.

1 files changed, 45 insertions(+), 11 deletions(-)

src/arch/wasm/CodeGen.zig+45-11
...@@ -1210,7 +1210,7 @@ fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1210,7 +1210,7 @@ fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue {
12101210
1211/// From given zig bitsize, returns the wasm bitsize1211/// From given zig bitsize, returns the wasm bitsize
1212fn toWasmBits(bits: u16) ?u16 {1212fn toWasmBits(bits: u16) ?u16 {
1213 return for ([_]u16{ 32, 64 }) |wasm_bits| {1213 return for ([_]u16{ 32, 64, 128 }) |wasm_bits| {
1214 if (bits <= wasm_bits) return wasm_bits;1214 if (bits <= wasm_bits) return wasm_bits;
1215 } else null;1215 } else null;
1216}1216}
...@@ -1837,8 +1837,11 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1837,8 +1837,11 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1837 }1837 }
1838 },1838 },
1839 .Int => if (ty.intInfo(self.target).bits > 64) {1839 .Int => if (ty.intInfo(self.target).bits > 64) {
1840 const len = @intCast(u32, ty.abiSize(self.target));1840 const lsb = try self.load(rhs, Type.u64, 0);
1841 return self.memcpy(lhs, rhs, .{ .imm32 = len });1841 const msb = try self.load(rhs, Type.u64, 8);
1842 try self.store(lhs, lsb, Type.u64, 0);
1843 try self.store(lhs, msb, Type.u64, 8);
1844 return;
1842 },1845 },
1843 else => {},1846 else => {},
1844 }1847 }
...@@ -2900,8 +2903,11 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2900,8 +2903,11 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2900 const ty = self.air.getRefType(ty_op.ty);2903 const ty = self.air.getRefType(ty_op.ty);
2901 const operand = try self.resolveInst(ty_op.operand);2904 const operand = try self.resolveInst(ty_op.operand);
2902 const operand_ty = self.air.typeOf(ty_op.operand);2905 const operand_ty = self.air.typeOf(ty_op.operand);
2903 if (ty.abiSize(self.target) > 8 or operand_ty.abiSize(self.target) > 8) {2906 if (ty.zigTypeTag() == .Vector or operand_ty.zigTypeTag() == .Vector) {
2904 return self.fail("todo Wasm intcast for bitsize > 64", .{});2907 return self.fail("todo Wasm intcast for vectors", .{});
2908 }
2909 if (ty.abiSize(self.target) > 16 or operand_ty.abiSize(self.target) > 16) {
2910 return self.fail("todo Wasm intcast for bitsize > 128", .{});
2905 }2911 }
29062912
2907 return self.intcast(operand, operand_ty, ty);2913 return self.intcast(operand, operand_ty, ty);
...@@ -2909,25 +2915,53 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2909,25 +2915,53 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29092915
2910/// Upcasts or downcasts an integer based on the given and wanted types,2916/// Upcasts or downcasts an integer based on the given and wanted types,
2911/// and stores the result in a new operand.2917/// and stores the result in a new operand.
2912/// Asserts type's bitsize <= 642918/// Asserts type's bitsize <= 128
2913fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue {2919fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
2914 const given_info = given.intInfo(self.target);2920 const given_info = given.intInfo(self.target);
2915 const wanted_info = wanted.intInfo(self.target);2921 const wanted_info = wanted.intInfo(self.target);
2916 assert(given_info.bits <= 64);2922 assert(given_info.bits <= 128);
2917 assert(wanted_info.bits <= 64);2923 assert(wanted_info.bits <= 128);
29182924
2919 const op_bits = toWasmBits(given_info.bits).?;2925 const op_bits = toWasmBits(given_info.bits).?;
2920 const wanted_bits = toWasmBits(wanted_info.bits).?;2926 const wanted_bits = toWasmBits(wanted_info.bits).?;
2921 if (op_bits == wanted_bits) return operand;2927 if (op_bits == wanted_bits) return operand;
29222928
2923 try self.emitWValue(operand);2929 if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) {
2924 if (op_bits > 32 and wanted_bits == 32) {2930 try self.emitWValue(operand);
2925 try self.addTag(.i32_wrap_i64);2931 try self.addTag(.i32_wrap_i64);
2926 } else if (op_bits == 32 and wanted_bits > 32) {2932 } else if (op_bits == 32 and wanted_bits > 32 and wanted_bits <= 64) {
2933 try self.emitWValue(operand);
2927 try self.addTag(switch (wanted_info.signedness) {2934 try self.addTag(switch (wanted_info.signedness) {
2928 .signed => .i64_extend_i32_s,2935 .signed => .i64_extend_i32_s,
2929 .unsigned => .i64_extend_i32_u,2936 .unsigned => .i64_extend_i32_u,
2930 });2937 });
2938 } else if (wanted_bits == 128) {
2939 // for 128bit integers we store the integer in the virtual stack, rather than a local
2940 const stack_ptr = try self.allocStack(wanted);
2941
2942 // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it
2943 // meaning less store operations are required.
2944 const lhs = if (op_bits == 32) blk: {
2945 const tmp = try self.intcast(
2946 operand,
2947 given,
2948 if (wanted.isSignedInt()) Type.i64 else Type.u64,
2949 );
2950 break :blk tmp;
2951 } else operand;
2952
2953 // store msb first
2954 try self.store(stack_ptr, lhs, Type.u64, 0);
2955
2956 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value
2957 if (wanted.isSignedInt()) {
2958 const shr = try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
2959 try self.store(stack_ptr, shr, Type.u64, 8);
2960 } else {
2961 // Ensure memory of lsb is zero'd
2962 try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
2963 }
2964 return stack_ptr;
2931 } else unreachable;2965 } else unreachable;
29322966
2933 const result = try self.allocLocal(wanted);2967 const result = try self.allocLocal(wanted);