From d1bd9518f97abc9ab80795962b8e0dfd8e4d8768 Mon Sep 17 00:00:00 2001 From: Pavel Verigo Date: Thu, 18 Jul 2024 17:18:17 +0200 Subject: [PATCH] stage2-wasm: fix big int comparison Unexpected to be found only now --- src/arch/wasm/CodeGen.zig | 56 +++++++++---------- test/behavior/basic.zig | 113 +++++++++++++++++++++++--------------- 2 files changed, 97 insertions(+), 72 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 9b65425141..a17602b4c6 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2675,41 +2675,41 @@ fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) Inner .@"and", .@"or", .xor => { const result = try func.allocStack(ty); try func.emitWValue(result); - const lhs_high_bit = try func.load(lhs, Type.u64, 0); - const rhs_high_bit = try func.load(rhs, Type.u64, 0); - const op_high_bit = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); - try func.store(.stack, op_high_bit, Type.u64, result.offset()); + const lhs_low_bit = try func.load(lhs, Type.u64, 0); + const rhs_low_bit = try func.load(rhs, Type.u64, 0); + const op_low_bit = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); + try func.store(.stack, op_low_bit, Type.u64, result.offset()); try func.emitWValue(result); - const lhs_low_bit = try func.load(lhs, Type.u64, 8); - const rhs_low_bit = try func.load(rhs, Type.u64, 8); - const op_low_bit = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); - try func.store(.stack, op_low_bit, Type.u64, result.offset() + 8); + const lhs_high_bit = try func.load(lhs, Type.u64, 8); + const rhs_high_bit = try func.load(rhs, Type.u64, 8); + const op_high_bit = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); + try func.store(.stack, op_high_bit, Type.u64, result.offset() + 8); return result; }, .add, .sub => { const result = try func.allocStack(ty); - var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64); - defer lhs_high_bit.free(func); - var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64); - defer rhs_high_bit.free(func); - var high_op_res = try (try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(func, Type.u64); - defer high_op_res.free(func); + var lhs_low_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64); + defer lhs_low_bit.free(func); + var rhs_low_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64); + defer rhs_low_bit.free(func); + var low_op_res = try (try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(func, Type.u64); + defer low_op_res.free(func); - const lhs_low_bit = try func.load(lhs, Type.u64, 8); - const rhs_low_bit = try func.load(rhs, Type.u64, 8); - const low_op_res = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); + const lhs_high_bit = try func.load(lhs, Type.u64, 8); + const rhs_high_bit = try func.load(rhs, Type.u64, 8); + const high_op_res = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); const lt = if (op == .add) blk: { - break :blk try func.cmp(high_op_res, rhs_high_bit, Type.u64, .lt); + break :blk try func.cmp(low_op_res, rhs_low_bit, Type.u64, .lt); } else if (op == .sub) blk: { - break :blk try func.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); + break :blk try func.cmp(lhs_low_bit, rhs_low_bit, Type.u64, .lt); } else unreachable; const tmp = try func.intcast(lt, Type.u32, Type.u64); - var tmp_op = try (try func.binOp(low_op_res, tmp, Type.u64, op)).toLocal(func, Type.u64); + var tmp_op = try (try func.binOp(high_op_res, tmp, Type.u64, op)).toLocal(func, Type.u64); defer tmp_op.free(func); - try func.store(result, high_op_res, Type.u64, 0); + try func.store(result, low_op_res, Type.u64, 0); try func.store(result, tmp_op, Type.u64, 8); return result; }, @@ -5523,16 +5523,16 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std return func.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.bitSize(pt)}); } - var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64); + var lhs_high_bit = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64); defer lhs_high_bit.free(func); - var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64); + var rhs_high_bit = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64); defer rhs_high_bit.free(func); switch (op) { .eq, .neq => { const xor_high = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor); - const lhs_low_bit = try func.load(lhs, Type.u64, 8); - const rhs_low_bit = try func.load(rhs, Type.u64, 8); + const lhs_low_bit = try func.load(lhs, Type.u64, 0); + const rhs_low_bit = try func.load(rhs, Type.u64, 0); const xor_low = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor); const or_result = try func.binOp(xor_high, xor_low, Type.u64, .@"or"); @@ -5545,9 +5545,9 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std else => { const ty = if (operand_ty.isSignedInt(mod)) Type.i64 else Type.u64; // leave those value on top of the stack for '.select' - const lhs_low_bit = try func.load(lhs, Type.u64, 8); - const rhs_low_bit = try func.load(rhs, Type.u64, 8); - _ = try func.cmp(lhs_low_bit, rhs_low_bit, ty, op); + const lhs_low_bit = try func.load(lhs, Type.u64, 0); + const rhs_low_bit = try func.load(rhs, Type.u64, 0); + _ = try func.cmp(lhs_low_bit, rhs_low_bit, Type.u64, op); _ = try func.cmp(lhs_high_bit, rhs_high_bit, ty, op); _ = try func.cmp(lhs_high_bit, rhs_high_bit, ty, .eq); try func.addTag(.select); diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index fb61247b11..05d6549683 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -1134,55 +1134,80 @@ test "pointer to struct literal with runtime field is constant" { try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_const); } -test "integer compare" { +fn testSignedCmp(comptime T: type) !void { + var z: T = 0; + var p: T = 123; + var n: T = -123; + var min: T = std.math.minInt(T); + var max: T = std.math.maxInt(T); + var half_min: T = std.math.minInt(T) / 2; + var half_max: T = std.math.minInt(T) / 2; + _ = .{ &z, &p, &n, &min, &max, &half_min, &half_max }; + try expect(z == z and z != p and z != n); + try expect(p == p and p != n and n == n); + try expect(z > n and z < p and z >= n and z <= p); + try expect(!(z < n or z > p or z <= n or z >= p or z > z or z < z)); + 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); + 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)); + try expect(z == 0 and z != 123 and z != -123 and 0 == z and 0 != p and 0 != n); + try expect(z > -123 and p > -123 and !(n > 123)); + try expect(z < 123 and !(p < 123) and n < 123); + try expect(-123 <= z and -123 <= p and -123 <= n); + try expect(123 >= z and 123 >= p and 123 >= n); + try expect(!(0 != z or 123 != p or -123 != n)); + try expect(!(z > 0 or -123 > p or 123 < n)); + + try expect(min <= max and z <= max and p <= max and n <= max and half_max <= max and half_min <= max); + try expect(min <= max and min <= z and min <= p and min <= n and min <= half_min and min <= half_max); +} + +fn testUnsignedCmp(comptime T: type) !void { + var z: T = 0; + var p: T = 123; + var max: T = std.math.maxInt(T); + var half_max: T = std.math.minInt(T) / 2; + _ = .{ &z, &p, &max, &half_max }; + try expect(z == z and z != p); + try expect(p == p); + try expect(z < p and z <= p); + try expect(!(z > p or z >= p or z > z or z < z)); + try expect(p >= p and p <= p); + try expect(!(p > p or p < p)); + try expect(z == 0 and z != 123 and z != -123 and 0 == z and 0 != p); + try expect(z > -123 and p > -123); + try expect(z < 123 and !(p < 123)); + try expect(-123 <= z and -123 <= p); + try expect(123 >= z and 123 >= p); + try expect(!(0 != z or 123 != p)); + try expect(!(z > 0 or -123 > p)); + + try expect(z <= max and p <= max and half_max <= max); + try expect(half_max != max); +} + +test "integer compare <= 64 bits" { if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const S = struct { - fn doTheTestSigned(comptime T: type) !void { - var z: T = 0; - var p: T = 123; - var n: T = -123; - _ = .{ &z, &p, &n }; - try expect(z == z and z != p and z != n); - try expect(p == p and p != n and n == n); - try expect(z > n and z < p and z >= n and z <= p); - try expect(!(z < n or z > p or z <= n or z >= p or z > z or z < z)); - 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); - 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)); - try expect(z == 0 and z != 123 and z != -123 and 0 == z and 0 != p and 0 != n); - try expect(z > -123 and p > -123 and !(n > 123)); - try expect(z < 123 and !(p < 123) and n < 123); - try expect(-123 <= z and -123 <= p and -123 <= n); - try expect(123 >= z and 123 >= p and 123 >= n); - try expect(!(0 != z or 123 != p or -123 != n)); - try expect(!(z > 0 or -123 > p or 123 < n)); - } - fn doTheTestUnsigned(comptime T: type) !void { - var z: T = 0; - var p: T = 123; - _ = .{ &z, &p }; - try expect(z == z and z != p); - try expect(p == p); - try expect(z < p and z <= p); - try expect(!(z > p or z >= p or z > z or z < z)); - try expect(p >= p and p <= p); - try expect(!(p > p or p < p)); - try expect(z == 0 and z != 123 and z != -123 and 0 == z and 0 != p); - try expect(z > -123 and p > -123); - try expect(z < 123 and !(p < 123)); - try expect(-123 <= z and -123 <= p); - try expect(123 >= z and 123 >= p); - try expect(!(0 != z or 123 != p)); - try expect(!(z > 0 or -123 > p)); - } - }; inline for (.{ u8, u16, u32, u64, usize, u10, u20, u30, u60 }) |T| { - try S.doTheTestUnsigned(T); - try comptime S.doTheTestUnsigned(T); + try testUnsignedCmp(T); + try comptime testUnsignedCmp(T); } inline for (.{ i8, i16, i32, i64, isize, i10, i20, i30, i60 }) |T| { - try S.doTheTestSigned(T); - try comptime S.doTheTestSigned(T); + try testSignedCmp(T); + try comptime testSignedCmp(T); + } +} + +test "integer compare <= 128 bits" { + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + inline for (.{ u65, u96, u127, u128 }) |T| { + try testUnsignedCmp(T); + try comptime testUnsignedCmp(T); + } + inline for (.{ i65, i96, i127, i128 }) |T| { + try testSignedCmp(T); + try comptime testSignedCmp(T); } }