c-x64参数和返回值调用约定



我用-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的作用是什么?

恐怕你弄错了:

  • 根据x86-64 System V调用约定,函数参数在rdi中传递
  • 寄存器rbx不能被函数修改;GCC根据需要保存/恢复它,因此它可以在对bar的调用中保留x的副本
  • 函数返回值以rax为单位。(实际上是eax;32位int只使用下半部分(

您可以通过编译像int foo(int x){return x;}这样的函数来验证基础知识——您只会看到一个mov eax, edi

这是一个评论版本:

foo:                                    # @foo
push    rbx           # save register rbx
mov     ebx, edi      # save argument `x` in ebx
call    bar           # a = bar()  (in eax)
or      eax, ebx      # compute `x | a`, setting FLAGS
mov     ecx, 456      # prepare 456 for conditional move
mov     eax, 123      # eax = 123
cmove   eax, ecx      # if `(x | a) == 0` set eax to 456
pop     rbx           # restore register rbx
ret                   # return value is in eax

编译器将x || b优化为允许无分支代码生成的(x | b) != 0

注意,与大多数整数ALU指令不同,mov不修改FLAGS。

最新更新