| 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,7 +498,7 @@ set(ZIG_STAGE2_SOURCES |
| 498 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/multi3.zig" | 498 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/multi3.zig" |
| 499 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/negXf2.zig" | 499 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/negXf2.zig" |
| 500 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/os_version_check.zig" | 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 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/shift.zig" | 502 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/shift.zig" |
| 503 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/stack_probe.zig" | 503 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/stack_probe.zig" |
| 504 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/truncXfYf2.zig" | 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,7 +177,6 @@ comptime { |
| 177 | .linkage = linkage, | 177 | .linkage = linkage, |
| 178 | }); | 178 | }); |
| 179 | }, | 179 | }, |
| 180 | |||
| 181 | else => {}, | 180 | else => {}, |
| 182 | } | 181 | } |
| 183 | 182 | ||
| ... | @@ -334,8 +333,12 @@ comptime { | ... | @@ -334,8 +333,12 @@ comptime { |
| 334 | 333 | ||
| 335 | const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4; | 334 | const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4; |
| 336 | @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage }); | 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 | @export(__popcountdi2, .{ .name = "__popcountdi2", .linkage = linkage }); | 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 | if (is_darwin) { | 343 | if (is_darwin) { |
| 341 | const __isPlatformVersionAtLeast = @import("compiler_rt/os_version_check.zig").__isPlatformVersionAtLeast; | 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,27 +1,34 @@ |
| 1 | const __popcountdi2 = @import("popcountdi2.zig").__popcountdi2; | 1 | const popcount = @import("popcount.zig"); |
| 2 | const testing = @import("std").testing; | 2 | const testing = @import("std").testing; |
| 3 | 3 | ||
| 4 | fn naive_popcount(a_param: i64) i32 { | 4 | fn popcountdi2Naive(a: i64) i32 { |
| 5 | var a = a_param; | 5 | var x = a; |
| 6 | var r: i32 = 0; | 6 | var r: i32 = 0; |
| 7 | while (a != 0) : (a = @bitCast(i64, @bitCast(u64, a) >> 1)) { | 7 | while (x != 0) : (x = @bitCast(i64, @bitCast(u64, x) >> 1)) { |
| 8 | r += @intCast(i32, a & 1); | 8 | r += @intCast(i32, x & 1); |
| 9 | } | 9 | } |
| 10 | return r; | 10 | return r; |
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | fn test__popcountdi2(a: i64) !void { | 13 | fn test__popcountdi2(a: i64) !void { |
| 14 | const x = __popcountdi2(a); | 14 | const x = popcount.__popcountdi2(a); |
| 15 | const expected = naive_popcount(a); | 15 | const expected = popcountdi2Naive(a); |
| 16 | try testing.expect(expected == x); | 16 | try testing.expectEqual(expected, x); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | test "popcountdi2" { | 19 | test "popcountdi2" { |
| 20 | try test__popcountdi2(0); | 20 | try test__popcountdi2(0); |
| 21 | try test__popcountdi2(1); | 21 | try test__popcountdi2(1); |
| 22 | try test__popcountdi2(2); | 22 | try test__popcountdi2(2); |
| 23 | try test__popcountdi2(@bitCast(i64, @as(u64, 0xFFFFFFFFFFFFFFFD))); | 23 | try test__popcountdi2(@bitCast(i64, @as(u64, 0xffffffff_fffffffd))); |
| 24 | try test__popcountdi2(@bitCast(i64, @as(u64, 0xFFFFFFFFFFFFFFFE))); | 24 | try test__popcountdi2(@bitCast(i64, @as(u64, 0xffffffff_fffffffe))); |
| 25 | try test__popcountdi2(@bitCast(i64, @as(u64, 0xFFFFFFFFFFFFFFFF))); | 25 | try test__popcountdi2(@bitCast(i64, @as(u64, 0xffffffff_ffffffff))); |
| 26 | // TODO some fuzz testing | 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 | } | ||