zimv20 said:
afaik, this is an undefined expression in the c++ language because the order of the evaulation of expressions is undefined.
therefore, neither compiler result is right or wrong, because the result is simply a product of its implemenation of that undefined expression.
i hope that, IRL, no one's coding like that.
Yes, this is the correct answer. In C/C++, you cannot refer to an autoincremented variabled in the same statement, as that's undefined.
Basically, if we break down what i = ++i means, in assembler, we get:
INCREMENT MemLocation i
STORE MemLocation i -> MemLocation i
Sounds straightforward, right? Well, most RISC platforms cannot operate on values in memory, but can only do so in registers. So, we have:
LOAD MemLocation i -> Register Rx
INCREMENT Rx
STORE Register Rx -> MemLocation i // Keep Rx, i in sync
STORE Register Rx -> MemLocation i // Do i = ...
In both cases there's a redundant STORE , but maybe an optimising compiler could take away the second STORE.
Now, let's look at i = ++i + ++i. Preincrement takes precedence over addition. Also, in this specific case, right to left, or left to right, would not make a difference, but would with a different expression.
So a parse tree would be built like this:
Code:
=
/ \
i +
/ \
pre++ pre++
| |
i i
We could write that parse tree as (let me build it in steps) the following. Assume arguments are evaluated left to right:
assignTo( "++i + ++i", i )
assignTo( add("++i", "++i"), i )
assignTo( add( preInc(i), preInc(i) ), i )
Because the C/C++ language(s) do not define this any further, it all comes down to compiler implementation. So, we're left with a bunch of questions:
1. Does each call to preInc(i) load i from memory? Or is it preloaded once, and used repeatedly, in a register, or is it lazily loaded on demand?
2. If preInc loads i into a register, does it save that register back into the memory location i right away?
So, a simplistic execution of the tree would yield, using lazy evaluation of i:
// 1st preInc
LOAD MemLocation i -> Register Rx
INCREMENT Rx
STORE Register Rx -> MemLocation i // Keep Rx, i in sync
// 2nd preInc
LOAD MemLocation i -> Register Ry
INCREMENT Ry
STORE Register Ry -> MemLocation i // Keep Ry, i in sync
ADD Rx,Ry,Rz
STORE Register Rz -> MemLocation i
// i = 3
Here's an optimised version, with preloading and sharing of i:
LOAD MemLocation i -> Register Rx
// 1st preInc
INCREMENT Rx
STORE Register Rx -> MemLocation i // Keep Rx, i in sync
// 2nd preInc
INCREMENT Rx
STORE Register Rx -> MemLocation i // Keep Rx, i in sync
ADD Rx,Rx,Rz
STORE Register Rz -> MemLocation i
// i = 4
The core of the problem is that when you use ++i or i++ inside of an expression, you're saying, I want to to modify i and pass the value to another part of the expression. Is i in RAM, or a register? Does the other part of the expression go back to i or just use it in the working register?
Or do we just say, do whatever's fastest on my architecture, with full optimisation:
// i is only a register, no RAM
// i = 0;
// i = ++i + ++i;
CLR Rx
INC Rx
INC Rx
ADD Rx,Rx,Rx
// i=4