authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-07-21 21:18:58+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-07-22 02:12:07+02:00
log619140c0d2abd22da668747820f0ee80b497eb24
tree9ea7d8506956202bc7560d87990b8f47b424b881
parent8924f81d8cd96f5a69a54d87119a748247079a09

wasm: correctly intcast signed integers

When a signed integer's bitsize is not 32 or 64, but the given bitsize and wanted bitsize are either both represented by Wasm's i32 or i64, we must either sign extend or wrap the integer.

2 files changed, 29 insertions(+), 2 deletions(-)

src/arch/wasm/CodeGen.zig+14-2
...@@ -4160,7 +4160,7 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4160,7 +4160,7 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41604160
4161 const op_bits = toWasmBits(@as(u16, @intCast(operand_ty.bitSize(mod)))).?;4161 const op_bits = toWasmBits(@as(u16, @intCast(operand_ty.bitSize(mod)))).?;
4162 const wanted_bits = toWasmBits(@as(u16, @intCast(ty.bitSize(mod)))).?;4162 const wanted_bits = toWasmBits(@as(u16, @intCast(ty.bitSize(mod)))).?;
4163 const result = if (op_bits == wanted_bits)4163 const result = if (op_bits == wanted_bits and !ty.isSignedInt(mod))
4164 func.reuseOperand(ty_op.operand, operand)4164 func.reuseOperand(ty_op.operand, operand)
4165 else4165 else
4166 try (try func.intcast(operand, operand_ty, ty)).toLocal(func, ty);4166 try (try func.intcast(operand, operand_ty, ty)).toLocal(func, ty);
...@@ -4181,7 +4181,19 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro...@@ -4181,7 +4181,19 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
41814181
4182 const op_bits = toWasmBits(given_bitsize).?;4182 const op_bits = toWasmBits(given_bitsize).?;
4183 const wanted_bits = toWasmBits(wanted_bitsize).?;4183 const wanted_bits = toWasmBits(wanted_bitsize).?;
4184 if (op_bits == wanted_bits) return operand;4184 if (op_bits == wanted_bits) {
4185 if (given.isSignedInt(mod)) {
4186 if (given_bitsize < wanted_bitsize) {
4187 // signed integers are stored as two's complement,
4188 // when we upcast from a smaller integer to larger
4189 // integers, we must get its absolute value similar to
4190 // i64_extend_i32_s instruction.
4191 return func.signAbsValue(operand, given);
4192 }
4193 return func.wrapOperand(operand, wanted);
4194 }
4195 return operand;
4196 }
41854197
4186 if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) {4198 if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) {
4187 try func.emitWValue(operand);4199 try func.emitWValue(operand);
test/behavior/cast_int.zig+15
...@@ -14,3 +14,18 @@ test "@intCast i32 to u7" {...@@ -14,3 +14,18 @@ test "@intCast i32 to u7" {
14 var z = x >> @as(u7, @intCast(y));14 var z = x >> @as(u7, @intCast(y));
15 try expect(z == 0xff);15 try expect(z == 0xff);
16}16}
17
18test "coerce i8 to i32 and @intCast back" {
19 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
20 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
21 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
22 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
23
24 var x: i8 = -5;
25 var y: i32 = -5;
26 try expect(y == x);
27
28 var x2: i32 = -5;
29 var y2: i8 = -5;
30 try expect(y2 == @as(i8, @intCast(x2)));
31}