diff --git a/CMakeLists.txt b/CMakeLists.txt index f4bc1fe386..816685425a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -655,6 +655,7 @@ set(ZIG_STD_FILES "special/compiler_rt/floatuntitf.zig" "special/compiler_rt/muloti4.zig" "special/compiler_rt/multi3.zig" + "special/compiler_rt/popcountdi2.zig" "special/compiler_rt/truncXfYf2.zig" "special/compiler_rt/udivmod.zig" "special/compiler_rt/udivmoddi4.zig" diff --git a/std/special/compiler_rt.zig b/std/special/compiler_rt.zig index 6521dc1aaf..d0f29a68dc 100644 --- a/std/special/compiler_rt.zig +++ b/std/special/compiler_rt.zig @@ -67,6 +67,7 @@ comptime { @export("__fixtfti", @import("compiler_rt/fixtfti.zig").__fixtfti, linkage); @export("__udivmoddi4", @import("compiler_rt/udivmoddi4.zig").__udivmoddi4, linkage); + @export("__popcountdi2", @import("compiler_rt/popcountdi2.zig").__popcountdi2, linkage); @export("__udivsi3", __udivsi3, linkage); @export("__udivdi3", __udivdi3, linkage); diff --git a/std/special/compiler_rt/popcountdi2.zig b/std/special/compiler_rt/popcountdi2.zig new file mode 100644 index 0000000000..ea36b0ec44 --- /dev/null +++ b/std/special/compiler_rt/popcountdi2.zig @@ -0,0 +1,24 @@ +const builtin = @import("builtin"); +const compiler_rt = @import("../compiler_rt.zig"); + +// ported from llvm compiler-rt 8.0.0rc3 95e1c294cb0415a377a7b1d6c7c7d4f89e1c04e4 +pub extern fn __popcountdi2(a: i64) i32 { + var x2 = @bitCast(u64, a); + x2 = x2 - ((x2 >> 1) & 0x5555555555555555); + // Every 2 bits holds the sum of every pair of bits (32) + x2 = ((x2 >> 2) & 0x3333333333333333) + (x2 & 0x3333333333333333); + // Every 4 bits holds the sum of every 4-set of bits (3 significant bits) (16) + x2 = (x2 + (x2 >> 4)) & 0x0F0F0F0F0F0F0F0F; + // Every 8 bits holds the sum of every 8-set of bits (4 significant bits) (8) + var x: u32 = @truncate(u32, x2 + (x2 >> 32)); + // The lower 32 bits hold four 16 bit sums (5 significant bits). + // Upper 32 bits are garbage */ + x = x + (x >> 16); + // The lower 16 bits hold two 32 bit sums (6 significant bits). + // Upper 16 bits are garbage */ + return @bitCast(i32, (x + (x >> 8)) & 0x0000007F); // (7 significant bits) +} + +test "import popcountdi2" { + _ = @import("popcountdi2_test.zig"); +} diff --git a/std/special/compiler_rt/popcountdi2_test.zig b/std/special/compiler_rt/popcountdi2_test.zig new file mode 100644 index 0000000000..bedcbcd1de --- /dev/null +++ b/std/special/compiler_rt/popcountdi2_test.zig @@ -0,0 +1,27 @@ +const __popcountdi2 = @import("popcountdi2.zig").__popcountdi2; +const testing = @import("std").testing; + +fn naive_popcount(a_param: i64) i32 { + var a = a_param; + var r: i32 = 0; + while (a != 0) : (a = @bitCast(i64, @bitCast(u64, a) >> 1)) { + r += @intCast(i32, a & 1); + } + return r; +} + +fn test__popcountdi2(a: i64) void { + const x = __popcountdi2(a); + const expected = naive_popcount(a); + testing.expect(expected == x); +} + +test "popcountdi2" { + test__popcountdi2(0); + test__popcountdi2(1); + test__popcountdi2(2); + test__popcountdi2(@bitCast(i64, u64(0xFFFFFFFFFFFFFFFD))); + test__popcountdi2(@bitCast(i64, u64(0xFFFFFFFFFFFFFFFE))); + test__popcountdi2(@bitCast(i64, u64(0xFFFFFFFFFFFFFFFF))); + // TODO some fuzz testing +}