What happens to eax internally?
Inline asm isn't magic or special. C++ compilers already translate C++ into asm.
Your inline asm just gets included in with the compiler-generated asm. If you want to understand what's really happening, look at the asm output from the compiler to see how your code fits in.
i.e. the answer to this part of the question is that it depends on the compiler, the context, and the optimization options, so you should just look at the generated asm to see for yourself.
Your question uses MSVC-style inline asm, which saves/restores registers around inline asm (other than ebp, and of course esp). So I think your example could would always have no effect. MSVC-style has no syntax to communicate anything to the compiler about register usage, or for getting values in/out in registers instead of memory.
You sometimes see MSVC inline asm that leaves a value in eax at the end of an int function with no return statement, making the mostly-safe assumption under limited circumstances that the compiler won't do anything with eax between the end of the inline-asm and the end of the function.
You said in comments you'd like an answer for g++, which couldn't even compile your example, but whatever, I'll write one for you.
GNU C inline asm uses different syntax, which requires you to tell the compiler which registers are inputs (and not modified), and which are read-write or write-only. Also which registers are clobbered scratch regs.
It's up to the programmer to correctly describe the asm to the compiler using constraints, otherwise you will step on its toes. Using GNU C inline asm is like a dance, where you can potentially achieve good results, but only if you're not careful you'll step on the compiler's toes. (Also, usually the compiler can make good asm on its own, and inline asm is a very brittle way to optimize; one of the many major problems is that constant propagation after inlining isn't possible through inline asm.)
Anyway, let's try it with a working example of GNU C inline asm:
int foo_badasm(int n) {
int factorial = 1;
for (int i=1 ; i < n ; i++ ) {
// compile with -masm=intel, since I'm using Intel syntax here
asm volatile ("mov eax, %[x] # THIS LINE FROM INLINE ASM\n"
"# more lines\n"
// "xor eax,eax\n"
: // no outputs, making the volatile implicit even if we didn't specify it
: [x] "rmi" (factorial) // input can be reg, memory, or immediate
: // "eax" // uncomment this to tell the compiler we clobber eax, so our asm doesn't break step on the compiler's toes.
);
factorial *= i;
}
return factorial;
}
See the code with asm output on the Godbolt compiler explorer, and for the same function with no asm statement.
The inner loop compiles to this (gcc 6.1 -O3 -fverbose-asm -masm=intel, with -fno-tree-vectorize to keep it simple). You can also try it with clang.
.L10:
mov eax, eax # THIS LINE FROM INLINE ASM # <retval>
imul eax, edx # <retval>, i
add edx, 1 # i,
cmp edi, edx # n, i
jne .L10 #,
ret
As you can see, in this case the inline asm statement produced a no-op. (mov eax,eax truncates rax to 32 bits, zeroing the upper 32 bits, but they were already zero in this function.)
If we'd don't anything else, like zero the register, or mov from a different source, we would have broken the function. The asm generated by the compiler depends only on the constraints listed in the asm statement, not on the text of the code (unlike MSVC).
See the inline-assembly tag wiki for more info, especially this answer on the difference between MSVC and GNU C inline asm.
More importantly, read https://gcc.gnu.org/wiki/DontUseInlineAsm before actually using inline asm for anything.