*lwr = x; lwr++;
This could be be represented with something like this (and this is a very vague approximation of an AST): block
statement (assignment)
expression
operator (dereference)
variable
expression
variable
statement
expression
operator (post-increment)
variable
The second form, that looks this in the source: *lwr++ = x
might look like this in the AST: block
statement (assignment)
expression
operator (post-increment)
operator (dereference)
variable
expression
variable
If these two ASTs are to take the same form in the compilation pipeline, there needs to be some kind of pass that transforms one into the other.As for why the second form is faster (or rather, why the compiler can generate faster code): There is likely an optimisation pass somewhere in llvm that recognises a pattern that this fits into, which allows it to generate branchless instructions. For instance, in the second form, there is a pattern of:
operator (post-increment)
operator (dereference)
that might be recognised by a pass. In the former form, the two operators are far apart in the tree, so a pass would have to "look further" to match them up. A single pass likely won't do this, either for (compiler) performance reasons, or for correctness reasons.Finding which pass that is can be non-trivial, as it's more than a matter of enabling individual passes until one works. It might be that an earlier pass does some code reshaping that allows the relevant pass to work. My suggestion would be to dump the llvm ir at the end, and find the rough pattern that you're looking for, then re-run the compilation with `-mllvm -print-after-all` to see what the IR looks like after each pass, and then manually "look back" until you can't see the pattern any more.
block
statement (assignment)
expression
operator (post-increment)
operator (dereference)
variable
expression
variable
I don't think this could possibly be a valid AST for '*lwr++ = x' because the increment is not an operation on the dereferenced value, its an operation on the pointer. So in this case I don't see how it could help but be transformed into a form similar to the "beginner friendly" case.Or perhaps I am wrong and it would generate an AST like you describe and rely on later passes to actually create a proper dependency graph. My mental model of how these kinds of postfix operators work always assumed it must very early on turn it into two separate statements. Thank you for the suggestions.
block
statement (assignment)
expression
operator (dereference)
operator (post-increment)
variable
expression
variableEdit: not saying they are different just that it is harder for the compiler to see the safety of the transform when they are quite different.
You can ask the compiler to dump its intermediate representations, so it might not be too hard to answer the question.
Why restrict the optimisation unnecessarily like this? It's never the intention to artificially restrict optimisations, but they are difficult to test and debug as they can interact with other optimisations, and applying an optimisation too broadly leads to horrible correctness bugs. So if you cannot be certain that you understand all possible interactions now and in the future, it makes some sense to be conservative in applying them.