diff --git a/lib/compiler_rt/limb64.zig b/lib/compiler_rt/limb64.zig index a5456b548c..c2dc4da8cf 100644 --- 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 index a2750fa02c..80572d4560 100644 --- 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 index 74b6c33020..c131287d70 100644 --- 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 index 20a1c4ed3e..b72eea6aa0 100644 --- 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;