The classic example of where register-poor ISAs hurt isn't about "subtle differences" at all. It's the fact that there are still only 8 (for i386) named registers, and so anything that needs to deal with a working set beyond that needs to do spill/fill to memory, and you can't "rename" memory accesses (though you can sort of cheat, as with the store forward optimization -- but that doesn't work nearly so well as renaming does).
Everything else is general memory optimizations that apply for everything like the aforementioned store forwarding. It's still expensive if the CPU can't use them (mismatched load/store size, incorrect speculation, etc.)
The only weird thing there is the compiler lucks out in generating a version that allows OoOE to save the programmer from themself.
In effect, the compiler was asked to compile
void foo(float &resVal, float &val1, float &val2, float &val3, float &val4, float &val5, float &val6, float &val7) {
float tmp = val1+val2+val3+val4+val5+val6+val7;
resVal += tmp;
}
whereas the author wrote asm doing void foo(float resVal, float val1, float val2, float val3, float val4, float val5, float val6, float val7) {
resVal += val1;
resVal += val2;
resVal += val3;
resVal += val4;
resVal += val5;
resVal += val6;
resVal += val7;
}
Which isn't the same thing. And if the author unrolled it with a hundred registers, it would still be every bit as slow.And it should be obvious that it's slow not because it's messing up OoOE, it's slow because it's one incredibly long dependency chain with a stall between each add, due to it depending on the result of the previous add. OoOE happens to be able to fix the programmer's mistake for the first case, which is luck because the programmer obviously didn't intend for it to do so.
But now I'm repeating myself...
Well, I'm assuming the programmer wrote it like that, because otherwise a smart compiler would have calculated val1+...+val7 before the loop and just added that precalculated value repeatedly.
Also it doesn't matter if it did keep resVal (and val1..val7) in a register, it would be every bit as fast as it is now.
Similarly, the compiler has no leeway about the order of floating point additions. If the C++ was written to have identical output as the asm, the compiler could not be faster than the asm without violating the C++ standard.
Again, OoOE plays absolutely positively no part whatsoever in making the asm slow. Neither does which values are in registers or memory.
It's slow because a chain of instructions that use the output of the previous as input cannot execute in fewer cycles than (latency)*(num instructions). Period.
It's the sort of thing that should have been written by the programmer as something like
res += (val1+val2)+(val3+val4);
res += (val5+val6)+(val7);
then we wouldn't be having this discussion...