Basically, do not depend on the order of evaluation of arguments to a function call, because it is unspecified and left to the implementation to decide according to the C99 standard.
The output should be (at least from my understanding and what PHP renders):
7 9 9
7 2 2
As far as reconciliation, I guess the first line makes sense when you think about it as machine code and how the pointers probably behave. The second line though, seems quite evil and I am going to go hide under my "Rails will save me" blanket until someone can explain this voodoo.
2,3,7 are all valid possibilities for the 4th and 6th numbers, but apparently gcc interprets the 5th as 2 + 1 somehow, which I can't see as being valid whatsoever.
Why not? It's doing the x=2 first (before anything else in there), then the ++ of the last expression (and returning 2 for it), then giving you the result of the middle one (which is now three since it ran after the last one). The 7 is because it ran that one before doing anything to the other values.
It should be noted that this is illegal C, and compilers are allowed to do absolutely anything they want when they see it. Even things that are totally not logical.
I re-read C99, and the reason this is allowed is not because of §6.5.2.2 p10, but rather §6.5.16 p4 as far as I can tell. Though it's weird to me that the expression (x = 2) can ever be evaluated as anything but 2.
Also, it's not illegal C, it's undefined C and compilers are only allowed to do whatever they want within the undefined area (which is larger than I expected.)
I find it hard to believe that you wouldn't know how many registers code should use. However - it's not C code, it's assembly code, so it seems unfair to blame gcc - a C compiler.
There are a couple reasons you wouldn't know that (for an entire inline asm function):
1. x86_64 vs. x86_32. It's possible that you'd want to use more registers than 32-bit has. In this case, it's usually optimal to use memory operands on x86_32 but registers on x86_64.
2. gcc _will not_ allow you to use ebx in inline asm with PIC code. Likewise with ebp with frame pointer. There's no technical reason gcc couldn't just save+restore those registers around the block, but it doesn't.
3. The case in the bugreport: you have various locations in memory you want to address, and you want gcc to figure out their addresses for you. Without analysis that eliminates identical registers, the naive implementation of using 1-2 new registers per operand will quickly fail.
> There's no technical reason gcc couldn't just save+restore those registers around the block, but it doesn't.
While it's not impossible to do, it makes debugging a lot harder. gdb would have a much harder job of figuring out what's going on if the code messed with ebp, since the fp location would depend on the code. Some more debug information to describe that location could solve the problem... but why create that problem in the first place.
Martin, you should realize that this problem cannot be solved. Yes,
there will perhaps be a time when this particular test case compiles,
though I think that is unlikely. But anyway, then there will be other
cases that fail.
The reason is dead simple: register allocation is NP-complete, so it
is even theoretically not possible to write register allocators that
always find a coloring. That means any register allocator will always
fail on some very constrained asm input. And you cannot allow it to
run indefinitely until a coloring is found, because then you've turned
the graph coloring problem into the halting problem because you can't
prove that a coloring exists and that the register allocator algorithm
will terminate.
So really it doesn't matter at all whether or not your specific inline
asm compiles or not. When yours does, someone else's will fail.
You are not the only one who does not understand the halting problem.
The halting problem only says you can not prove every program will (or won't) halt - the general case.
It says nothing at all about any specific case, and there are plenty of programs where you can prove they halt.
And NP just means the solution does not scale well, not that it can't be done.
And NP-complete is something else entirely. It has to do with being able to interconvert all NP-complete problems (i.e. if you solve one you solve all).
There are many NP problems that are not NP-complete. Proving any particular problem is NP-complete, and not just NP is usually a big deal, so don't just throw that term around.
As for your explanation, yes, that's correct. The thing that I didn't get about the way they were sparring on the list was that if there is a solution the problem might not be solved in an elegant way, but with the number of variations involved you could simply brute force it.
Then if a solution exists you'll find it, if it doesn't exist then you can report that. After all, if you say you want to put 12 variables in to 7 registers and there is no possible way then there is no possible way. If there is such a possibility the number of permutations is small enough that it could be found anyway.
GCC contributes to the problem by having some overhead that reserves registers that could be saved on the stack for the duration of this part of the computation.
x = 7;
printf("%d %d %d",x++,++x,x++);
x = 7;
printf("%d %d %d",x++,x=2,x++);
Go ahead, try it, run it, and explain the output.