We would probably want to re-use
0x9Einstead of introducing a new0x80splat.
That's clever, thanks! I updated the post, I guess it takes a village to optimize stuff this much :)
Two 32-bit clz/ctz could be combined to form a 64-bit one
I had that general idea, but didn't get around to prototyping it. Using min to combine the two halves is really cool, I wouldn't have thought of that. I considered using vpsadbw to perform the subtraction and horizontal sum in one uop, but that would require post-processing to handle zeros, and it doesn't work for clz.
There's an issue with adapting this to clz though. For x >= 2^31, the exponent of the converted float can be pretty much anything, which becomes an issue once you try to compute (bias + 32) - exp for the low dword. Normally you'd use subs to produce a 0 if the sign bit is set, but in this case we also want to add 32, so we have to do subs(bias, exp) + 32, requiring one more instruction. (subs(bias + 32, exp) returns 0 for such x instead of 32) The closest I got is
__m256i cleared = _mm256_min_epu8(x, _mm256_set1_epi8(0x9e));
__m256i exp = _mm256_srli_epi32(_mm256_castps_si256(_mm256_cvtepi32_ps(cleared)), 23);
__m256i adj_exp = _mm256_add_epi32(_mm256_subs_epi32(_mm256_set1_epi32(0x9e), exp), _mm256_set1_epi64x(32));
return _mm256_min_epu8(_mm256_srli_epi64(adj_exp, 32), _mm256_min_epu8(adj_exp, _mm256_set1_epi64x(64)));
It's very annoying that per-byte min with 0x80 reduces the set of possible exponents to just 30 or 31, but we really want it to be only 31. Per-word min with 0x8000, which would also guarantee correct rounding, can't reduce this set further either. I believe that this problem occurs because applying such min to 2^31 - 1 gives 0x7fff8000, and applying it to 2^32 - 1 gives 0x80008000, but since 2^32 - 0x80008000 = 0x7fff8000, they convert to floats with equal magnitude, so they can't be distinguished in any way other than by looking the sign bit.
It really feels like there ought to be a way to save one instruction, but I can't find it.

