logoalt Hacker News

fsmvtoday at 4:23 PM2 repliesview on HN

But it's not exactly a cosmetic change. x++ is semantically different from x; x++; I wonder if clang would make it branchless if you instead write

  if (BLQS_CMP(x, piv)) { *lwr = x; ++lwr; }
    else { *rwr = x; --rwr; }
The difference is post-increment has strange semantics. While the compiler should be able to understand that the value wasn't used and post increment and pre increment are the same I wouldn't be surprised if it tracks that it was post increment and misses some optimizations because it's trying to garuntee post increment semantics.

Although it's true compilers can be very sensitive to exact phrasing triggering specific optimization passes. So it still might not give the branchless version by changing it to pre increment (which is the same as a normal +=1).

The only way to really know is to dig into what optimization passes clang took in both cases and analyze the difference.


Replies

chnoblouchtoday at 5:00 PM

Looking at the optimization pipeline in Compiler Explorer (https://godbolt.org/z/rd3qber3b) after the code is converted to SSA (SROAPass), it seems that the fast version computes the pointer increment (`lwr++`) before storing `x` (`*lwr = x`), whereas the slow version does it the other way around. This happens because Clang chooses a different instruction order when generating a single statement compared to two statements.

The computation is the same, but apparently, this tiny change prevents SimplifyCFGPass from turning the code into the branchless version later on. I'm not sure why this happens, perhaps because it messes something up in the pattern recognition of the pass?

pixelesquetoday at 4:57 PM

Note: in some cases (not completely sure if it applies in this situation as I haven't looked into the details of this code enough), using pre-increment AND when the value is used is a dependency on that operation, so "+= 1" is actually faster for out of order execution.

So yeah, as you say, compilers can be very sensitive to this, and in the past (although more than 10 years ago now, so might not be relevant now), ICC often used to generate better (faster executing) code when using "val += 1" vs "++val".