【问题标题】:register allocation --- how to utilize and spill the caller saved registers寄存器分配 --- 如何利用和溢出调用者保存的寄存器
【发布时间】:2020-10-01 22:50:31
【问题描述】:

我了解到,如果被调用者使用了任何caller saved registers (rax rdx rcx rsi rdi r8 r9 r10 r11),那么它必须在调用者的call 指令之前保存并在调用者之后恢复。

通过下面的例子,

int read();
void print(int i);

int main()
{
    int a = read();
    int b = read();
    int c = read();
    int d = read();
    int e = read();
    int f = read();
    int g = read();
    print(a);
    print(b);
    print(c);
    print(d);
    print(e);
    print(f);
    print(g);
}

注意

  1. 变量a - g 应该使用所有callee saved registers (rbp rsp rbx r12 r13 r14 r15)。而且我们不能同时使用rbprsp,因为它们都必须用于寻址堆栈内存。

  2. readprint 来自某个外部编译单元。因此,当我们编译当前编译单元时,特别是在为main 函数分配寄存器期间,我们并不真正了解它们的调用方保存寄存器的使用情况。

godbolt-O3 中,它编译为以下内容

main:
  pushq %r15
  pushq %r14
  pushq %r13
  pushq %r12
  pushq %rbp
  pushq %rbx
  subq $24, %rsp # spill here
  call read()
  movl %eax, 12(%rsp) # spill here
  call read()
  movl %eax, %ebx
  call read()
  movl %eax, %r15d
  call read()
  movl %eax, %r14d
  call read()
  movl %eax, %r13d
  call read()
  movl %eax, %r12d
  call read()
  movl 12(%rsp), %edi
  movl %eax, %ebp
  call print(int)
  movl %ebx, %edi
  call print(int)
  movl %r15d, %edi
  call print(int)
  movl %r14d, %edi
  call print(int)
  movl %r13d, %edi
  call print(int)
  movl %r12d, %edi
  call print(int)
  movl %ebp, %edi
  call print(int)
  addq $24, %rsp
  xorl %eax, %eax
  popq %rbx
  popq %rbp
  popq %r12
  popq %r13
  popq %r14
  popq %r15
  ret

注意

  1. 变量a 溢出到12(%rsp)

  2. 我们不需要泄漏任何caller saved registers,因为它们根本没有被使用,结果证明在这里更有效。

我的问题

  1. 看起来,如果我们不使用 caller saved registers,我们真的不需要处理它们。因此,我们什么时候应该使用caller saved registers

  2. 对于像readprint这样的被调用者,因为我们不知道他们的寄存器使用情况,我们应该如何为caller saved registers做溢出?

谢谢

【问题讨论】:

    标签: x86 compiler-construction x86-64 calling-convention register-allocation


    【解决方案1】:

    看起来令人困惑且不直观的“调用者保存/被调用者保存”术语误导您认为每个寄存器都应该始终由某个地方的某个人保存。请参阅What are callee and caller saved registers? - “呼叫保留”与“呼叫破坏”在易于记忆和作为心理模型方面更有用。让值被销毁是正常的,就像函数 arg 一样。

    如果我们不使用调用者保存的寄存器,看起来我们真的不需要处理溢出调用者保存的寄存器。

    请注意,您的函数确实使用了几个调用破坏(“调用者保存”)寄存器:它使用 RDI 将 arg 传递给 print(int),并将 RAX 归零作为 main 的返回值。

    如果它在调用破坏寄存器中有一个值需要在函数调用中继续存在,GCC 选择mov 将该值保存到调用保留寄存器。例如read()返回时,其返回值在EAX中,下次调用时会销毁。因此它使用mov %eax, %ebp 或其他任何方式将其保存到保留调用的寄存器中,或者将一个溢出到12(%rsp)

    (请注意,GCC 使用 push/pop 来保存/恢复它使用的调用保留寄存器的调用者值。)

    GCC 的默认代码生成策略是保存/恢复调用保留寄存器以在调用之间保存值,而不是溢出到此函数内部的内存中。对于不那么琐碎的情况,这通常是一件好事,尤其是对于循环内的调用。有关详细信息,请参阅Why do compilers insist on using a callee-saved register here?

    我们不能同时使用 rbp 或 rsp,因为它们都必须用于寻址堆栈内存。

    同样错误:使用-fomit-frame-pointer(在大多数优化级别上),RBP 只是另一个保留调用的寄存器。您的函数使用它来保存 read 返回值。 (EBP 是 RBP 的低 32 位)。

    【讨论】:

    • 感谢您的回答。我承认我被命名约定误导了一点。根据您的解释,call clobbered registers 应仅用于在调用中保存临时值,而 call preserved registers 用于保存变量之类的东西。
    • 一些后续行动。 1. 因此,在进行寄存器分配时,即将变量映射到寄存器时,我们应该只使用call preserved registers? 2.当我们从另一个编译单元调用程序时,我相信我们不需要关心保存/恢复调用破坏的寄存器。 3、如果是这样,那么我们在设计架构的时候,自然应该选择更多的call preserved registers来获得更多可能的变量数?
    • @Lin:去阅读What are callee and caller saved registers? - 我在那里提供了更多的例子和细节。 1. 不,在叶函数中,首先使用调用破坏寄存器,希望不需要保存/恢复任何内容或分配任何堆栈空间。或者,如果您有一个仅在两个函数调用之间 存在的变量,例如不包含任何调用的循环的循环计数器,请为其使用调用破坏寄存器。
    • 3.不,呼叫破坏寄存器也很有价值。而且这不是 ISA 的功能;设计调用约定与硬件是分开的。比如x86-64有两个主要的,x86-64 System V和Windows x64。请参阅Why does Windows64 use a different calling convention from all other OSes on x86-64? 获取一些指向邮件列表存档消息的链接,了解它是如何/为什么这样设计的。另请参阅Why not store function parameters in XMM vector registers?,了解有关 volatile / non-volatile regs 之间权衡的更多信息
    【解决方案2】:

    我了解到,如果调用者保存的任何寄存器 (rax rdx rcx rsi rdi r8 r9 r10 r11) 被调用者使用,那么它必须在调用者的调用指令之前保存并在调用指令之后恢复。

    应该是

    我了解到,如果调用者使用了调用者保存的任何寄存器 (rax rdx rcx rsi rdi r8 r9 r10 r11),那么它必须在调用者的调用指令之前保存并在调用指令之后恢复。

    调用者保存寄存器是那些可能被任何调用函数破坏的寄存器。您不确定是否有任何给定的被调用者使用它们,因此您必须假设最坏的情况。但是,如果调用者正在使用它们,调用者只需要保存它们。如果你不是,你不在乎他们可能会被破坏。

    【讨论】:

    • 好吧,您不必保存和恢复寄存器本身;仅当它包含您需要保留的值时。理想情况下,您可以设计编译器,使需要在函数调用中保留的值永远不会放在此类寄存器中,而是放在被调用者保存的寄存器或内存中。
    猜你喜欢
    • 2019-08-18
    • 2012-03-05
    • 2010-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-27
    • 1970-01-01
    相关资源
    最近更新 更多