Small repro case here: https://godbolt.org/z/61898b6Ps
Looks like this regressed from Clang 17 to 18, then regressed even further from Clang 20 to Clang 21.
The 17->18 change looks like a x86 backend regression to me. 17 and 18 both have a int32 shufflevector with (0,1,2,3, 12,13,14,15) as the final operation, Clang 17 lowers it to vblendps while 18 uses vinsertf128.
The 20->21 regression looks trickier to me: evidently extra transforms ran at the IR level that got rid of the shuffles representing the i64 broadcast and coalesced all 3 original shuffles into a int64 shufflevector with indices (0,0,2,2).
Either way this was written specifically to avoid shuffle operations in a kernel that is already shuffle-heavy and bottlenecked on the relevant ports (especially on Intel Haswell through the Skylake variants which only have a single shuffle unit on port 5). The literal translation would've been great here (the broadcast-loads get handled by the load unit), this is strictly worse.
Small repro case here: https://godbolt.org/z/61898b6Ps
Looks like this regressed from Clang 17 to 18, then regressed even further from Clang 20 to Clang 21.
The 17->18 change looks like a x86 backend regression to me. 17 and 18 both have a int32
shufflevectorwith (0,1,2,3, 12,13,14,15) as the final operation, Clang 17 lowers it tovblendpswhile 18 usesvinsertf128.The 20->21 regression looks trickier to me: evidently extra transforms ran at the IR level that got rid of the shuffles representing the i64 broadcast and coalesced all 3 original shuffles into a int64
shufflevectorwith indices (0,0,2,2).Either way this was written specifically to avoid shuffle operations in a kernel that is already shuffle-heavy and bottlenecked on the relevant ports (especially on Intel Haswell through the Skylake variants which only have a single shuffle unit on port 5). The literal translation would've been great here (the broadcast-loads get handled by the load unit), this is strictly worse.