【发布时间】:2021-08-01 07:15:47
【问题描述】:
我用-Os -march=haswell 调用Clang 12.0.0 来编译以下C 程序:
int bar(int);
int foo(int x) {
const int b = bar(x);
if (x || b) {
return 123;
}
return 456;
}
生成以下程序集:
foo: # @foo
push rbx
mov ebx, edi
call bar
or eax, ebx
mov ecx, 456
mov eax, 123
cmove eax, ecx
pop rbx
ret
https://gcc.godbolt.org/z/WsGoM56Ez
据我了解,foo 的调用者在 RAX/EAX 中设置了 x。 foo 然后调用 bar,这不需要修改 RAX/EAX,因为 x 作为未修改的输入传递。
or eax, ebx 指令似乎将输入 x 与 bar 的结果进行比较。这个结果如何在 EBX 中结束? mov ebx,edi 的作用是什么?
【问题讨论】:
-
x是通过edi传入的。ebx是一个被调用者保存的寄存器,mov ebx, edi用于在call bar中保存值。bar(x)的结果是eax中or eax, ebx的结果,x仍在ebx中。 -
or在按位或运算中组合了两个值。此后,目标寄存器在此处被覆盖。但是,算术状态标志仍由or设置。所以cmove(条件移动,如果相等)只有在or发现它的两个输入都等于0时才将新值写入它的目的地。 -
or eax, ebx正在实现x || b,它没有比较任何东西。 -
我认为所有这些误解都源于对调用约定在哪里通过
x的错误假设,从而使其重复。如果没有,我们可以重新打开或查找更多重复项。 Matt Godbolt 的 CppCon 演讲 (youtu.be/bSkpMdDe4g4) 可能会有所帮助。 (相关:How to remove "noise" from GCC/clang assembly output?)
标签: c assembly x86-64 calling-convention