diff --git a/CMakeLists.txt b/CMakeLists.txt index f8fc04ef7d..91272fc9cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -494,8 +494,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/modti3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/mulXf3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/muldi3.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/mulodi4.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/muloti4.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/mulo.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/multi3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/negXf2.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/negXi2.zig" diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index e9f422be59..310bffbbe2 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -77,9 +77,11 @@ comptime { @export(__extendhfsf2, .{ .name = "__gnu_h2f_ieee", .linkage = linkage }); // Integral arithmetic which returns if overflow - const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4; + const __mulosi4 = @import("compiler_rt/mulo.zig").__mulosi4; + @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage }); + const __mulodi4 = @import("compiler_rt/mulo.zig").__mulodi4; @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage }); - const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4; + const __muloti4 = @import("compiler_rt/mulo.zig").__muloti4; @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage }); } diff --git a/lib/std/special/compiler_rt/bswap.zig b/lib/std/special/compiler_rt/bswap.zig index d903039626..0646f68c1a 100644 --- a/lib/std/special/compiler_rt/bswap.zig +++ b/lib/std/special/compiler_rt/bswap.zig @@ -6,7 +6,6 @@ const builtin = @import("builtin"); // ie for u32 // DE AD BE EF <- little|big endian // FE BE AD DE <- big|little endian -// ie for u32 // ff 00 00 00 >> 3*8 (leftmost byte) // 00 ff 00 00 >> 1*8 (2nd left byte) // 00 00 ff 00 << 1*8 (2n right byte) diff --git a/lib/std/special/compiler_rt/mulo.zig b/lib/std/special/compiler_rt/mulo.zig new file mode 100644 index 0000000000..1e812c7e99 --- /dev/null +++ b/lib/std/special/compiler_rt/mulo.zig @@ -0,0 +1,69 @@ +const builtin = @import("builtin"); + +// mulo - multiplication overflow +// - muloXi4_generic for unoptimized version + +// return a*b. +// return if a*b overflows => 1 else => 0 +// see https://stackoverflow.com/a/26320664 for possible implementations + +fn muloXi4_generic(comptime ST: type) fn (a: ST, b: ST, overflow: *c_int) callconv(.C) ST { + return struct { + fn f(a: ST, b: ST, overflow: *c_int) callconv(.C) ST { + @setRuntimeSafety(builtin.is_test); + const BSIZE = @bitSizeOf(ST); + comptime var UT = switch (ST) { + i32 => u32, + i64 => u64, + i128 => u128, + else => unreachable, + }; + const min = @bitCast(ST, @as(UT, 1 << (BSIZE - 1))); + const max = ~min; + overflow.* = 0; + const result = a *% b; + + // edge cases + if (a == min) { + if (b != 0 and b != 1) overflow.* = 1; + return result; + } + if (b == min) { + if (a != 0 and a != 1) overflow.* = 1; + return result; + } + + // take sign of x sx + const sa = a >> (BSIZE - 1); + const sb = b >> (BSIZE - 1); + // take absolute value of a and b via + // abs(x) = (x^sx)) - sx + const abs_a = (a ^ sa) -% sa; + const abs_b = (b ^ sb) -% sb; + + // unitary magnitude, cannot have overflow + if (abs_a < 2 or abs_b < 2) return result; + + // compare the signs of operands + if ((a ^ b) >> (BSIZE - 1) != 0) { + if (abs_a > @divTrunc(max, abs_b)) overflow.* = 1; + } else { + if (abs_a > @divTrunc(min, -abs_b)) overflow.* = 1; + } + + return result; + } + }.f; +} + +pub const __mulosi4 = muloXi4_generic(i32); + +pub const __mulodi4 = muloXi4_generic(i64); + +pub const __muloti4 = muloXi4_generic(i128); + +test { + _ = @import("mulosi4_test.zig"); + _ = @import("mulodi4_test.zig"); + _ = @import("muloti4_test.zig"); +} diff --git a/lib/std/special/compiler_rt/mulodi4.zig b/lib/std/special/compiler_rt/mulodi4.zig deleted file mode 100644 index d58cfbfe11..0000000000 --- a/lib/std/special/compiler_rt/mulodi4.zig +++ /dev/null @@ -1,42 +0,0 @@ -const builtin = @import("builtin"); -const compiler_rt = @import("../compiler_rt.zig"); - -pub fn __mulodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 { - @setRuntimeSafety(builtin.is_test); - - const min = @bitCast(i64, @as(u64, 1 << (64 - 1))); - const max = ~min; - - overflow.* = 0; - const result = a *% b; - - // Edge cases - if (a == min) { - if (b != 0 and b != 1) overflow.* = 1; - return result; - } - if (b == min) { - if (a != 0 and a != 1) overflow.* = 1; - return result; - } - - // Take absolute value of a and b via abs(x) = (x^(x >> 63)) - (x >> 63). - const abs_a = (a ^ (a >> 63)) -% (a >> 63); - const abs_b = (b ^ (b >> 63)) -% (b >> 63); - - // Unitary magnitude, cannot have overflow - if (abs_a < 2 or abs_b < 2) return result; - - // Compare the signs of the operands - if ((a ^ b) >> 63 != 0) { - if (abs_a > @divTrunc(max, abs_b)) overflow.* = 1; - } else { - if (abs_a > @divTrunc(min, -abs_b)) overflow.* = 1; - } - - return result; -} - -test { - _ = @import("mulodi4_test.zig"); -} diff --git a/lib/std/special/compiler_rt/mulodi4_test.zig b/lib/std/special/compiler_rt/mulodi4_test.zig index b9fea2553f..3944f62ede 100644 --- a/lib/std/special/compiler_rt/mulodi4_test.zig +++ b/lib/std/special/compiler_rt/mulodi4_test.zig @@ -1,9 +1,11 @@ -const __mulodi4 = @import("mulodi4.zig").__mulodi4; +const mulo = @import("mulo.zig"); const testing = @import("std").testing; +// ported from https://github.com/llvm-mirror/compiler-rt/tree/release_80/test/builtins/Unit + fn test__mulodi4(a: i64, b: i64, expected: i64, expected_overflow: c_int) !void { var overflow: c_int = undefined; - const x = __mulodi4(a, b, &overflow); + const x = mulo.__mulodi4(a, b, &overflow); try testing.expect(overflow == expected_overflow and (expected_overflow != 0 or x == expected)); } diff --git a/lib/std/special/compiler_rt/mulosi4_test.zig b/lib/std/special/compiler_rt/mulosi4_test.zig new file mode 100644 index 0000000000..523faa490f --- /dev/null +++ b/lib/std/special/compiler_rt/mulosi4_test.zig @@ -0,0 +1,72 @@ +const mulo = @import("mulo.zig"); +const testing = @import("std").testing; + +// ported from https://github.com/llvm-mirror/compiler-rt/tree/release_80/test/builtins/Unit + +fn test__mulosi4(a: i32, b: i32, expected: i32, expected_overflow: c_int) !void { + var overflow: c_int = undefined; + const x = mulo.__mulosi4(a, b, &overflow); + try testing.expect(overflow == expected_overflow and (expected_overflow != 0 or x == expected)); +} + +test "mulosi4" { + try test__mulosi4(0, 0, 0, 0); + try test__mulosi4(0, 1, 0, 0); + try test__mulosi4(1, 0, 0, 0); + try test__mulosi4(0, 10, 0, 0); + try test__mulosi4(10, 0, 0, 0); + try test__mulosi4(0, 0x1234567, 0, 0); + try test__mulosi4(0x1234567, 0, 0, 0); + + try test__mulosi4(0, -1, 0, 0); + try test__mulosi4(-1, 0, 0, 0); + try test__mulosi4(0, -10, 0, 0); + try test__mulosi4(-10, 0, 0, 0); + try test__mulosi4(0, -0x1234567, 0, 0); + try test__mulosi4(-0x1234567, 0, 0, 0); + + try test__mulosi4(1, 1, 1, 0); + try test__mulosi4(1, 10, 10, 0); + try test__mulosi4(10, 1, 10, 0); + try test__mulosi4(1, 0x1234567, 0x1234567, 0); + try test__mulosi4(0x1234567, 1, 0x1234567, 0); + + try test__mulosi4(1, -1, -1, 0); + try test__mulosi4(1, -10, -10, 0); + try test__mulosi4(-10, 1, -10, 0); + try test__mulosi4(1, -0x1234567, -0x1234567, 0); + try test__mulosi4(-0x1234567, 1, -0x1234567, 0); + + try test__mulosi4(0x7FFFFFFF, -2, @bitCast(i32, @as(u32, 0x80000001)), 1); + try test__mulosi4(-2, 0x7FFFFFFF, @bitCast(i32, @as(u32, 0x80000001)), 1); + try test__mulosi4(0x7FFFFFFF, -1, @bitCast(i32, @as(u32, 0x80000001)), 0); + try test__mulosi4(-1, 0x7FFFFFFF, @bitCast(i32, @as(u32, 0x80000001)), 0); + try test__mulosi4(0x7FFFFFFF, 0, 0, 0); + try test__mulosi4(0, 0x7FFFFFFF, 0, 0); + try test__mulosi4(0x7FFFFFFF, 1, 0x7FFFFFFF, 0); + try test__mulosi4(1, 0x7FFFFFFF, 0x7FFFFFFF, 0); + try test__mulosi4(0x7FFFFFFF, 2, @bitCast(i32, @as(u32, 0x80000001)), 1); + try test__mulosi4(2, 0x7FFFFFFF, @bitCast(i32, @as(u32, 0x80000001)), 1); + + try test__mulosi4(@bitCast(i32, @as(u32, 0x80000000)), -2, @bitCast(i32, @as(u32, 0x80000000)), 1); + try test__mulosi4(-2, @bitCast(i32, @as(u32, 0x80000000)), @bitCast(i32, @as(u32, 0x80000000)), 1); + try test__mulosi4(@bitCast(i32, @as(u32, 0x80000000)), -1, @bitCast(i32, @as(u32, 0x80000000)), 1); + try test__mulosi4(-1, @bitCast(i32, @as(u32, 0x80000000)), @bitCast(i32, @as(u32, 0x80000000)), 1); + try test__mulosi4(@bitCast(i32, @as(u32, 0x80000000)), 0, 0, 0); + try test__mulosi4(0, @bitCast(i32, @as(u32, 0x80000000)), 0, 0); + try test__mulosi4(@bitCast(i32, @as(u32, 0x80000000)), 1, @bitCast(i32, @as(u32, 0x80000000)), 0); + try test__mulosi4(1, @bitCast(i32, @as(u32, 0x80000000)), @bitCast(i32, @as(u32, 0x80000000)), 0); + try test__mulosi4(@bitCast(i32, @as(u32, 0x80000000)), 2, @bitCast(i32, @as(u32, 0x80000000)), 1); + try test__mulosi4(2, @bitCast(i32, @as(u32, 0x80000000)), @bitCast(i32, @as(u32, 0x80000000)), 1); + + try test__mulosi4(@bitCast(i32, @as(u32, 0x80000001)), -2, @bitCast(i32, @as(u32, 0x80000001)), 1); + try test__mulosi4(-2, @bitCast(i32, @as(u32, 0x80000001)), @bitCast(i32, @as(u32, 0x80000001)), 1); + try test__mulosi4(@bitCast(i32, @as(u32, 0x80000001)), -1, 0x7FFFFFFF, 0); + try test__mulosi4(-1, @bitCast(i32, @as(u32, 0x80000001)), 0x7FFFFFFF, 0); + try test__mulosi4(@bitCast(i32, @as(u32, 0x80000001)), 0, 0, 0); + try test__mulosi4(0, @bitCast(i32, @as(u32, 0x80000001)), 0, 0); + try test__mulosi4(@bitCast(i32, @as(u32, 0x80000001)), 1, @bitCast(i32, @as(u32, 0x80000001)), 0); + try test__mulosi4(1, @bitCast(i32, @as(u32, 0x80000001)), @bitCast(i32, @as(u32, 0x80000001)), 0); + try test__mulosi4(@bitCast(i32, @as(u32, 0x80000001)), 2, @bitCast(i32, @as(u32, 0x80000000)), 1); + try test__mulosi4(2, @bitCast(i32, @as(u32, 0x80000001)), @bitCast(i32, @as(u32, 0x80000000)), 1); +} diff --git a/lib/std/special/compiler_rt/muloti4.zig b/lib/std/special/compiler_rt/muloti4.zig deleted file mode 100644 index 7e3497ed7b..0000000000 --- a/lib/std/special/compiler_rt/muloti4.zig +++ /dev/null @@ -1,49 +0,0 @@ -const builtin = @import("builtin"); -const compiler_rt = @import("../compiler_rt.zig"); - -pub fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 { - @setRuntimeSafety(builtin.is_test); - - const min = @bitCast(i128, @as(u128, 1 << (128 - 1))); - const max = ~min; - overflow.* = 0; - - const r = a *% b; - if (a == min) { - if (b != 0 and b != 1) { - overflow.* = 1; - } - return r; - } - if (b == min) { - if (a != 0 and a != 1) { - overflow.* = 1; - } - return r; - } - - const sa = a >> (128 - 1); - const abs_a = (a ^ sa) -% sa; - const sb = b >> (128 - 1); - const abs_b = (b ^ sb) -% sb; - - if (abs_a < 2 or abs_b < 2) { - return r; - } - - if (sa == sb) { - if (abs_a > @divTrunc(max, abs_b)) { - overflow.* = 1; - } - } else { - if (abs_a > @divTrunc(min, -abs_b)) { - overflow.* = 1; - } - } - - return r; -} - -test { - _ = @import("muloti4_test.zig"); -} diff --git a/lib/std/special/compiler_rt/muloti4_test.zig b/lib/std/special/compiler_rt/muloti4_test.zig index 08450797fa..6d204ff785 100644 --- a/lib/std/special/compiler_rt/muloti4_test.zig +++ b/lib/std/special/compiler_rt/muloti4_test.zig @@ -1,9 +1,11 @@ -const __muloti4 = @import("muloti4.zig").__muloti4; +const mulo = @import("mulo.zig"); const testing = @import("std").testing; +// ported from https://github.com/llvm-mirror/compiler-rt/tree/release_80/test/builtins/Unit + fn test__muloti4(a: i128, b: i128, expected: i128, expected_overflow: c_int) !void { var overflow: c_int = undefined; - const x = __muloti4(a, b, &overflow); + const x = mulo.__muloti4(a, b, &overflow); try testing.expect(overflow == expected_overflow and (expected_overflow != 0 or x == expected)); } @@ -13,7 +15,6 @@ test "muloti4" { try test__muloti4(1, 0, 0, 0); try test__muloti4(0, 10, 0, 0); try test__muloti4(10, 0, 0, 0); - try test__muloti4(0, 81985529216486895, 0, 0); try test__muloti4(81985529216486895, 0, 0, 0); @@ -24,6 +25,18 @@ test "muloti4" { try test__muloti4(0, -81985529216486895, 0, 0); try test__muloti4(-81985529216486895, 0, 0, 0); + try test__muloti4(1, 1, 1, 0); + try test__muloti4(1, 10, 10, 0); + try test__muloti4(10, 1, 10, 0); + try test__muloti4(1, 81985529216486895, 81985529216486895, 0); + try test__muloti4(81985529216486895, 1, 81985529216486895, 0); + + try test__muloti4(1, -1, -1, 0); + try test__muloti4(1, -10, -10, 0); + try test__muloti4(-10, 1, -10, 0); + try test__muloti4(1, -81985529216486895, -81985529216486895, 0); + try test__muloti4(-81985529216486895, 1, -81985529216486895, 0); + try test__muloti4(3037000499, 3037000499, 9223372030926249001, 0); try test__muloti4(-3037000499, 3037000499, -9223372030926249001, 0); try test__muloti4(3037000499, -3037000499, -9223372030926249001, 0);