authorgravatar for xavierb@gmail.comXavier Bouchoux <xavierb@gmail.com> 2023-10-14 10:15:11+02:00
committergravatar for xavierb@gmail.comXavier Bouchoux <xavierb@gmail.com> 2023-10-14 10:23:36+02:00
logd5991ee7cae44531b38483e6a31c5daf59fa175d
tree1bd13ed6ecd638c6349d31dfc3c0731edcbce151
parent27a19908edb3916344f4c41899a4a9c9a53a1528

codegen/wasm: fix non-byte-sized signed integer comparison


2 files changed, 86 insertions(+), 17 deletions(-)

src/arch/wasm/CodeGen.zig+38-17
......@@ -3602,11 +3602,6 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO
36023602 return func.cmpBigInt(lhs, rhs, ty, op);
36033603 }
36043604
3605 // ensure that when we compare pointers, we emit
3606 // the true pointer of a stack value, rather than the stack pointer.
3607 try func.lowerToStack(lhs);
3608 try func.lowerToStack(rhs);
3609
36103605 const signedness: std.builtin.Signedness = blk: {
36113606 // by default we tell the operand type is unsigned (i.e. bools and enum values)
36123607 if (ty.zigTypeTag(mod) != .Int) break :blk .unsigned;
......@@ -3614,6 +3609,30 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO
36143609 // incase of an actual integer, we emit the correct signedness
36153610 break :blk ty.intInfo(mod).signedness;
36163611 };
3612 const extend_sign = blk: {
3613 // do we need to extend the sign bit?
3614 if (signedness != .signed) break :blk false;
3615 if (op == .eq or op == .neq) break :blk false;
3616 const int_bits = ty.intInfo(mod).bits;
3617 const wasm_bits = toWasmBits(int_bits) orelse unreachable;
3618 break :blk (wasm_bits != int_bits);
3619 };
3620
3621 const lhs_wasm = if (extend_sign)
3622 try func.signExtendInt(lhs, ty)
3623 else
3624 lhs;
3625
3626 const rhs_wasm = if (extend_sign)
3627 try func.signExtendInt(rhs, ty)
3628 else
3629 rhs;
3630
3631 // ensure that when we compare pointers, we emit
3632 // the true pointer of a stack value, rather than the stack pointer.
3633 try func.lowerToStack(lhs_wasm);
3634 try func.lowerToStack(rhs_wasm);
3635
36173636 const opcode: wasm.Opcode = buildOpcode(.{
36183637 .valtype1 = typeToValtype(ty, mod),
36193638 .op = switch (op) {
......@@ -6920,12 +6939,13 @@ fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type,
69206939 const int_info = ty.intInfo(mod);
69216940 const wasm_bits = toWasmBits(int_info.bits).?;
69226941 const is_wasm_bits = wasm_bits == int_info.bits;
6942 const ext_ty = if (!is_wasm_bits) try mod.intType(int_info.signedness, wasm_bits) else ty;
69236943
69246944 var lhs = if (!is_wasm_bits) lhs: {
6925 break :lhs try (try func.signExtendInt(lhs_operand, ty)).toLocal(func, ty);
6945 break :lhs try (try func.signExtendInt(lhs_operand, ty)).toLocal(func, ext_ty);
69266946 } else lhs_operand;
69276947 var rhs = if (!is_wasm_bits) rhs: {
6928 break :rhs try (try func.signExtendInt(rhs_operand, ty)).toLocal(func, ty);
6948 break :rhs try (try func.signExtendInt(rhs_operand, ty)).toLocal(func, ext_ty);
69296949 } else rhs_operand;
69306950
69316951 const max_val: u64 = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(int_info.bits - 1))) - 1));
......@@ -6941,20 +6961,20 @@ fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type,
69416961 else => unreachable,
69426962 };
69436963
6944 var bin_result = try (try func.binOp(lhs, rhs, ty, op)).toLocal(func, ty);
6964 var bin_result = try (try func.binOp(lhs, rhs, ext_ty, op)).toLocal(func, ext_ty);
69456965 if (!is_wasm_bits) {
69466966 defer bin_result.free(func); // not returned in this branch
69476967 defer lhs.free(func); // uses temporary local for absvalue
69486968 defer rhs.free(func); // uses temporary local for absvalue
69496969 try func.emitWValue(bin_result);
69506970 try func.emitWValue(max_wvalue);
6951 _ = try func.cmp(bin_result, max_wvalue, ty, .lt);
6971 _ = try func.cmp(bin_result, max_wvalue, ext_ty, .lt);
69526972 try func.addTag(.select);
69536973 try func.addLabel(.local_set, bin_result.local.value); // re-use local
69546974
69556975 try func.emitWValue(bin_result);
69566976 try func.emitWValue(min_wvalue);
6957 _ = try func.cmp(bin_result, min_wvalue, ty, .gt);
6977 _ = try func.cmp(bin_result, min_wvalue, ext_ty, .gt);
69586978 try func.addTag(.select);
69596979 try func.addLabel(.local_set, bin_result.local.value); // re-use local
69606980 return (try func.wrapOperand(bin_result, ty)).toLocal(func, ty);
......@@ -7036,12 +7056,13 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
70367056 64 => WValue{ .imm64 = shift_size },
70377057 else => unreachable,
70387058 };
7059 const ext_ty = try mod.intType(int_info.signedness, wasm_bits);
70397060
7040 var shl_res = try (try func.binOp(lhs, shift_value, ty, .shl)).toLocal(func, ty);
7061 var shl_res = try (try func.binOp(lhs, shift_value, ext_ty, .shl)).toLocal(func, ext_ty);
70417062 defer shl_res.free(func);
7042 var shl = try (try func.binOp(shl_res, rhs, ty, .shl)).toLocal(func, ty);
7063 var shl = try (try func.binOp(shl_res, rhs, ext_ty, .shl)).toLocal(func, ext_ty);
70437064 defer shl.free(func);
7044 var shr = try (try func.binOp(shl, rhs, ty, .shr)).toLocal(func, ty);
7065 var shr = try (try func.binOp(shl, rhs, ext_ty, .shr)).toLocal(func, ext_ty);
70457066 defer shr.free(func);
70467067
70477068 switch (wasm_bits) {
......@@ -7053,7 +7074,7 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
70537074
70547075 try func.addImm32(std.math.minInt(i32));
70557076 try func.addImm32(std.math.maxInt(i32));
7056 _ = try func.cmp(shl_res, .{ .imm32 = 0 }, ty, .lt);
7077 _ = try func.cmp(shl_res, .{ .imm32 = 0 }, ext_ty, .lt);
70577078 try func.addTag(.select);
70587079 },
70597080 64 => blk: {
......@@ -7064,16 +7085,16 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
70647085
70657086 try func.addImm64(@as(u64, @bitCast(@as(i64, std.math.minInt(i64)))));
70667087 try func.addImm64(@as(u64, @bitCast(@as(i64, std.math.maxInt(i64)))));
7067 _ = try func.cmp(shl_res, .{ .imm64 = 0 }, ty, .lt);
7088 _ = try func.cmp(shl_res, .{ .imm64 = 0 }, ext_ty, .lt);
70687089 try func.addTag(.select);
70697090 },
70707091 else => unreachable,
70717092 }
70727093 try func.emitWValue(shl);
7073 _ = try func.cmp(shl_res, shr, ty, .neq);
7094 _ = try func.cmp(shl_res, shr, ext_ty, .neq);
70747095 try func.addTag(.select);
70757096 try func.addLabel(.local_set, result.local.value);
7076 var shift_result = try func.binOp(result, shift_value, ty, .shr);
7097 var shift_result = try func.binOp(result, shift_value, ext_ty, .shr);
70777098 if (is_signed) {
70787099 shift_result = try func.wrapOperand(shift_result, ty);
70797100 }
test/behavior/basic.zig+48
......@@ -1172,3 +1172,51 @@ test "pointer to struct literal with runtime field is constant" {
11721172 const ptr = &S{ .data = runtime_zero };
11731173 try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_const);
11741174}
1175
1176test "integer compare" {
1177 const S = struct {
1178 fn doTheTestSigned(comptime T: type) !void {
1179 var z: T = 0;
1180 var p: T = 123;
1181 var n: T = -123;
1182 try expect(z == z and z != p and z != n);
1183 try expect(p == p and p != n and n == n);
1184 try expect(z > n and z < p and z >= n and z <= p);
1185 try expect(!(z < n or z > p or z <= n or z >= p or z > z or z < z));
1186 try expect(p > n and n < p and p >= n and n <= p and p >= p and p <= p and n >= n and n <= n);
1187 try expect(!(p < n or n > p or p <= n or n >= p or p > p or p < p or n > n or n < n));
1188 try expect(z == 0 and z != 123 and z != -123 and 0 == z and 0 != p and 0 != n);
1189 try expect(z > -123 and p > -123 and !(n > 123));
1190 try expect(z < 123 and !(p < 123) and n < 123);
1191 try expect(-123 <= z and -123 <= p and -123 <= n);
1192 try expect(123 >= z and 123 >= p and 123 >= n);
1193 try expect(!(0 != z or 123 != p or -123 != n));
1194 try expect(!(z > 0 or -123 > p or 123 < n));
1195 }
1196 fn doTheTestUnsigned(comptime T: type) !void {
1197 var z: T = 0;
1198 var p: T = 123;
1199 try expect(z == z and z != p);
1200 try expect(p == p);
1201 try expect(z < p and z <= p);
1202 try expect(!(z > p or z >= p or z > z or z < z));
1203 try expect(p >= p and p <= p);
1204 try expect(!(p > p or p < p));
1205 try expect(z == 0 and z != 123 and z != -123 and 0 == z and 0 != p);
1206 try expect(z > -123 and p > -123);
1207 try expect(z < 123 and !(p < 123));
1208 try expect(-123 <= z and -123 <= p);
1209 try expect(123 >= z and 123 >= p);
1210 try expect(!(0 != z or 123 != p));
1211 try expect(!(z > 0 or -123 > p));
1212 }
1213 };
1214 inline for (.{ u8, u16, u32, u64, usize, u10, u20, u30, u60 }) |T| {
1215 try S.doTheTestUnsigned(T);
1216 try comptime S.doTheTestUnsigned(T);
1217 }
1218 inline for (.{ i8, i16, i32, i64, isize, i10, i20, i30, i60 }) |T| {
1219 try S.doTheTestSigned(T);
1220 try comptime S.doTheTestSigned(T);
1221 }
1222}