Generate mask for non zero (or inverse) nibbles in integer Apologies for the cryptic title, but the problem really is rather simple.
I have a 64 bit integer which I interpret as 16 4 bit nibbles, or a nibble array if you wish, and I needed generate a mask covering the nibbles which are zero, or the inverse.
I have already come up with a solution, but it seems to me that there has to be a better way.
uint64_t getNonZeroMask( uint64_t b )
{
    return ( ( ( b &gt;&gt; 2 | b ) &gt;&gt; 1 | ( b &gt;&gt; 2 | b ) ) &amp; 0x1111111111111111 ) * 15;
}

The above is the most efficient I've found. I've tried numerous approaches, but they pretty much all use the same approach.
Just in case there should be any doubt what exactly I'm frying to do, a give value:
ex.
0x010203456000abc0

Should give the mask:
0x0f0f0ffff000fff0

or the inverse.
What I have works, of course, but I do believe it can be done better, and I'd very much like to know how. Preferably it would only involve bitwise operations. Intrinsics from bmi and similar are okay, but I haven't found anything that seems useful. It may be that I'm entirely in the wrong, and this is indeed the way of doing it, in which case I'd very much like to know that too.
Intrinsics from bmi and similar are okay, but I haven't found anything that seems useful.

They're not very useful for this. pdep and pext let you do this in a different way, but it's not better (especially not on AMD Zen1), you shouldn't use this (but maybe it's interesting to think about):
uint64_t m = 0x1111111111111111;
uint64_t stacked = _pext_u64(b, m) | _pext_u64(b, m &lt;&lt; 1) |
                   _pext_u64(b, m &lt;&lt; 2) | _pext_u64(b, m &lt;&lt; 3);
return _pdep_u64(stacked, m) * 15;

There is a slightly different way to write the original logic in a way that may be easier to read, of course that's subjective but it will be as a straight-line composition of common bitwise idioms, it shouldn't make any difference for a compiler. It's the same thing really, but instead of being a single expression it's linearized into discrete steps, which also creates the opportunity for in-line comments to explain the steps:
uint64_t getNonZeroMask( uint64_t b )
{
    // horizontal OR of nibbles in log(bits) steps
    b |= b &gt;&gt; 2;
    b |= b &gt;&gt; 1;
    // remove polluted bits, leaving the results in the lsb of every nibble
    b &amp;= 0x1111111111111111;
    // spread single bit flags into full nibbles
    return b * 15;
}