diff --git a/lib/compiler_rt/limb64.zig b/lib/compiler_rt/limb64.zig index 64f924e778a1730d9b63f44ed57ca19bdb35eb89..4c0126d4b37e2a851e6ce56dc1de9431f2599c61 100644 --- a/lib/compiler_rt/limb64.zig +++ b/lib/compiler_rt/limb64.zig @@ -7,6 +7,7 @@ const divCeil = std.math.divCeil; const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); +const symbol = @import("../compiler_rt.zig").symbol; const endian = builtin.cpu.arch.endian(); @@ -55,7 +56,7 @@ fn limbWrap(limb: u64, is_signed: bool, bits: u16) u64 { } comptime { - @export(&__addo_limb64, .{ .name = "__addo_limb64", .linkage = compiler_rt.linkage, .visibility = compiler_rt.visibility }); + symbol(&__addo_limb64, "__addo_limb64"); } fn __addo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool { @@ -127,7 +128,7 @@ test __addo_limb64 { } comptime { - @export(&__subo_limb64, .{ .name = "__subo_limb64", .linkage = compiler_rt.linkage, .visibility = compiler_rt.visibility }); + symbol(&__subo_limb64, "__subo_limb64"); } fn __subo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool { @@ -198,7 +199,7 @@ test __subo_limb64 { } comptime { - @export(&__cmp_limb64, .{ .name = "__cmp_limb64", .linkage = compiler_rt.linkage, .visibility = compiler_rt.visibility }); + symbol(&__cmp_limb64, "__cmp_limb64"); } // a < b -> -1 @@ -264,3 +265,577 @@ test __cmp_limb64 { try test__cmp_limb64(i255, -5, -5, 0); try test__cmp_limb64(i255, 2, -3, 1); } + +comptime { + symbol(&__and_limb64, "__and_limb64"); +} + +fn __and_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_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 b = b_ptr[0..limb_cnt]; + + var i: usize = 0; + while (i < limb_cnt) : (i += 1) { + limbSet(out, i, limbGet(a, i) & limbGet(b, i)); + } +} + +fn test__and_limb64(comptime T: type, a: T, b: T, expected: T) !void { + const int_info = @typeInfo(T).int; + + var a_limbs = asLimbs(a); + var b_limbs = asLimbs(b); + var out: Limbs(T) = undefined; + __and_limb64(&out, &a_limbs, &b_limbs, int_info.bits); + + const expected_limbs = asLimbs(expected); + try testing.expectEqual(expected_limbs, out); +} + +test __and_limb64 { + try test__and_limb64(u64, 1, 2, 0); + try test__and_limb64(u64, maxInt(u64), 2, 2); + try test__and_limb64(u65, maxInt(u65), 2, 2); + try test__and_limb64(u255, maxInt(u255), 7, 7); + + try test__and_limb64(i64, 1, 2, 0); + try test__and_limb64(i64, -1, 2, 2); + try test__and_limb64(i65, minInt(i65), -1, minInt(i65)); + try test__and_limb64(i255, -1, 2, 2); +} + +comptime { + symbol(&__or_limb64, "__or_limb64"); +} + +fn __or_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_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 b = b_ptr[0..limb_cnt]; + + var i: usize = 0; + while (i < limb_cnt) : (i += 1) { + limbSet(out, i, limbGet(a, i) | limbGet(b, i)); + } +} + +fn test__or_limb64(comptime T: type, a: T, b: T, expected: T) !void { + const int_info = @typeInfo(T).int; + + var a_limbs = asLimbs(a); + var b_limbs = asLimbs(b); + var out: Limbs(T) = undefined; + __or_limb64(&out, &a_limbs, &b_limbs, int_info.bits); + + const expected_limbs = asLimbs(expected); + try testing.expectEqual(expected_limbs, out); +} + +test __or_limb64 { + try test__or_limb64(u64, 1, 2, 3); + try test__or_limb64(u64, maxInt(u64), 2, maxInt(u64)); + try test__or_limb64(u65, maxInt(u65), 2, maxInt(u65)); + try test__or_limb64(u255, 1, 2, 3); + + try test__or_limb64(i64, 1, 2, 3); + try test__or_limb64(i64, -1, 2, -1); + try test__or_limb64(i65, minInt(i65), 1, minInt(i65) + 1); + try test__or_limb64(i255, -3, 2, -1); +} + +comptime { + symbol(&__xor_limb64, "__xor_limb64"); +} + +fn __xor_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_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 b = b_ptr[0..limb_cnt]; + + var i: usize = 0; + while (i < limb_cnt) : (i += 1) { + limbSet(out, i, limbGet(a, i) ^ limbGet(b, i)); + } +} + +fn test__xor_limb64(comptime T: type, a: T, b: T, expected: T) !void { + const int_info = @typeInfo(T).int; + + var a_limbs = asLimbs(a); + var b_limbs = asLimbs(b); + var out: Limbs(T) = undefined; + __xor_limb64(&out, &a_limbs, &b_limbs, int_info.bits); + + const expected_limbs = asLimbs(expected); + try testing.expectEqual(expected_limbs, out); +} + +test __xor_limb64 { + try test__xor_limb64(u64, 1, 2, 3); + try test__xor_limb64(u64, 3, 2, 1); + try test__xor_limb64(u65, maxInt(u65), 2, maxInt(u65) - 2); + try test__xor_limb64(u255, 7, 3, 4); + + try test__xor_limb64(i64, 3, 2, 1); + try test__xor_limb64(i64, -1, 2, -3); + try test__xor_limb64(i65, minInt(i65), -1, maxInt(i65)); + try test__xor_limb64(i255, -3, 2, -1); +} + +comptime { + symbol(&__not_limb64, "__not_limb64"); +} + +fn __not_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void { + const limb_cnt = limbCount(bits); + const out = out_ptr[0..limb_cnt]; + const a = a_ptr[0..limb_cnt]; + + var i: usize = 0; + while (i < limb_cnt - 1) : (i += 1) { + limbSet(out, i, ~limbGet(a, i)); + } + + var limb: u64 = ~limbGet(a, i); + if (!is_signed and bits % 64 != 0) { + limb = limbWrap(limb, is_signed, bits); + } + limbSet(out, i, limb); +} + +fn test__not_limb64(comptime T: type, a: T, expected: T) !void { + const int_info = @typeInfo(T).int; + const is_signed = int_info.signedness == .signed; + + var a_limbs = asLimbs(a); + var out: Limbs(T) = undefined; + __not_limb64(&out, &a_limbs, is_signed, int_info.bits); + + const expected_limbs = asLimbs(expected); + try testing.expectEqual(expected_limbs, out); +} + +test __not_limb64 { + try test__not_limb64(u64, 1, maxInt(u64) - 1); + try test__not_limb64(u64, 3, maxInt(u64) - 3); + try test__not_limb64(u65, maxInt(u65), 0); + try test__not_limb64(u255, 7, maxInt(u255) - 7); + + try test__not_limb64(i64, 3, -4); + try test__not_limb64(i64, -1, 0); + try test__not_limb64(i65, minInt(i65), maxInt(i65)); + try test__not_limb64(i255, -3, 2); +} + +comptime { + symbol(&__shlo_limb64, "__shlo_limb64"); +} + +fn __shlo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bool, bits: u16) callconv(.c) bool { + const limb_cnt = limbCount(bits); + const out = out_ptr[0..limb_cnt]; + const a = a_ptr[0..limb_cnt]; + + assert(shift < bits); + + const limb_shift = shift / 64; + const bit_shift = shift % 64; + + var carry: u64 = 0; + var i: usize = 0; + while (i < limb_cnt - 1) : (i += 1) { + if (i < limb_shift) { + limbSet(out, i, 0); + } else { + const limb = limbGet(a, i - limb_shift); + limbSet(out, i, (limb << @intCast(bit_shift)) | carry); + carry = if (bit_shift != 0) (limb >> @intCast(64 - bit_shift)) else 0; + } + } + + const limb = limbGet(a, i - limb_shift); + const raw_last = (limb << @intCast(bit_shift)) | carry; + carry = if (bit_shift != 0) (limb >> @intCast(64 - bit_shift)) else 0; + + const last = if (bits % 64 == 0) raw_last else limbWrap(raw_last, is_signed, bits); + limbSet(out, i, last); + + const sign_extend: u64 = if (is_signed and (last >> 63) == 1) ~@as(u64, 0) else 0; + const expected_carry: u64 = if (bit_shift == 0) 0 else sign_extend >> @intCast(64 - bit_shift); + + var overflow = carry != expected_carry; + if (bits % 64 != 0) { + overflow = overflow or raw_last != last; + } + + var j = limb_cnt - limb_shift; + while (j < limb_cnt) : (j += 1) { + overflow = overflow or limbGet(a, j) != sign_extend; + } + + return overflow; +} + +fn test__shlo_limb64(comptime T: type, a: T, shift: u16, expected: struct { T, bool }) !void { + const int_info = @typeInfo(T).int; + const is_signed = int_info.signedness == .signed; + + var a_limbs = asLimbs(a); + var out: Limbs(T) = undefined; + const overflow = __shlo_limb64(&out, &a_limbs, shift, is_signed, int_info.bits); + + const expected_limbs = asLimbs(expected[0]); + try testing.expectEqual(expected_limbs, out); + try testing.expectEqual(expected[1], overflow); +} + +test __shlo_limb64 { + try test__shlo_limb64(u64, 0x1234_5678_9ABC_DEF0, 4, .{ 0x2345_6789_ABCD_EF00, true }); + try test__shlo_limb64(u64, 0x8000_0000_0000_0001, 63, .{ 0x8000_0000_0000_0000, true }); + try test__shlo_limb64(u65, 1, 64, .{ 0x1_0000_0000_0000_0000, false }); + try test__shlo_limb64(u65, 0x1_0000_0000_0000_0000, 1, .{ 0, true }); + try test__shlo_limb64(u128, 0x1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0, 4, .{ 0x2345_6789_ABCD_EF01_2345_6789_ABCD_EF00, true }); + try test__shlo_limb64(u255, maxInt(u255), 1, .{ maxInt(u255) - 1, true }); + try test__shlo_limb64(u633, 1 << 299, 333, .{ 1 << 632, false }); + try test__shlo_limb64(u633, 1 << 300, 333, .{ 0, true }); + try test__shlo_limb64(u633, 1 << 298, 333, .{ 1 << 631, false }); + + try test__shlo_limb64(i64, -2, 1, .{ -4, false }); + try test__shlo_limb64(i64, minInt(i64), 1, .{ 0, true }); + try test__shlo_limb64(i64, minInt(i64), 63, .{ 0, true }); + try test__shlo_limb64(i65, minInt(i63), 1, .{ minInt(i64), false }); + try test__shlo_limb64(i65, -1, 17, .{ -1 << 17, false }); + try test__shlo_limb64(i65, -3, 64, .{ -1 << 64, true }); + try test__shlo_limb64(i128, -0x1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0, 4, .{ -0x2345_6789_ABCD_EF01_2345_6789_ABCD_EF00, true }); + try test__shlo_limb64(i255, -3, 1, .{ -6, false }); + try test__shlo_limb64(i633, 1 << 298, 333, .{ 1 << 631, false }); + try test__shlo_limb64(i633, 1 << 299, 333, .{ minInt(i633), true }); + try test__shlo_limb64(i633, 1 << 300, 333, .{ 0, true }); + try test__shlo_limb64(i633, 1 << 297, 333, .{ 1 << 630, false }); + try test__shlo_limb64(i633, -1 << 299, 333, .{ -1 << 632, false }); + try test__shlo_limb64(i633, -1 << 300, 333, .{ 0, true }); + try test__shlo_limb64(i633, -1 << 298, 333, .{ -1 << 631, false }); +} + +comptime { + symbol(&__shr_limb64, "__shr_limb64"); +} + +fn __shr_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bool, bits: u16) callconv(.c) void { + const limb_cnt = limbCount(bits); + const out = out_ptr[0..limb_cnt]; + const a = a_ptr[0..limb_cnt]; + + assert(shift < bits); + + const limb_shift = shift / 64; + const bit_shift = shift % 64; + + const ms = limbGet(a, limb_cnt - 1); + const sign_extend: u64 = if (is_signed and (ms >> 63) == 1) ~@as(u64, 0) else 0; + + var carry: u64 = if (bit_shift != 0) (sign_extend << @intCast(64 - bit_shift)) else 0; + var i: usize = 0; + while (i < limb_cnt) : (i += 1) { + const j = limb_cnt - 1 - i; + if (i < limb_shift) { + limbSet(out, j, sign_extend); + } else { + const limb = limbGet(a, j + limb_shift); + limbSet(out, j, (limb >> @intCast(bit_shift)) | carry); + carry = if (bit_shift != 0) (limb << @intCast(64 - bit_shift)) else 0; + } + } +} + +fn test__shr_limb64(comptime T: type, a: T, shift: u16, expected: T) !void { + const int_info = @typeInfo(T).int; + const is_signed = int_info.signedness == .signed; + + var a_limbs = asLimbs(a); + var out: Limbs(T) = undefined; + __shr_limb64(&out, &a_limbs, shift, is_signed, int_info.bits); + + const expected_limbs = asLimbs(expected); + try testing.expectEqual(expected_limbs, out); +} + +test __shr_limb64 { + try test__shr_limb64(u64, 0x1234_5678_9ABC_DEF0, 4, 0x0123_4567_89AB_CDEF); + try test__shr_limb64(u64, 0x8000_0000_0000_0001, 63, 1); + try test__shr_limb64(u65, 0x1_0000_0000_0000_0000, 64, 1); + try test__shr_limb64(u65, 0x1_0000_0000_0000_0001, 1, 0x0_8000_0000_0000_0000); + try test__shr_limb64(u128, 0x1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0, 4, 0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF); + try test__shr_limb64(u255, maxInt(u255), 1, maxInt(u254)); + try test__shr_limb64(u633, 1 << 333, 333, 1); + try test__shr_limb64(u633, 1 << 334, 333, 2); + try test__shr_limb64(u633, 1 << 332, 333, 0); + + try test__shr_limb64(i64, -2, 1, -1); + try test__shr_limb64(i64, minInt(i64), 63, -1); + try test__shr_limb64(i65, minInt(i65), 1, minInt(i65) | (1 << 63)); + try test__shr_limb64(i65, -1, 17, -1); + try test__shr_limb64(i128, -0x1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0, 4, -0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF); + try test__shr_limb64(i255, -3, 1, -2); + try test__shr_limb64(i633, 1 << 333, 333, 1); + try test__shr_limb64(i633, 1 << 334, 333, 2); + try test__shr_limb64(i633, 1 << 332, 333, 0); + try test__shr_limb64(i633, -1 << 333, 333, -1); + try test__shr_limb64(i633, -1 << 334, 333, -2); + try test__shr_limb64(i633, -1 << 332, 333, -1); +} + +comptime { + symbol(&__clz_limb64, "__clz_limb64"); +} + +fn __clz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 { + const limb_cnt = limbCount(bits); + const a = a_ptr[0..limb_cnt]; + + var res: u16 = 0; + var i: usize = 0; + + if (bits % 64 != 0) { + const limb = limbGet(a, limb_cnt - 1); + if (limb == 0) { + res += bits % 64; + } else { + return @clz(limb << @intCast(64 - bits % 64)); + } + i += 1; + } + + while (i < limb_cnt) : (i += 1) { + const j = limb_cnt - 1 - i; + const limb = limbGet(a, j); + if (limb == 0) { + res += 64; + } else { + res += @clz(limb); + break; + } + } + + return res; +} + +fn test__clz_limb64(comptime T: type, a: T, expected: u16) !void { + const int_info = @typeInfo(T).int; + + var a_limbs = asLimbs(a); + const out = __clz_limb64(&a_limbs, int_info.bits); + + try testing.expectEqual(expected, out); +} + +test __clz_limb64 { + try test__clz_limb64(u64, 0, 64); + try test__clz_limb64(u65, 1 << 64, 0); + try test__clz_limb64(u65, 1 << 9, 55); + try test__clz_limb64(u128, 1 << 31, 96); + try test__clz_limb64(u255, 1 << 62, 192); + + try test__clz_limb64(i64, -1, 0); + try test__clz_limb64(i65, minInt(i65), 0); + try test__clz_limb64(i65, 1 << 32, 32); + try test__clz_limb64(i128, 0, 128); + try test__clz_limb64(i255, 1 << 130, 124); +} + +comptime { + symbol(&__ctz_limb64, "__ctz_limb64"); +} + +fn __ctz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 { + const limb_cnt = limbCount(bits); + const a = a_ptr[0..limb_cnt]; + + var res: u16 = 0; + var i: usize = 0; + while (i < limb_cnt - 1) : (i += 1) { + const limb = limbGet(a, i); + if (limb == 0) { + res += 64; + } else { + res += @ctz(limb); + return res; + } + } + + const limb = limbGet(a, i); + if (bits % 64 != 0 and limb == 0) { + res += bits % 64; + } else { + res += @ctz(limb); + } + + return res; +} + +fn test__ctz_limb64(comptime T: type, a: T, expected: u16) !void { + const int_info = @typeInfo(T).int; + + var a_limbs = asLimbs(a); + const out = __ctz_limb64(&a_limbs, int_info.bits); + + try testing.expectEqual(expected, out); +} + +test __ctz_limb64 { + try test__ctz_limb64(u64, 1 << 17, 17); + try test__ctz_limb64(u65, 1 << 64, 64); + try test__ctz_limb64(u65, 0, 65); + try test__ctz_limb64(u128, 1 << 100, 100); + try test__ctz_limb64(u255, 1 << 200, 200); + + try test__ctz_limb64(i64, -1 << 9, 9); + try test__ctz_limb64(i65, minInt(i65), 64); + try test__ctz_limb64(i65, 0, 65); + try test__ctz_limb64(i128, -1 << 73, 73); + try test__ctz_limb64(i255, 1 << 130, 130); +} + +comptime { + symbol(&__popcount_limb64, "__popcount_limb64"); +} + +fn __popcount_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 { + const limb_cnt = limbCount(bits); + const a = a_ptr[0..limb_cnt]; + + var res: u16 = 0; + var i: usize = 0; + while (i < limb_cnt - 1) : (i += 1) { + res += @popCount(limbGet(a, i)); + } + + var limb = limbGet(a, i); + if (bits % 64 != 0) { + limb <<= @intCast(64 - bits % 64); + } + res += @popCount(limb); + + return res; +} + +fn test__popcount_limb64(comptime T: type, a: T, expected: u16) !void { + const int_info = @typeInfo(T).int; + + var a_limbs = asLimbs(a); + const out = __popcount_limb64(&a_limbs, int_info.bits); + + try testing.expectEqual(expected, out); +} + +test __popcount_limb64 { + try test__popcount_limb64(u64, 0xF0F0_0000_0000_0001, 9); + try test__popcount_limb64(u65, 1 << 64, 1); + try test__popcount_limb64(u65, maxInt(u65), 65); + try test__popcount_limb64(u128, (1 << 100) | (1 << 5) | 1, 3); + try test__popcount_limb64(u255, maxInt(u255), 255); + + try test__popcount_limb64(i64, -1, 64); + try test__popcount_limb64(i65, minInt(i65), 1); + try test__popcount_limb64(i65, -1, 65); + try test__popcount_limb64(i128, -1 << 7, 121); + try test__popcount_limb64(i255, -1 << 200, 55); +} + +comptime { + symbol(&__bitreverse_limb64, "__bitreverse_limb64"); +} + +fn __bitreverse_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void { + const limb_cnt = limbCount(bits); + const out = out_ptr[0..limb_cnt]; + const a = a_ptr[0..limb_cnt]; + + var i: usize = 0; + while (i < limb_cnt) : (i += 1) { + const j = limb_cnt - 1 - i; + limbSet(out, j, @bitReverse(limbGet(a, i))); + } + + if (bits % 64 != 0) { + __shr_limb64(out_ptr, out_ptr, 64 - bits % 64, is_signed, bits); + } +} + +fn test__bitreverse_limb64(comptime T: type, a: T, expected: T) !void { + const int_info = @typeInfo(T).int; + const is_signed = int_info.signedness == .signed; + + var a_limbs = asLimbs(a); + var out: Limbs(T) = undefined; + __bitreverse_limb64(&out, &a_limbs, is_signed, int_info.bits); + + const expected_limbs = asLimbs(expected); + try testing.expectEqual(expected_limbs, out); +} + +test __bitreverse_limb64 { + try test__bitreverse_limb64(u64, 1 << 7, 1 << 56); + try test__bitreverse_limb64(u65, 1 << 64, 1); + try test__bitreverse_limb64(u65, 1 << 9, 1 << 55); + try test__bitreverse_limb64(u128, 1 << 100, 1 << 27); + try test__bitreverse_limb64(u255, 1 << 200, 1 << 54); + + try test__bitreverse_limb64(i64, -1, -1); + try test__bitreverse_limb64(i65, 1 << 32, 1 << 32); + try test__bitreverse_limb64(i65, minInt(i65), 1); + try test__bitreverse_limb64(i128, 1 << 63, 1 << 64); + try test__bitreverse_limb64(i255, 1 << 130, 1 << 124); +} + +comptime { + symbol(&__byteswap_limb64, "__byteswap_limb64"); +} + +fn __byteswap_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void { + const limb_cnt = limbCount(bits); + const out = out_ptr[0..limb_cnt]; + const a = a_ptr[0..limb_cnt]; + + assert(bits % 8 == 0); + + var i: usize = 0; + while (i < limb_cnt) : (i += 1) { + const j = limb_cnt - 1 - i; + limbSet(out, j, @byteSwap(limbGet(a, i))); + } + + if (bits % 64 != 0) { + __shr_limb64(out_ptr, out_ptr, 64 - bits % 64, is_signed, bits); + } +} + +fn test__byteswap_limb64(comptime T: type, a: T, expected: T) !void { + const int_info = @typeInfo(T).int; + const is_signed = int_info.signedness == .signed; + + var a_limbs = asLimbs(a); + var out: Limbs(T) = undefined; + __byteswap_limb64(&out, &a_limbs, is_signed, int_info.bits); + + const expected_limbs = asLimbs(expected); + try testing.expectEqual(expected_limbs, out); +} + +test __byteswap_limb64 { + try test__byteswap_limb64(u64, 0x0123_4567_89AB_CDEF, 0xEFCD_AB89_6745_2301); + try test__byteswap_limb64(u72, 0x01_23_45_67_89_AB_CD_EF_11, 0x11_EF_CD_AB_89_67_45_23_01); + try test__byteswap_limb64(u128, 1 << 72, 1 << 48); + try test__byteswap_limb64(u248, 1, 1 << 240); + try test__byteswap_limb64(u256, 1 << 120, 1 << 128); + + try test__byteswap_limb64(i64, minInt(i64), 128); + try test__byteswap_limb64(i72, 1, 1 << 64); + try test__byteswap_limb64(i72, -1, -1); + try test__byteswap_limb64(i128, 1 << 56, 1 << 64); + try test__byteswap_limb64(i248, minInt(i248), 128); +} diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig index 14f2716b3300c297b4dee8fa6803ab9658f5d940..19fb654ae807c54fd11d617a22ffff729f7d8d50 100644 --- a/src/codegen/wasm/CodeGen.zig +++ b/src/codegen/wasm/CodeGen.zig @@ -2629,7 +2629,17 @@ fn intAnd(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue return result; }, - else => return cg.fail("TODO: Support intAnd for integer bitsize: {d}", .{ty.bits}), + else => { + const result = try cg.allocInt(ty); + + try cg.lowerToStack(result); + try cg.lowerToStack(lhs); + try cg.lowerToStack(rhs); + try cg.addImm32(ty.bits); + try cg.addCallIntrinsic(.__and_limb64); + + return result; + }, } } @@ -2663,7 +2673,17 @@ fn intOr(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue return result; }, - else => return cg.fail("TODO: Support intOr for integer bitsize: {d}", .{ty.bits}), + else => { + const result = try cg.allocInt(ty); + + try cg.lowerToStack(result); + try cg.lowerToStack(lhs); + try cg.lowerToStack(rhs); + try cg.addImm32(ty.bits); + try cg.addCallIntrinsic(.__or_limb64); + + return result; + }, } } @@ -2697,7 +2717,17 @@ fn intXor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue return result; }, - else => return cg.fail("TODO: Support intXor for integer bitsize: {d}", .{ty.bits}), + else => { + const result = try cg.allocInt(ty); + + try cg.lowerToStack(result); + try cg.lowerToStack(lhs); + try cg.lowerToStack(rhs); + try cg.addImm32(ty.bits); + try cg.addCallIntrinsic(.__xor_limb64); + + return result; + }, } } @@ -2755,11 +2785,22 @@ fn intNot(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { return result; }, - else => return cg.fail("TODO: Support intNot for integer bitsize: {d}", .{ty.bits}), + else => { + const result = try cg.allocInt(ty); + + try cg.lowerToStack(result); + try cg.lowerToStack(operand); + try cg.addImm32(@intFromBool(ty.is_signed)); + try cg.addImm32(ty.bits); + try cg.addCallIntrinsic(.__not_limb64); + + return result; + }, } } // rhs is a shift count, pointing to i32 value +// does not perform wrapping, padding bits does not satisfy invariant fn intShl(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue { switch (ty.bits) { 0 => unreachable, @@ -2777,7 +2818,19 @@ fn intShl(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue return .stack; }, 65...128 => return cg.callIntrinsic(.__ashlti3, &.{ .i128_type, .i32_type }, Type.i128, &.{ lhs, rhs }), - else => return cg.fail("TODO: Support intShl for integer bitsize: {d}", .{ty.bits}), + else => { + const result = try cg.allocInt(ty); + + try cg.lowerToStack(result); + try cg.lowerToStack(lhs); + try cg.lowerToStack(rhs); + try cg.addImm32(@intFromBool(ty.is_signed)); + try cg.addImm32(ty.bits); + try cg.addCallIntrinsic(.__shlo_limb64); + try cg.addTag(.drop); + + return result; + }, } } @@ -2805,7 +2858,18 @@ fn intShr(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue return cg.callIntrinsic(.__lshrti3, &.{ .i128_type, .i32_type }, Type.i128, &.{ lhs, rhs }); } }, - else => return cg.fail("TODO: Support intShr for integer bitsize: {d}", .{ty.bits}), + else => { + const result = try cg.allocInt(ty); + + try cg.lowerToStack(result); + try cg.lowerToStack(lhs); + try cg.lowerToStack(rhs); + try cg.addImm32(@intFromBool(ty.is_signed)); + try cg.addImm32(ty.bits); + try cg.addCallIntrinsic(.__shr_limb64); + + return result; + }, } } @@ -2932,7 +2996,13 @@ fn intClz(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { try cg.addTag(.i32_wrap_i64); return .stack; }, - else => return cg.fail("TODO: Support intClz for integer bitsize: {d}", .{ty.bits}), + else => { + try cg.lowerToStack(operand); + try cg.addImm32(ty.bits); + try cg.addCallIntrinsic(.__clz_limb64); + + return .stack; + }, } } @@ -2978,7 +3048,13 @@ fn intCtz(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { try cg.addTag(.i32_wrap_i64); return .stack; }, - else => return cg.fail("TODO: Support intCtz for integer bitsize: {d}", .{ty.bits}), + else => { + try cg.lowerToStack(operand); + try cg.addImm32(ty.bits); + try cg.addCallIntrinsic(.__ctz_limb64); + + return .stack; + }, } } @@ -3018,7 +3094,13 @@ fn intPopCount(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { try cg.addTag(.i32_wrap_i64); return .stack; }, - else => return cg.fail("TODO: Support intPopCount for integer bitsize: {d}", .{ty.bits}), + else => { + try cg.lowerToStack(operand); + try cg.addImm32(ty.bits); + try cg.addCallIntrinsic(.__popcount_limb64); + + return .stack; + }, } } @@ -3077,7 +3159,17 @@ fn intBitReverse(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { return tmp; } }, - else => return cg.fail("TODO: Support intBitReverse for integer bitsize: {d}", .{ty.bits}), + else => { + const result = try cg.allocInt(ty); + + try cg.lowerToStack(result); + try cg.lowerToStack(operand); + try cg.addImm32(@intFromBool(ty.is_signed)); + try cg.addImm32(ty.bits); + try cg.addCallIntrinsic(.__bitreverse_limb64); + + return result; + }, } } @@ -3133,7 +3225,17 @@ fn intByteSwap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { return tmp; } }, - else => return cg.fail("TODO: Support intByteSwap for integer bitsize: {d}", .{ty.bits}), + else => { + const result = try cg.allocInt(ty); + + try cg.lowerToStack(result); + try cg.lowerToStack(operand); + try cg.addImm32(@intFromBool(ty.is_signed)); + try cg.addImm32(ty.bits); + try cg.addCallIntrinsic(.__byteswap_limb64); + + return result; + }, } } @@ -3191,7 +3293,30 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { return result; }, 128 => return operand, - else => return cg.fail("TODO: Support intWrap for integer bitsize: {d}", .{ty.bits}), + else => { + const bits = mem.alignForward(u16, ty.bits, 64); + if (ty.bits == bits) return operand; + + const result = try cg.allocInt(ty); + + const len = bits / 8; + try cg.memcpy(result, operand, .{ .imm32 = len - 8 }); + + try cg.emitWValue(result); + _ = try cg.load(operand, Type.u64, len - 8); + if (ty.is_signed) { + try cg.addImm64(bits - ty.bits); + try cg.addTag(.i64_shl); + try cg.addImm64(bits - ty.bits); + try cg.addTag(.i64_shr_s); + } else { + try cg.addImm64(~@as(u64, 0) >> @intCast(bits - ty.bits)); + try cg.addTag(.i64_and); + } + try cg.store(.stack, .stack, Type.u64, result.offset() + len - 8); + + return result; + }, } } @@ -3633,20 +3758,31 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner return .{ .result = result_val, .ov = .{ .local = overflow_bit.local } }; } -fn intShlOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError!OverflowResult { - switch (int_ty.bits) { +fn intShlOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!OverflowResult { + switch (ty.bits) { 0 => unreachable, 1...128 => { - const raw_shl = try cg.intShl(int_ty, lhs, rhs); - const wrapped_shl = try cg.intWrap(int_ty, raw_shl); - const shl_tmp = try cg.toLocalInt(wrapped_shl, int_ty); + const raw_shl = try cg.intShl(ty, lhs, rhs); + const wrapped_shl = try cg.intWrap(ty, raw_shl); + const shl_tmp = try cg.toLocalInt(wrapped_shl, ty); - const shr = try cg.intShr(int_ty, shl_tmp, rhs); - const overflow_bit = try cg.intCmp(int_ty, .neq, shr, lhs); + const shr = try cg.intShr(ty, shl_tmp, rhs); + const overflow_bit = try cg.intCmp(ty, .neq, shr, lhs); return .{ .result = shl_tmp, .ov = overflow_bit }; }, - else => return cg.fail("TODO: Support intShlOverflow for integer bitsize: {d}", .{int_ty.bits}), + else => { + const result = try cg.allocInt(ty); + + try cg.lowerToStack(result); + try cg.lowerToStack(lhs); + try cg.lowerToStack(rhs); + try cg.addImm32(@intFromBool(ty.is_signed)); + try cg.addImm32(ty.bits); + try cg.addCallIntrinsic(.__shlo_limb64); + + return .{ .result = result, .ov = .stack }; + }, } } @@ -3654,17 +3790,13 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn const src_bits: u16 = switch (src_ty.bits) { 0 => unreachable, 1...32 => 32, - 33...64 => 64, - 65...128 => 128, - else => unreachable, + else => mem.alignForward(u16, src_ty.bits, 64), }; const dest_bits: u16 = switch (dest_ty.bits) { 0 => unreachable, 1...32 => 32, - 33...64 => 64, - 65...128 => 128, - else => unreachable, + else => mem.alignForward(u16, dest_ty.bits, 64), }; if (src_bits == dest_bits) { @@ -3677,34 +3809,78 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn return .stack; } else if (src_bits == 32 and dest_bits == 64) { try cg.emitWValue(operand); - try cg.addTag(if (dest_ty.is_signed) .i64_extend_i32_s else .i64_extend_i32_u); + try cg.addTag(if (src_ty.is_signed) .i64_extend_i32_s else .i64_extend_i32_u); return .stack; - } else if (dest_bits == 128) { - const stack_ptr = try cg.allocStack(Type.u128); - try cg.emitWValue(stack_ptr); + } else if (dest_bits >= 128) { + const result = try cg.allocInt(dest_ty); - const lhs = if (src_bits == 32) blk: { - const sign_ty: IntType = .{ .is_signed = dest_ty.is_signed, .bits = 64 }; - break :blk try (try cg.intCast(sign_ty, src_ty, operand)).toLocal(cg, Type.u64); - } else operand; + const dest_len = dest_bits / 8; - try cg.store(.stack, lhs, Type.u64, stack_ptr.offset()); - - if (dest_ty.is_signed) { - try cg.emitWValue(stack_ptr); - const shr = try cg.intShr(IntType.i64, lhs, .{ .imm32 = 63 }); - try cg.store(.stack, shr, Type.u64, 8 + stack_ptr.offset()); + if (dest_bits <= src_bits) { + assert(src_bits >= 128); + try cg.memcpy(result, operand, .{ .imm32 = dest_len }); } else { - try cg.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8); - } + var src_len: u32 = undefined; + if (src_bits == 32) { + try cg.emitWValue(result); + try cg.emitWValue(operand); + try cg.addTag(if (src_ty.is_signed) .i64_extend_i32_s else .i64_extend_i32_u); + try cg.store(.stack, .stack, Type.u64, result.offset()); + src_len = 8; + } else if (src_bits == 64) { + try cg.emitWValue(result); + try cg.emitWValue(operand); + try cg.store(.stack, .stack, Type.u64, result.offset()); + src_len = 8; + } else { + src_len = src_bits / 8; + try cg.memcpy(result, operand, .{ .imm32 = src_len }); + } - if (src_bits == 32) { - var tmp_lhs = lhs; - tmp_lhs.free(cg); + if (dest_bits == 128) { + if (src_ty.is_signed) { + try cg.emitWValue(result); + if (src_bits == 32) { + try cg.emitWValue(operand); + try cg.addTag(if (dest_ty.is_signed) .i64_extend_i32_s else .i64_extend_i32_u); + } else if (src_bits == 64) { + try cg.emitWValue(operand); + } else unreachable; + const shr = try cg.intShr(IntType.i64, .stack, .{ .imm32 = 63 }); + try cg.store(.stack, shr, Type.u64, 8 + result.offset()); + } else { + try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8); + } + } else { + var pad = result; + pad.stack_offset.value += src_len; + const memset_len = dest_len - src_len; + if (src_ty.is_signed) { + if (src_bits == 32) { + try cg.emitWValue(operand); + _ = try cg.intShr(IntType.i32, .stack, .{ .imm32 = 31 }); + } else if (src_bits == 64) { + try cg.emitWValue(operand); + _ = try cg.intShr(IntType.i64, .stack, .{ .imm32 = 63 }); + try cg.addTag(.i32_wrap_i64); + } else { + _ = try cg.load(operand, Type.u64, src_len - 8); + _ = try cg.intShr(IntType.i64, .stack, .{ .imm32 = 63 }); + try cg.addTag(.i32_wrap_i64); + } + var sign_byte = try @as(WValue, .stack).toLocal(cg, Type.u32); + try cg.memset(Type.u8, pad, .{ .imm32 = memset_len }, sign_byte); + sign_byte.free(cg); + } else { + try cg.memset(Type.u8, pad, .{ .imm32 = memset_len }, .{ .imm32 = 0 }); + } + } } - return stack_ptr; + return result; } else { + assert(dest_bits <= 64); + assert(src_bits >= 128); const load_ty = if (dest_bits == 32) Type.u32 else Type.u64; return cg.load(operand, load_ty, 0); } @@ -3716,9 +3892,7 @@ fn intTrunc(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) In const dest_wasm_bits: u16 = switch (dest_ty.bits) { 0 => unreachable, 1...32 => 32, - 33...64 => 64, - 65...128 => 128, - else => return cg.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{dest_ty.bits}), + else => mem.alignForward(u16, dest_ty.bits, 64), }; if (dest_wasm_bits != dest_ty.bits) { diff --git a/src/codegen/wasm/Mir.zig b/src/codegen/wasm/Mir.zig index 3276c0f15c36fa9046738b79e700e52a1ca0c366..d7297af253a2e59ce07dcfc47b40c49d9d315106 100644 --- a/src/codegen/wasm/Mir.zig +++ b/src/codegen/wasm/Mir.zig @@ -1007,4 +1007,15 @@ pub const Intrinsic = enum(u32) { __addo_limb64, __subo_limb64, __cmp_limb64, + __and_limb64, + __or_limb64, + __xor_limb64, + __not_limb64, + __shlo_limb64, + __shr_limb64, + __clz_limb64, + __ctz_limb64, + __popcount_limb64, + __bitreverse_limb64, + __byteswap_limb64, }; diff --git a/test/behavior/cast_int.zig b/test/behavior/cast_int.zig index 8a1fcf79b2f0c9ab3a2105780fa37c3bffdbbd2a..7308da065214e903f3b771dbe90b1078582b6a05 100644 --- a/test/behavior/cast_int.zig +++ b/test/behavior/cast_int.zig @@ -3,6 +3,7 @@ const std = @import("std"); const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const maxInt = std.math.maxInt; +const minInt = std.math.minInt; test "@intCast i32 to u7" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -136,6 +137,82 @@ test "coerce non byte-sized integers accross 32bits boundary" { } } +fn testIntCast(comptime S: type, a: S, comptime D: type, expected: D) !void { + const actual: D = @intCast(a); + try expect(actual == expected); +} + +test "@intCast <= 64 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testIntCast(i32, minInt(i32), i64, minInt(i32)); + try testIntCast(i32, maxInt(i32), i64, maxInt(i32)); + try testIntCast(u32, maxInt(u32), u64, maxInt(u32)); + try testIntCast(u32, maxInt(i32), i64, maxInt(i32)); + try testIntCast(u32, maxInt(u32), i64, maxInt(u32)); + + try testIntCast(i32, 0, u32, 0); + try testIntCast(i32, maxInt(i32), u32, maxInt(i32)); + try testIntCast(i64, 0, u64, 0); + try testIntCast(i64, maxInt(i64), u64, maxInt(i64)); + + try testIntCast(u32, 0, i32, 0); + try testIntCast(u32, maxInt(i32), i32, maxInt(i32)); + try testIntCast(u64, 0, i64, 0); + try testIntCast(u64, maxInt(i64), i64, maxInt(i64)); + + try testIntCast(i64, minInt(i32), i32, minInt(i32)); + try testIntCast(i64, maxInt(i32), i32, maxInt(i32)); + try testIntCast(u64, maxInt(u32), u32, maxInt(u32)); + try testIntCast(i64, maxInt(u32), u32, maxInt(u32)); +} + +test "@intCast > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testIntCast(u8, 123, u140, 123); + try testIntCast(u64, 1 << 63, u140, 1 << 63); + try testIntCast(u127, maxInt(u127), u140, maxInt(u127)); + try testIntCast(i8, -42, i140, -42); + try testIntCast(i64, minInt(i64), i140, minInt(i64)); + try testIntCast(i127, maxInt(i127), i140, maxInt(i127)); + try testIntCast(u127, 1 << 100, i140, 1 << 100); + try testIntCast(i127, 1 << 100, u140, 1 << 100); + + try testIntCast(u140, 0, u128, 0); + try testIntCast(u140, 1 << 100, u128, 1 << 100); + try testIntCast(u140, maxInt(u128), u128, maxInt(u128)); + try testIntCast(i140, -1, i128, -1); + try testIntCast(i140, minInt(i128), i128, minInt(i128)); + try testIntCast(i140, maxInt(i128), i128, maxInt(i128)); + try testIntCast(u140, 1 << 100, i128, 1 << 100); + try testIntCast(i140, 1 << 100, u128, 1 << 100); + + try testIntCast(u16, 255, u256, 255); + try testIntCast(u128, 1 << 127, u256, 1 << 127); + try testIntCast(i16, -7, i256, -7); + try testIntCast(i128, minInt(i128), i256, minInt(i128)); + try testIntCast(u128, maxInt(i128), i256, maxInt(i128)); + try testIntCast(i128, 1 << 100, u256, 1 << 100); + + try testIntCast(u256, 1 << 139, u140, 1 << 139); + try testIntCast(u256, maxInt(u140), u140, maxInt(u140)); + try testIntCast(i256, -1, i140, -1); + try testIntCast(i256, minInt(i140), i140, minInt(i140)); + try testIntCast(i256, maxInt(i140), i140, maxInt(i140)); + try testIntCast(u256, 1 << 120, i140, 1 << 120); + try testIntCast(i256, 1 << 120, u140, 1 << 120); + + try testIntCast(i257, maxInt(i256), i256, maxInt(i256)); + try testIntCast(i257, minInt(i256), i256, minInt(i256)); + try testIntCast(u257, maxInt(u256), u256, maxInt(u256)); + try testIntCast(u257, 1 << 255, u256, 1 << 255); + + try testIntCast(u32, maxInt(u32), i255, maxInt(u32)); + try testIntCast(u64, maxInt(u64), i255, maxInt(u64)); + try testIntCast(u128, maxInt(u128), i255, maxInt(u128)); +} + const Piece = packed struct { color: Color, type: Type, diff --git a/test/behavior/math.zig b/test/behavior/math.zig index f569845a06584b18428fc512989c77d69b23d22f..085a72f390d2d15b207ae10876080bab6c0957be 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -61,17 +61,17 @@ fn assertFalse(b: bool) !void { try expect(!b); } -test "@clz" { +test "@clz small" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO - try testClz(); - try comptime testClz(); + try testClzSmall(); + try comptime testClzSmall(); } -fn testClz() !void { +fn testClzSmall() !void { try expect(testOneClz(u8, 0b10001010) == 0); try expect(testOneClz(u8, 0b00001010) == 4); try expect(testOneClz(u8, 0b00011010) == 3); @@ -142,17 +142,17 @@ fn expectVectorsEqual(a: anytype, b: anytype) !void { try expect(@reduce(.And, a == b)); } -test "@ctz" { +test "@ctz small" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - try testCtz(); - try comptime testCtz(); + try testCtzSmall(); + try comptime testCtzSmall(); } -fn testCtz() !void { +fn testCtzSmall() !void { try expect(testOneCtz(u8, 0b10100000) == 5); try expect(testOneCtz(u8, 0b10001010) == 1); try expect(testOneCtz(u8, 0b00000000) == 8); @@ -1298,6 +1298,338 @@ test "@shlWithOverflow > 64 bits" { try testShlWithOverflow(i128, 0x0100_0000_0000_0000_0000000000000000, 9, 0, 1); } +test "@shlWithOverflow > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testShlWithOverflow(u140, 1 << 100, 20, 1 << 120, 0); + try testShlWithOverflow(u140, 1 << 100, 40, 0, 1); + try testShlWithOverflow(u140, 3, 138, (1 << 139) | (1 << 138), 0); + try testShlWithOverflow(u140, 7, 138, (1 << 139) | (1 << 138), 1); + + try testShlWithOverflow(u256, 1 << 200, 40, 1 << 240, 0); + try testShlWithOverflow(u256, 1 << 200, 55, 1 << 255, 0); + try testShlWithOverflow(u256, 1 << 200, 56, 0, 1); + try testShlWithOverflow(u256, maxInt(u256), 1, maxInt(u256) - 1, 1); + + try testShlWithOverflow(i140, 1 << 100, 20, 1 << 120, 0); + try testShlWithOverflow(i140, 1 << 100, 39, minInt(i140), 1); + try testShlWithOverflow(i140, -1 << 20, 10, -1 << 30, 0); + try testShlWithOverflow(i140, minInt(i140), 1, 0, 1); + + try testShlWithOverflow(i256, 1 << 200, 30, 1 << 230, 0); + try testShlWithOverflow(i256, 1 << 200, 55, minInt(i256), 1); + try testShlWithOverflow(i256, -1 << 120, 40, -1 << 160, 0); + try testShlWithOverflow(i256, minInt(i256), 1, 0, 1); +} + +fn testAnd(comptime T: type, a: T, b: T, expected: T) !void { + try expect((a & b) == expected); +} + +test "and > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testAnd(u140, (1 << 139) | (1 << 70) | 0xaa, (1 << 139) | (1 << 69) | 0xcc, (1 << 139) | 0x88); + try testAnd(u140, maxInt(u140), 1 << 100, 1 << 100); + try testAnd(u140, 0, maxInt(u140), 0); + try testAnd(u140, (1 << 80) | (1 << 17) | 1, (1 << 17) | (1 << 9) | 1, (1 << 17) | 1); + + try testAnd(u256, maxInt(u256), (1 << 255) | (1 << 200) | 7, (1 << 255) | (1 << 200) | 7); + try testAnd(u256, (1 << 255) | (1 << 5), (1 << 254) | (1 << 5), 1 << 5); + try testAnd(u256, (1 << 130) | (1 << 64) | (1 << 2), (1 << 130) | (1 << 63) | (1 << 2), (1 << 130) | (1 << 2)); + try testAnd(u256, 0, 1 << 200, 0); + + try testAnd(i140, -1, 1 << 17, 1 << 17); + try testAnd(i140, minInt(i140), -1, minInt(i140)); + try testAnd(i140, -1 << 40, (1 << 100) | (1 << 80) | (1 << 40), (1 << 100) | (1 << 80) | (1 << 40)); + try testAnd(i140, 0, maxInt(i140), 0); + + try testAnd(i256, -1, 1 << 200, 1 << 200); + try testAnd(i256, minInt(i256), maxInt(i256), 0); + try testAnd(i256, -1 << 130, -1 << 129, -1 << 130); + try testAnd(i256, minInt(i256), -1 << 10, minInt(i256)); +} + +fn testOr(comptime T: type, a: T, b: T, expected: T) !void { + try expect((a | b) == expected); +} + +test "or > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testOr(u140, 0, 1 << 139, 1 << 139); + try testOr(u140, (1 << 70) | 0xa, (1 << 69) | 0x5, (1 << 70) | (1 << 69) | 0xf); + try testOr(u140, maxInt(u140), 0, maxInt(u140)); + try testOr(u140, (1 << 17) | (1 << 3), (1 << 17) | (1 << 1), (1 << 17) | 0xa); + + try testOr(u256, 0, 1 << 255, 1 << 255); + try testOr(u256, (1 << 200) | 0x30, (1 << 199) | 0x0f, (1 << 200) | (1 << 199) | 0x3f); + try testOr(u256, maxInt(u256), 1 << 17, maxInt(u256)); + try testOr(u256, 1 << 130, 1 << 64, (1 << 130) | (1 << 64)); + + try testOr(i140, -1, 0, -1); + try testOr(i140, minInt(i140), 1, minInt(i140) + 1); + try testOr(i140, -1 << 40, (1 << 5) | 1, (-1 << 40) | 0x21); + try testOr(i140, 0, maxInt(i140), maxInt(i140)); + + try testOr(i256, -1, 1 << 200, -1); + try testOr(i256, minInt(i256), 1 << 17, minInt(i256) | (1 << 17)); + try testOr(i256, -1 << 130, 0xff, (-1 << 130) | 0xff); + try testOr(i256, 0, maxInt(i256), maxInt(i256)); +} + +fn testXor(comptime T: type, a: T, b: T, expected: T) !void { + try expect((a ^ b) == expected); +} + +test "xor > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testXor(u140, 0, maxInt(u140), maxInt(u140)); + try testXor(u140, 1 << 139, 1 << 139, 0); + try testXor(u140, (1 << 70) | 0xa, (1 << 69) | 0x5, (1 << 70) | (1 << 69) | 0xf); + try testXor(u140, maxInt(u140), 1 << 100, maxInt(u140) ^ (1 << 100)); + + try testXor(u256, 0, maxInt(u256), maxInt(u256)); + try testXor(u256, 1 << 255, 1 << 255, 0); + try testXor(u256, (1 << 200) | (1 << 5), (1 << 199) | (1 << 5), (1 << 200) | (1 << 199)); + try testXor(u256, maxInt(u256), 1 << 17, maxInt(u256) ^ (1 << 17)); + + try testXor(i140, -1, -1, 0); + try testXor(i140, -1, 0, -1); + try testXor(i140, minInt(i140), -1, maxInt(i140)); + try testXor(i140, -1 << 40, -1 << 39, 1 << 39); + + try testXor(i256, -1, -1, 0); + try testXor(i256, -1, 0, -1); + try testXor(i256, minInt(i256), -1, maxInt(i256)); + try testXor(i256, -1 << 130, -1 << 129, 1 << 129); +} + +fn testNot(comptime T: type, a: T, expected: T) !void { + try expect((~a) == expected); +} + +test "not > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testNot(u140, 0, maxInt(u140)); + try testNot(u140, maxInt(u140), 0); + try testNot(u140, 1 << 139, maxInt(u140) ^ (1 << 139)); + try testNot(u140, (1 << 17) | 1, maxInt(u140) ^ ((1 << 17) | 1)); + + try testNot(u256, 0, maxInt(u256)); + try testNot(u256, maxInt(u256), 0); + try testNot(u256, 1 << 255, maxInt(u256) ^ (1 << 255)); + try testNot(u256, (1 << 200) | (1 << 5), maxInt(u256) ^ ((1 << 200) | (1 << 5))); + + try testNot(i140, -1, 0); + try testNot(i140, 0, -1); + try testNot(i140, minInt(i140), maxInt(i140)); + try testNot(i140, -1 << 10, (1 << 10) - 1); + + try testNot(i256, -1, 0); + try testNot(i256, 0, -1); + try testNot(i256, minInt(i256), maxInt(i256)); + try testNot(i256, -1 << 200, (1 << 200) - 1); +} + +fn testShl(comptime T: type, a: T, b: std.math.Log2Int(T), expected: T) !void { + try expect((a << b) == expected); +} + +test "shl > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testShl(u140, 1 << 5, 10, 1 << 15); + try testShl(u140, 3, 138, (1 << 139) | (1 << 138)); + try testShl(u140, 1 << 139, 1, 0); + try testShl(u140, (1 << 70) | 1, 3, (1 << 73) | 8); + + try testShl(u256, 1 << 200, 20, 1 << 220); + try testShl(u256, 1 << 255, 1, 0); + try testShl(u256, (1 << 128) | 5, 7, (1 << 135) | 0x280); + try testShl(u256, maxInt(u256), 1, maxInt(u256) - 1); + + try testShl(i140, 1 << 20, 5, 1 << 25); + try testShl(i140, -1, 7, -128); + try testShl(i140, minInt(i140), 1, 0); + try testShl(i140, -1 << 10, 5, -1 << 15); + + try testShl(i256, 1 << 200, 30, 1 << 230); + try testShl(i256, -1, 200, -1 << 200); + try testShl(i256, minInt(i256), 1, 0); + try testShl(i256, -1 << 100, 50, -1 << 150); +} + +fn testShr(comptime T: type, a: T, b: std.math.Log2Int(T), expected: T) !void { + try expect((a >> b) == expected); +} + +test "shr > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testShr(u140, 1 << 139, 39, 1 << 100); + try testShr(u140, (1 << 70) | 8, 3, (1 << 67) | 1); + try testShr(u140, 1, 1, 0); + try testShr(u140, maxInt(u140), 139, 1); + + try testShr(u256, 1 << 255, 55, 1 << 200); + try testShr(u256, (1 << 200) | (1 << 7), 7, (1 << 193) | 1); + try testShr(u256, 1, 1, 0); + try testShr(u256, maxInt(u256), 255, 1); + + try testShr(i140, -1, 17, -1); + try testShr(i140, minInt(i140), 1, minInt(i140) >> 1); + try testShr(i140, -1 << 80, 40, -1 << 40); + try testShr(i140, -5, 1, -3); + + try testShr(i256, -1, 200, -1); + try testShr(i256, minInt(i256), 1, minInt(i256) >> 1); + try testShr(i256, -1 << 180, 80, -1 << 100); + try testShr(i256, -5, 1, -3); +} + +fn testClz(comptime T: type, a: T, expected: u16) !void { + try expect(@clz(a) == expected); +} + +test "@clz > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testClz(u140, 0, 140); + try testClz(u140, 1 << 139, 0); + try testClz(u140, 1 << 70, 69); + try testClz(u140, maxInt(u140), 0); + + try testClz(u256, 0, 256); + try testClz(u256, 1 << 255, 0); + try testClz(u256, 1 << 200, 55); + try testClz(u256, 1, 255); + + try testClz(i140, -1, 0); + try testClz(i140, minInt(i140), 0); + try testClz(i140, 1 << 70, 69); + try testClz(i140, 0, 140); + + try testClz(i256, -1, 0); + try testClz(i256, minInt(i256), 0); + try testClz(i256, 1 << 200, 55); + try testClz(i256, 0, 256); +} + +fn testCtz(comptime T: type, a: T, expected: u16) !void { + try expect(@ctz(a) == expected); +} + +test "@ctz > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testCtz(u140, 0, 140); + try testCtz(u140, 1 << 139, 139); + try testCtz(u140, 1 << 70, 70); + try testCtz(u140, maxInt(u140), 0); + + try testCtz(u256, 0, 256); + try testCtz(u256, 1 << 255, 255); + try testCtz(u256, 1 << 200, 200); + try testCtz(u256, 3 << 5, 5); + + try testCtz(i140, -1, 0); + try testCtz(i140, minInt(i140), 139); + try testCtz(i140, 0, 140); + try testCtz(i140, -1 << 70, 70); + + try testCtz(i256, -1, 0); + try testCtz(i256, minInt(i256), 255); + try testCtz(i256, 0, 256); + try testCtz(i256, -1 << 200, 200); +} + +fn testPopCount(comptime T: type, a: T, expected: u16) !void { + try expect(@popCount(a) == expected); +} + +test "@popCount > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testPopCount(u140, 0, 0); + try testPopCount(u140, maxInt(u140), 140); + try testPopCount(u140, (1 << 139) | (1 << 70) | 1, 3); + try testPopCount(u140, (1 << 5) - 1, 5); + + try testPopCount(u256, 0, 0); + try testPopCount(u256, maxInt(u256), 256); + try testPopCount(u256, (1 << 255) | (1 << 200) | (1 << 17) | (1 << 3), 4); + try testPopCount(u256, (1 << 64) - 1, 64); + + try testPopCount(i140, -1, 140); + try testPopCount(i140, minInt(i140), 1); + try testPopCount(i140, 0, 0); + try testPopCount(i140, -1 << 70, 70); + + try testPopCount(i256, -1, 256); + try testPopCount(i256, minInt(i256), 1); + try testPopCount(i256, 0, 0); + try testPopCount(i256, -1 << 200, 56); +} + +fn testBitReverse(comptime T: type, a: T, expected: T) !void { + try expect(@bitReverse(a) == expected); +} + +test "@bitReverse > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testBitReverse(u140, 1 << 139, 1); + try testBitReverse(u140, 1 << 70, 1 << 69); + try testBitReverse(u140, 0, 0); + try testBitReverse(u140, maxInt(u140), maxInt(u140)); + + try testBitReverse(u256, 1 << 255, 1); + try testBitReverse(u256, 1 << 200, 1 << 55); + try testBitReverse(u256, 0, 0); + try testBitReverse(u256, maxInt(u256), maxInt(u256)); + + try testBitReverse(i140, -1, -1); + try testBitReverse(i140, minInt(i140), 1); + try testBitReverse(i140, 1 << 70, 1 << 69); + try testBitReverse(i140, 0, 0); + + try testBitReverse(i256, -1, -1); + try testBitReverse(i256, minInt(i256), 1); + try testBitReverse(i256, 1 << 200, 1 << 55); + try testBitReverse(i256, 0, 0); +} + +fn testByteSwap(comptime T: type, a: T, expected: T) !void { + try expect(@byteSwap(a) == expected); +} + +test "@byteSwap > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testByteSwap(u144, 1 << 136, 1); + try testByteSwap(u144, 1, 1 << 136); + try testByteSwap(u144, 0, 0); + try testByteSwap(u144, maxInt(u144), maxInt(u144)); + + try testByteSwap(u256, 1 << 248, 1); + try testByteSwap(u256, 1 << 120, 1 << 128); + try testByteSwap(u256, 1, 1 << 248); + try testByteSwap(u256, maxInt(u256), maxInt(u256)); + + try testByteSwap(i144, -1, -1); + try testByteSwap(i144, minInt(i144), 128); + try testByteSwap(i144, 1, 1 << 136); + try testByteSwap(i144, 1 << 64, 1 << 72); + + try testByteSwap(i256, -1, -1); + try testByteSwap(i256, minInt(i256), 128); + try testByteSwap(i256, 1, 1 << 248); + try testByteSwap(i256, 1 << 120, 1 << 128); +} + test "overflow arithmetic with u0 values" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; diff --git a/test/behavior/truncate.zig b/test/behavior/truncate.zig index 3d3c98b86544b012e81d2dacc848a09aae38131b..7b2c234dc2cfa81e95701468dd7d94e9c268995a 100644 --- a/test/behavior/truncate.zig +++ b/test/behavior/truncate.zig @@ -1,4 +1,7 @@ const std = @import("std"); +const math = std.math; +const maxInt = math.maxInt; +const minInt = math.minInt; const builtin = @import("builtin"); const assert = std.debug.assert; const expect = std.testing.expect; @@ -64,6 +67,48 @@ test "truncate on comptime integer" { try expect(w == 0); } +fn testTruncate(comptime S: type, a: S, comptime D: type, expected: D) !void { + const actual: D = @truncate(a); + try expect(actual == expected); +} + +test "@truncate > 128 bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + try testTruncate(u140, 0, u128, 0); + try testTruncate(u140, maxInt(u140), u128, maxInt(u128)); + try testTruncate(u140, 1 << 139, u128, 0); + try testTruncate(u140, (1 << 139) | (1 << 64) | 0x55, u128, (1 << 64) | 0x55); + try testTruncate(u140, (1 << 100) | (1 << 63), u128, (1 << 100) | (1 << 63)); + try testTruncate(u140, (1 << 130) | 0xabcd, u16, 0xabcd); + + try testTruncate(u256, 1 << 200, u128, 0); + try testTruncate(u256, (1 << 200) | (1 << 127) | 1, u128, (1 << 127) | 1); + try testTruncate(u256, maxInt(u256), u128, maxInt(u128)); + try testTruncate(u256, (1 << 255) | (1 << 128) | 0x1234_5678_9abc_def0, u64, 0x1234_5678_9abc_def0); + try testTruncate(u256, (1 << 250) | (1 << 32), u32, 0); + try testTruncate(u256, (1 << 129) | (1 << 63), u64, 1 << 63); + + try testTruncate(i140, 0, i128, 0); + try testTruncate(i140, -1, i128, -1); + try testTruncate(i140, -2, i8, -2); + try testTruncate(i140, -1 << 80, i64, 0); + try testTruncate(i140, (-1 << 80) | 0x1234, i16, 0x1234); + try testTruncate(i140, minInt(i140), i128, 0); + try testTruncate(i140, maxInt(i140), i128, -1); + try testTruncate(i140, (1 << 127) - 1, i128, maxInt(i128)); + + try testTruncate(i256, -1, i128, -1); + try testTruncate(i256, minInt(i256), i128, 0); + try testTruncate(i256, (-1 << 128) | maxInt(i128), i128, maxInt(i128)); + try testTruncate(i256, (-1 << 200) | (1 << 127), i128, minInt(i128)); + try testTruncate(i256, -255, i8, 1); + try testTruncate(i256, (-1 << 64) | 0x1234_5678, i32, 0x1234_5678); + + try testTruncate(i257, maxInt(i257), i256, -1); + try testTruncate(u257, maxInt(u257), u256, maxInt(u256)); +} + test "truncate on vectors" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;