@HiredGoon said:
x = x + y evaluates x twice. This cannot always be optimized away as x
does not have to be a primitive, perhaps it is inside a collections and
requires 2 lookups?
This might be easier if we write x and y as X and Y, to suggest
arbitrary expressions, rather than variables. Let's say they're
of type T.
In the language-machine, X = X + Y means "add (eval X) and (eval Y) in z and store z in (eval X as an lvalue)". If:
1. the compiler can make reasonable assumptions about the
semantics of evaluating X as an lvalue (for example, if X is a
variable, not some crazy function-result reference) and
2. it's able to store that lvalue,
then it should be able to optimize away the double evaluation of X. Furthermore, if
3. constructing and destructing objects of type T has no side-effects and
4. the value of A + B is identical to the value of A += B
then it ought to be able to optimize X = X + Y into X += Y.
Statements 3 and 4 are undecidable in the general case, so it's
unlikely that a C++ compiler would attempt them for anything except the
built-in types -- but it's quite reasonable to attempt them for
primitive types, which is why the gcc results compiled identically.
Now, C++ lets you construct lvalues willy-nilly -- you can return a
reference, for example, or simply deference a pointer -- but Java
really only has two lvalue expressions: non-final local variables
and array-index expressions. You can't really optimize anything
about a local variable load/store, but you theoretically could optimize
an array-index expression load/store by keeping &a[i] around --
except that the JVM doesn't support that sort of thing.
The only difference between your two java-assembler listings is that one does a dup2 to turn this stack:
..., a, 0
into this one:
..., a, 0, a, 0
and the other just does loads the local variable and does an
iconst_0. I doubt this has any significant performance effect,
but a competent JIT compiler might fix it anyway.