| author | |
| committer | |
| log | 1ea650bb75e009bb37b5bd4e896a90e2a86f8583 |
| tree | b23a47759b5c2f5c0f00a367227a56b03503c174 |
| parent | d3426ce634a09ec289480510040b3decfbd38c39 |
- apply simpler approach than LLVM for __popcountdi2
taken from The Art of Computer Programming and generalized
- rename popcountdi2.zig to popcount.zig
- test cases derived from popcountdi2_test.zig
- tests: compare naive approach 10_000 times with
random numbers created from naive seed 42
See #12907 files changed, 159 insertions(+), 39 deletions(-)
CMakeLists.txt+1-1| ... | ... | @@ -498,7 +498,7 @@ set(ZIG_STAGE2_SOURCES |
| 498 | 498 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/multi3.zig" |
| 499 | 499 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/negXf2.zig" |
| 500 | 500 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/os_version_check.zig" |
| 501 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/popcountdi2.zig" | |
| 501 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/popcount.zig" | |
| 502 | 502 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/shift.zig" |
| 503 | 503 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/stack_probe.zig" |
| 504 | 504 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/truncXfYf2.zig" |
lib/std/special/compiler_rt.zig+5-2| ... | ... | @@ -177,7 +177,6 @@ comptime { |
| 177 | 177 | .linkage = linkage, |
| 178 | 178 | }); |
| 179 | 179 | }, |
| 180 | ||
| 181 | 180 | else => {}, |
| 182 | 181 | } |
| 183 | 182 | |
| ... | ... | @@ -334,8 +333,12 @@ comptime { |
| 334 | 333 | |
| 335 | 334 | const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4; |
| 336 | 335 | @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage }); |
| 337 | const __popcountdi2 = @import("compiler_rt/popcountdi2.zig").__popcountdi2; | |
| 336 | const __popcountsi2 = @import("compiler_rt/popcount.zig").__popcountsi2; | |
| 337 | @export(__popcountsi2, .{ .name = "__popcountsi2", .linkage = linkage }); | |
| 338 | const __popcountdi2 = @import("compiler_rt/popcount.zig").__popcountdi2; | |
| 338 | 339 | @export(__popcountdi2, .{ .name = "__popcountdi2", .linkage = linkage }); |
| 340 | const __popcountti2 = @import("compiler_rt/popcount.zig").__popcountti2; | |
| 341 | @export(__popcountti2, .{ .name = "__popcountti2", .linkage = linkage }); | |
| 339 | 342 | |
| 340 | 343 | if (is_darwin) { |
| 341 | 344 | const __isPlatformVersionAtLeast = @import("compiler_rt/os_version_check.zig").__isPlatformVersionAtLeast; |
lib/std/special/compiler_rt/popcount.zig created+66| ... | ... | @@ -0,0 +1,66 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const std = @import("std"); | |
| 3 | ||
| 4 | // popcount - population count | |
| 5 | // counts the number of 1 bits | |
| 6 | ||
| 7 | // SWAR-Popcount: count bits of duos, aggregate to nibbles, and bytes inside | |
| 8 | // x-bit register in parallel to sum up all bytes | |
| 9 | // SWAR-Masks and factors can be defined as 2-adic fractions | |
| 10 | // TAOCP: Combinational Algorithms, Bitwise Tricks And Techniques, | |
| 11 | // subsubsection "Working with the rightmost bits" and "Sideways addition". | |
| 12 | fn popcountXi2_generic(comptime T: type) fn (a: T) callconv(.C) i32 { | |
| 13 | return struct { | |
| 14 | fn f(a: T) callconv(.C) i32 { | |
| 15 | @setRuntimeSafety(builtin.is_test); | |
| 16 | ||
| 17 | var x = switch (@bitSizeOf(T)) { | |
| 18 | 32 => @bitCast(u32, a), | |
| 19 | 64 => @bitCast(u64, a), | |
| 20 | 128 => @bitCast(u128, a), | |
| 21 | else => unreachable, | |
| 22 | }; | |
| 23 | const k1 = switch (@bitSizeOf(T)) { // -1/3 | |
| 24 | 32 => @as(u32, 0x55555555), | |
| 25 | 64 => @as(u64, 0x55555555_55555555), | |
| 26 | 128 => @as(u128, 0x55555555_55555555_55555555_55555555), | |
| 27 | else => unreachable, | |
| 28 | }; | |
| 29 | const k2 = switch (@bitSizeOf(T)) { // -1/5 | |
| 30 | 32 => @as(u32, 0x33333333), | |
| 31 | 64 => @as(u64, 0x33333333_33333333), | |
| 32 | 128 => @as(u128, 0x33333333_33333333_33333333_33333333), | |
| 33 | else => unreachable, | |
| 34 | }; | |
| 35 | const k4 = switch (@bitSizeOf(T)) { // -1/17 | |
| 36 | 32 => @as(u32, 0x0f0f0f0f), | |
| 37 | 64 => @as(u64, 0x0f0f0f0f_0f0f0f0f), | |
| 38 | 128 => @as(u128, 0x0f0f0f0f_0f0f0f0f_0f0f0f0f_0f0f0f0f), | |
| 39 | else => unreachable, | |
| 40 | }; | |
| 41 | const kf = switch (@bitSizeOf(T)) { // -1/255 | |
| 42 | 32 => @as(u32, 0x01010101), | |
| 43 | 64 => @as(u64, 0x01010101_01010101), | |
| 44 | 128 => @as(u128, 0x01010101_01010101_01010101_01010101), | |
| 45 | else => unreachable, | |
| 46 | }; | |
| 47 | x = x - ((x >> 1) & k1); // aggregate duos | |
| 48 | x = (x & k2) + ((x >> 2) & k2); // aggregate nibbles | |
| 49 | x = (x + (x >> 4)) & k4; // aggregate bytes | |
| 50 | x = (x *% kf) >> @bitSizeOf(T) - 8; // 8 most significant bits of x + (x<<8) + (x<<16) + .. | |
| 51 | return @intCast(i32, x); | |
| 52 | } | |
| 53 | }.f; | |
| 54 | } | |
| 55 | ||
| 56 | pub const __popcountsi2 = popcountXi2_generic(i32); | |
| 57 | ||
| 58 | pub const __popcountdi2 = popcountXi2_generic(i64); | |
| 59 | ||
| 60 | pub const __popcountti2 = popcountXi2_generic(i128); | |
| 61 | ||
| 62 | test { | |
| 63 | _ = @import("popcountsi2_test.zig"); | |
| 64 | _ = @import("popcountdi2_test.zig"); | |
| 65 | _ = @import("popcountti2_test.zig"); | |
| 66 | } |
lib/std/special/compiler_rt/popcountdi2.zig deleted-24| ... | ... | @@ -1,24 +0,0 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const compiler_rt = @import("../compiler_rt.zig"); | |
| 3 | ||
| 4 | // ported from llvm compiler-rt 8.0.0rc3 95e1c294cb0415a377a7b1d6c7c7d4f89e1c04e4 | |
| 5 | pub fn __popcountdi2(a: i64) callconv(.C) i32 { | |
| 6 | var x2 = @bitCast(u64, a); | |
| 7 | x2 = x2 - ((x2 >> 1) & 0x5555555555555555); | |
| 8 | // Every 2 bits holds the sum of every pair of bits (32) | |
| 9 | x2 = ((x2 >> 2) & 0x3333333333333333) + (x2 & 0x3333333333333333); | |
| 10 | // Every 4 bits holds the sum of every 4-set of bits (3 significant bits) (16) | |
| 11 | x2 = (x2 + (x2 >> 4)) & 0x0F0F0F0F0F0F0F0F; | |
| 12 | // Every 8 bits holds the sum of every 8-set of bits (4 significant bits) (8) | |
| 13 | var x: u32 = @truncate(u32, x2 + (x2 >> 32)); | |
| 14 | // The lower 32 bits hold four 16 bit sums (5 significant bits). | |
| 15 | // Upper 32 bits are garbage */ | |
| 16 | x = x + (x >> 16); | |
| 17 | // The lower 16 bits hold two 32 bit sums (6 significant bits). | |
| 18 | // Upper 16 bits are garbage */ | |
| 19 | return @bitCast(i32, (x + (x >> 8)) & 0x0000007F); // (7 significant bits) | |
| 20 | } | |
| 21 | ||
| 22 | test { | |
| 23 | _ = @import("popcountdi2_test.zig"); | |
| 24 | } |
lib/std/special/compiler_rt/popcountdi2_test.zig+19-12| ... | ... | @@ -1,27 +1,34 @@ |
| 1 | const __popcountdi2 = @import("popcountdi2.zig").__popcountdi2; | |
| 1 | const popcount = @import("popcount.zig"); | |
| 2 | 2 | const testing = @import("std").testing; |
| 3 | 3 | |
| 4 | fn naive_popcount(a_param: i64) i32 { | |
| 5 | var a = a_param; | |
| 4 | fn popcountdi2Naive(a: i64) i32 { | |
| 5 | var x = a; | |
| 6 | 6 | var r: i32 = 0; |
| 7 | while (a != 0) : (a = @bitCast(i64, @bitCast(u64, a) >> 1)) { | |
| 8 | r += @intCast(i32, a & 1); | |
| 7 | while (x != 0) : (x = @bitCast(i64, @bitCast(u64, x) >> 1)) { | |
| 8 | r += @intCast(i32, x & 1); | |
| 9 | 9 | } |
| 10 | 10 | return r; |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | 13 | fn test__popcountdi2(a: i64) !void { |
| 14 | const x = __popcountdi2(a); | |
| 15 | const expected = naive_popcount(a); | |
| 16 | try testing.expect(expected == x); | |
| 14 | const x = popcount.__popcountdi2(a); | |
| 15 | const expected = popcountdi2Naive(a); | |
| 16 | try testing.expectEqual(expected, x); | |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | 19 | test "popcountdi2" { |
| 20 | 20 | try test__popcountdi2(0); |
| 21 | 21 | try test__popcountdi2(1); |
| 22 | 22 | try test__popcountdi2(2); |
| 23 | try test__popcountdi2(@bitCast(i64, @as(u64, 0xFFFFFFFFFFFFFFFD))); | |
| 24 | try test__popcountdi2(@bitCast(i64, @as(u64, 0xFFFFFFFFFFFFFFFE))); | |
| 25 | try test__popcountdi2(@bitCast(i64, @as(u64, 0xFFFFFFFFFFFFFFFF))); | |
| 26 | // TODO some fuzz testing | |
| 23 | try test__popcountdi2(@bitCast(i64, @as(u64, 0xffffffff_fffffffd))); | |
| 24 | try test__popcountdi2(@bitCast(i64, @as(u64, 0xffffffff_fffffffe))); | |
| 25 | try test__popcountdi2(@bitCast(i64, @as(u64, 0xffffffff_ffffffff))); | |
| 26 | ||
| 27 | const RndGen = @import("std").rand.DefaultPrng; | |
| 28 | var rnd = RndGen.init(42); | |
| 29 | var i: u32 = 0; | |
| 30 | while (i < 10_000) : (i += 1) { | |
| 31 | var rand_num = rnd.random().int(i64); | |
| 32 | try test__popcountdi2(rand_num); | |
| 33 | } | |
| 27 | 34 | } |
lib/std/special/compiler_rt/popcountsi2_test.zig created+34| ... | ... | @@ -0,0 +1,34 @@ |
| 1 | const popcount = @import("popcount.zig"); | |
| 2 | const testing = @import("std").testing; | |
| 3 | ||
| 4 | fn popcountsi2Naive(a: i32) i32 { | |
| 5 | var x = a; | |
| 6 | var r: i32 = 0; | |
| 7 | while (x != 0) : (x = @bitCast(i32, @bitCast(u32, x) >> 1)) { | |
| 8 | r += @intCast(i32, x & 1); | |
| 9 | } | |
| 10 | return r; | |
| 11 | } | |
| 12 | ||
| 13 | fn test__popcountsi2(a: i32) !void { | |
| 14 | const x = popcount.__popcountsi2(a); | |
| 15 | const expected = popcountsi2Naive(a); | |
| 16 | try testing.expectEqual(expected, x); | |
| 17 | } | |
| 18 | ||
| 19 | test "popcountsi2" { | |
| 20 | try test__popcountsi2(0); | |
| 21 | try test__popcountsi2(1); | |
| 22 | try test__popcountsi2(2); | |
| 23 | try test__popcountsi2(@bitCast(i32, @as(u32, 0xfffffffd))); | |
| 24 | try test__popcountsi2(@bitCast(i32, @as(u32, 0xfffffffe))); | |
| 25 | try test__popcountsi2(@bitCast(i32, @as(u32, 0xffffffff))); | |
| 26 | ||
| 27 | const RndGen = @import("std").rand.DefaultPrng; | |
| 28 | var rnd = RndGen.init(42); | |
| 29 | var i: u32 = 0; | |
| 30 | while (i < 10_000) : (i += 1) { | |
| 31 | var rand_num = rnd.random().int(i32); | |
| 32 | try test__popcountsi2(rand_num); | |
| 33 | } | |
| 34 | } |
lib/std/special/compiler_rt/popcountti2_test.zig created+34| ... | ... | @@ -0,0 +1,34 @@ |
| 1 | const popcount = @import("popcount.zig"); | |
| 2 | const testing = @import("std").testing; | |
| 3 | ||
| 4 | fn popcountti2Naive(a: i128) i32 { | |
| 5 | var x = a; | |
| 6 | var r: i32 = 0; | |
| 7 | while (x != 0) : (x = @bitCast(i128, @bitCast(u128, x) >> 1)) { | |
| 8 | r += @intCast(i32, x & 1); | |
| 9 | } | |
| 10 | return r; | |
| 11 | } | |
| 12 | ||
| 13 | fn test__popcountti2(a: i128) !void { | |
| 14 | const x = popcount.__popcountti2(a); | |
| 15 | const expected = popcountti2Naive(a); | |
| 16 | try testing.expectEqual(expected, x); | |
| 17 | } | |
| 18 | ||
| 19 | test "popcountti2" { | |
| 20 | try test__popcountti2(0); | |
| 21 | try test__popcountti2(1); | |
| 22 | try test__popcountti2(2); | |
| 23 | try test__popcountti2(@bitCast(i128, @as(u128, 0xffffffff_ffffffff_ffffffff_fffffffd))); | |
| 24 | try test__popcountti2(@bitCast(i128, @as(u128, 0xffffffff_ffffffff_ffffffff_fffffffe))); | |
| 25 | try test__popcountti2(@bitCast(i128, @as(u128, 0xffffffff_ffffffff_ffffffff_ffffffff))); | |
| 26 | ||
| 27 | const RndGen = @import("std").rand.DefaultPrng; | |
| 28 | var rnd = RndGen.init(42); | |
| 29 | var i: u32 = 0; | |
| 30 | while (i < 10_000) : (i += 1) { | |
| 31 | var rand_num = rnd.random().int(i128); | |
| 32 | try test__popcountti2(rand_num); | |
| 33 | } | |
| 34 | } |