eax 内部发生了什么?
内联汇编并不神奇或特殊。 C++ 编译器已经将 C++ 翻译成 asm。
您的内联 asm 刚刚包含在编译器生成的 asm 中。如果您想了解到底发生了什么,请查看编译器的 asm 输出,看看您的代码如何适应。
也就是说,这部分问题的答案是它取决于编译器、上下文和优化选项,因此您应该只查看生成的 asm 来亲自查看。
您的问题使用 MSVC 风格的内联 asm,它保存/恢复内联 asm 周围的寄存器(除了ebp
, 而且当然esp
)。所以我认为你的例子可能永远没有效果。 MSVC 风格没有任何语法可以向编译器传达有关寄存器使用情况的任何信息,或者在寄存器(而不是内存)中输入/输出值。
有时您会看到 MSVC 内联汇编在其中留下一个值eax
在一个结束时int
函数没有return
语句,在有限的情况下做出最安全的假设,即编译器不会在内联汇编末尾和函数末尾之间对 eax 执行任何操作。
您在评论中说您想要 g++ 的答案,它甚至无法编译您的示例,但无论如何,我会为您写一个。
GNU C 内联汇编 https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html使用不同的语法,这需要您告诉编译器哪些寄存器是输入(并且未修改),哪些寄存器是读写或只写的。还有哪些寄存器是被破坏的暂存寄存器。
程序员有责任使用约束向编译器正确描述汇编,否则你就会踩到它的脚趾。使用 GNU C 内联汇编就像一场舞蹈,你可以在其中can可能会取得好的结果,但只有当你不小心时,你才会踩到编译器的脚趾。 (此外,通常编译器可以自己生成良好的 asm,而内联 asm 是一种非常脆弱的优化方式;许多主要问题之一是内联后的常量传播不可能通过内联 asm 实现。)
无论如何,让我们尝试一下 GNU C 内联汇编的工作示例:
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;
}
请参阅带有 asm 输出的代码Godbolt 编译器浏览器 http://gcc.godbolt.org/#compilers:!((compiler:g6,options:'-xc+-fverbose-asm+-Wall+-Wextra+-O3+-march%3Dhaswell+-masm%3Dintel+-fno-tree-vectorize',source:'//+function+with+an+input+and+an+output,+so+it+doesn!'t+optimize+away+to+returning+a+constant%0Aint+foo(int+n)+%7B%0A++int+factorial+%3D+1%3B%0A++for+(int+i%3D1+%3B+i+%3C+n+%3B+i%2B%2B+)%0A++++factorial+*%3D+i%3B%0A++return+factorial%3B%0A%7D%0A%0A%0Aint+foo_badasm(int+n)+%7B%0A++int+factorial+%3D+1%3B%0A++for+(int+i%3D1+%3B+i+%3C+n+%3B+i%2B%2B+)+%7B%0A++++//+compile+with+-masm%3Dintel,+since+I!'m+using+Intel+syntax+here%0A++++asm+volatile+(%22mov+++eax,+%25%5Bx%5D+++%23+THIS+LINE+FROM+INLINE+ASM%5Cn%22%0A++++++++++++++++++%22%23+more+lines%5Cn%22%0A++++++++++++++++++//+%22xor++eax,eax%5Cn%22%0A++++++++:+//+no+outputs,+making+the+volatile+implicit+even+if+we+didn!'t+specify+it%0A++++++++:+%5Bx%5D+%22rmi%22+(factorial)%0A++++++++:+//+%22eax%22++//+uncomment+the+eax+clobber+to+make+this+function+work%0A++++)%3B%0A++++factorial+*%3D+i%3B%0A++%7D%0A++return+factorial%3B%0A%7D%0A')),filterAsm:(commentOnly:!t,directives:!t,intel:!t,labels:!t),version:3,并且对于相同的函数,没有asm
陈述。
内部循环编译为此(gcc 6.1-O3 -fverbose-asm -masm=intel
, with -fno-tree-vectorize
为了简单起见)。你也可以用 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
正如您所看到的,在这种情况下,内联 asm 语句产生了一个无操作。 (mov eax,eax
截断rax
到 32 位,将高 32 位清零,但在此函数中它们已经为零。)
如果我们不做任何其他事情,比如将寄存器归零,或者mov
如果来自不同的来源,我们就会破坏该功能。编译器生成的asm取决于only中列出的约束条件asm
声明,而不是代码文本(与 MSVC 不同)。
See the 内联汇编 /questions/tagged/inline-assembly标记维基以获取更多信息,特别是这个关于 MSVC 和 GNU C 内联汇编之间差异的答案 https://stackoverflow.com/questions/3323445/what-is-the-difference-between-asm-and-asm/35959859#35959859.
更重要的是,阅读https://gcc.gnu.org/wiki/DontUseInlineAsm https://gcc.gnu.org/wiki/DontUseInlineAsm在实际使用内联汇编之前。