x64 参数和返回值调用约定

Cha*_*son 4 c assembly x86-64 calling-convention

我调用 Clang 12.0.0 来-Os -march=haswell编译以下 C 程序:

int bar(int);

int foo(int x) {
  const int b = bar(x);
  if (x || b) {
      return 123;
  }
  return 456;
}
Run Code Online (Sandbox Code Playgroud)

生成以下程序集:

foo:                                    # @foo
        push    rbx
        mov     ebx, edi
        call    bar
        or      eax, ebx
        mov     ecx, 456
        mov     eax, 123
        cmove   eax, ecx
        pop     rbx
        ret
Run Code Online (Sandbox Code Playgroud)

https://gcc.godbolt.org/z/WsGoM56Ez

据我了解,foo 的调用者在 RAX/EAX 中设置了 x。然后 foo 调用 bar,这不需要修改 RAX/EAX,因为 x 是作为未修改的输入传递的。

or eax, ebx指令似乎是将输入 x 与 bar 的结果进行比较。该结果如何最终进入 EBX?有何目的mov ebx,edi

chq*_*lie 6

恐怕你误会了:

  • 根据x86-64 System V 调用约定,函数参数在 rdi 中传递。
  • 寄存器 rbx不得被函数修改;GCC 根据需要保存/恢复它,因此它可以x在调用bar.
  • 函数返回值以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
Run Code Online (Sandbox Code Playgroud)

编译器进行优化x || b(x | b) != 0允许生成无分支代码。

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