commit df79ea941bc3c355dfcb51c1de3d2d4fd186dd46 (tree)
parent 0ebd270d907a3fb7e2e600891fe2f455b7b2d4ad
Author: Pavel Verigo <paul.verigo@gmail.com>
Date: Tue, 7 Apr 2026 00:25:49 +0200
stage2-wasm: bigint abs + test min/max
Diffstat:
4 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/lib/compiler_rt/limb64.zig b/lib/compiler_rt/limb64.zig
@@ -977,3 +977,53 @@ test __mulo_limb64 {
try test__mulo_limb64(i200, maxInt(i200), maxInt(i200), .{ 1, true });
try test__mulo_limb64(i200, minInt(i200), minInt(i200), .{ 0, true });
}
+
+comptime {
+ symbol(&__abs_limb64, "__abs_limb64");
+}
+
+fn __abs_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, bits: u16) callconv(.c) void {
+ const limb_cnt = limbCount(bits);
+ const out = out_ptr[0..limb_cnt];
+ const a = a_ptr[0..limb_cnt];
+
+ const ms = limbGet(a, limb_cnt - 1);
+ if ((ms >> 63) == 0) {
+ @memcpy(out, a);
+ return;
+ }
+
+ var carry: u1 = 1;
+ var i: usize = 0;
+ while (i < limb_cnt) : (i += 1) {
+ const s = @addWithOverflow(~limbGet(a, i), carry);
+ limbSet(out, i, s[0]);
+ carry = s[1];
+ }
+}
+
+fn test__abs_limb64(comptime T: type, a: T, expected: @Int(.unsigned, @typeInfo(T).int.bits)) !void {
+ const int_info = @typeInfo(T).int;
+ comptime assert(int_info.signedness == .signed);
+
+ var a_limbs = asLimbs(a);
+ var out: Limbs(@TypeOf(expected)) = undefined;
+ __abs_limb64(&out, &a_limbs, int_info.bits);
+
+ const expected_limbs = asLimbs(expected);
+ try testing.expectEqual(expected_limbs, out);
+}
+
+test __abs_limb64 {
+ try test__abs_limb64(i64, 0, 0);
+ try test__abs_limb64(i64, -1, 1);
+ try test__abs_limb64(i64, minInt(i64), 1 << 63);
+ try test__abs_limb64(i65, -1, 1);
+ try test__abs_limb64(i65, minInt(i65), 1 << 64);
+ try test__abs_limb64(i65, maxInt(i65), maxInt(i65));
+ try test__abs_limb64(i128, -1 << 80, 1 << 80);
+ try test__abs_limb64(i128, 1 << 64, 1 << 64);
+ try test__abs_limb64(i200, -1 << 198, 1 << 198);
+ try test__abs_limb64(i255, -5, 5);
+ try test__abs_limb64(i255, minInt(i255), 1 << 254);
+}
diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig
@@ -2941,7 +2941,16 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
const b = try cg.intSub(u128_ty, a, mask);
return b;
},
- else => return cg.fail("TODO: Support intAbs for integer bitsize: {d}", .{ty.bits}),
+ else => {
+ const result = try cg.allocInt(ty);
+
+ try cg.lowerToStack(result);
+ try cg.lowerToStack(operand);
+ try cg.addImm32(ty.bits);
+ try cg.addCallIntrinsic(.__abs_limb64);
+
+ return result;
+ },
}
}
diff --git a/src/codegen/wasm/Mir.zig b/src/codegen/wasm/Mir.zig
@@ -1019,4 +1019,5 @@ pub const Intrinsic = enum(u32) {
__bitreverse_limb64,
__byteswap_limb64,
__mulo_limb64,
+ __abs_limb64,
};
diff --git a/test/behavior/math.zig b/test/behavior/math.zig
@@ -1657,6 +1657,85 @@ test "@byteSwap > 128 bits" {
try testByteSwap(i256, 1 << 120, 1 << 128);
}
+fn testMax(comptime T: type, a: T, b: T, expected: T) !void {
+ try expect(@max(a, b) == expected);
+}
+
+test "@max > 128 bits" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+
+ try testMax(u140, 0, maxInt(u140), maxInt(u140));
+ try testMax(u140, 1 << 139, 1 << 138, 1 << 139);
+ try testMax(u140, (1 << 100) + 7, (1 << 100) + 3, (1 << 100) + 7);
+ try testMax(u140, maxInt(u140) - 1, maxInt(u140), maxInt(u140));
+
+ try testMax(u200, 1 << 199, 1 << 198, 1 << 199);
+ try testMax(u200, (1 << 150) + (1 << 17), (1 << 150) + (1 << 18), (1 << 150) + (1 << 18));
+ try testMax(u200, 0, 1 << 123, 1 << 123);
+ try testMax(u200, maxInt(u200), maxInt(u200) - 1, maxInt(u200));
+
+ try testMax(i140, -1, 0, 0);
+ try testMax(i140, minInt(i140), maxInt(i140), maxInt(i140));
+ try testMax(i140, -1 << 70, -1 << 69, -1 << 69);
+ try testMax(i140, (1 << 100) - 1, 1 << 100, 1 << 100);
+
+ try testMax(i200, -1, minInt(i200), -1);
+ try testMax(i200, -1 << 150, -1 << 149, -1 << 149);
+ try testMax(i200, 1 << 198, (1 << 198) - 1, 1 << 198);
+ try testMax(i200, maxInt(i200), 0, maxInt(i200));
+}
+
+fn testMin(comptime T: type, a: T, b: T, expected: T) !void {
+ try expect(@min(a, b) == expected);
+}
+
+test "@min > 128 bits" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+
+ try testMin(u140, 0, maxInt(u140), 0);
+ try testMin(u140, 1 << 139, 1 << 138, 1 << 138);
+ try testMin(u140, (1 << 100) + 7, (1 << 100) + 3, (1 << 100) + 3);
+ try testMin(u140, maxInt(u140) - 1, maxInt(u140), maxInt(u140) - 1);
+
+ try testMin(u200, 1 << 199, 1 << 198, 1 << 198);
+ try testMin(u200, (1 << 150) + (1 << 17), (1 << 150) + (1 << 18), (1 << 150) + (1 << 17));
+ try testMin(u200, 0, 1 << 123, 0);
+ try testMin(u200, maxInt(u200), maxInt(u200) - 1, maxInt(u200) - 1);
+
+ try testMin(i140, -1, 0, -1);
+ try testMin(i140, minInt(i140), maxInt(i140), minInt(i140));
+ try testMin(i140, -1 << 70, -1 << 69, -1 << 70);
+ try testMin(i140, (1 << 100) - 1, 1 << 100, (1 << 100) - 1);
+
+ try testMin(i200, -1, minInt(i200), minInt(i200));
+ try testMin(i200, -1 << 150, -1 << 149, -1 << 150);
+ try testMin(i200, 1 << 198, (1 << 198) - 1, (1 << 198) - 1);
+ try testMin(i200, maxInt(i200), 0, 0);
+}
+
+fn testAbs(comptime T: type, a: T, expected: anytype) !void {
+ try expect(@abs(a) == expected);
+}
+
+test "@abs > 128 bits" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+
+ try testAbs(u140, 0, 0);
+ try testAbs(u140, 1 << 139, 1 << 139);
+ try testAbs(u200, 123456789, 123456789);
+ try testAbs(u200, maxInt(u200), maxInt(u200));
+
+ try testAbs(i140, 0, 0);
+ try testAbs(i140, 1, 1);
+ try testAbs(i140, -1, 1);
+ try testAbs(i140, minInt(i140), 1 << 139);
+
+ try testAbs(i200, 1 << 198, 1 << 198);
+ try testAbs(i200, -1 << 198, 1 << 198);
+ try testAbs(i200, maxInt(i200), maxInt(i200));
+ try testAbs(i200, minInt(i200), 1 << 199);
+}
+
test "overflow arithmetic with u0 values" {
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;