Skip to content

Use the popcount64c bit-counting sequence in Dar_WordCountOnes - #533

Open
TrevorHansen wants to merge 1 commit into
berkeley-abc:masterfrom
TrevorHansen:popcount64c-in-darcut
Open

Use the popcount64c bit-counting sequence in Dar_WordCountOnes#533
TrevorHansen wants to merge 1 commit into
berkeley-abc:masterfrom
TrevorHansen:popcount64c-in-darcut

Conversation

@TrevorHansen

@TrevorHansen TrevorHansen commented Jul 27, 2026

Copy link
Copy Markdown

The popcount (hamming weight) instruction has been common on x86 processors since about 2008. It returns the number of 1-bits in a register with 3-cycle latency and 1-cycle throughput.

ABC uses hand rolled SIMD within a register (SWAR) to achieve the same.

static inline int Dar_WordCountOnes( unsigned uWord )
{
    uWord = (uWord & 0x55555555) + ((uWord>>1) & 0x55555555);
    uWord = (uWord & 0x33333333) + ((uWord>>2) & 0x33333333);
    uWord = (uWord & 0x0F0F0F0F) + ((uWord>>4) & 0x0F0F0F0F);
    uWord = (uWord & 0x00FF00FF) + ((uWord>>8) & 0x00FF00FF);
    return  (uWord & 0x0000FFFF) + (uWord>>16);
}

When passed the -mpopcnt flag, clang and GCC will pattern match and replace some matching SWAR with a popcount instruction.

Unfortunately, the SWAR above is not recognised by any GCC I tested (7 to 16), or any clang (9 to 22), so it emits 23 instructions instead of 1.

Popcounts are in other places in ABC (e.g. #436), so other users would benefit if the other uses were sped up. Only this function is in our hot-path - some CNF generation problems have 7% of their runtime in Dar_WordCountOnes, so I've focused on it.

The above function is the naive implementation (popcount64a) from:
https://en.wikipedia.org/wiki/Hamming_weight

This PR changes to the best implementation (popcount64c) from the same page:

uWord = uWord - ((uWord >> 1) & 0x55555555);
uWord = (uWord & 0x33333333) + ((uWord >> 2) & 0x33333333);
uWord = (uWord + (uWord >> 4)) & 0x0F0F0F0F;
return (int)((uWord * 0x01010101) >> 24);

Which is converted by GCC 10+ and clang 10+ to a single instruction, and is 8 fewer instructions if not compiled with -mpopcnt.

The sequence currently used is popcount64a, which no GCC from 7 to 16 and no
clang from 9 to 22 recognises as a population count, so it always emits 23
instructions. popcount64c is recognised by GCC 10 and later and clang 10 and
later, which emit a single popcnt where the target has the instruction, and is
8 instructions shorter where it does not.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant