【发布时间】:2019-12-26 23:37:17
【问题描述】:
我正在尝试理解 System V AMD64 - ABI 从函数按值返回的含义。
对于以下数据类型
struct Vec3{
double x, y, z;
};
Vec3 类型属于 MEMORY 类,因此 ABI 关于“返回值”指定以下内容:
如果该类型具有 MEMORY 类,则调用者为返回值提供空间,并在 %rdi 中传递此存储的地址,就好像 它是函数的第一个参数。实际上,这个地址 成为“隐藏”的第一个参数。此存储不得重叠任何 通过除此参数之外的其他名称对被调用者可见的数据。
开启 return %rax 将包含由 %rdi 中的调用者。
考虑到这一点,以下(愚蠢的)功能:
struct Vec3 create(void);
struct Vec3 use(){
return create();
}
可以编译为:
use_v2:
jmp create
在我看来,可以执行尾调用优化,正如 ABI 向我们保证的那样,create 将放入 %rdi 传递的值到 %rax 寄存器中。
但是,似乎没有任何编译器(gcc、clang、icc)执行此优化(此处为on godbolt)。生成的汇编代码将 %rdi 保存在堆栈上,只是为了能够将其值移动到 %rax,例如 gcc:
use:
pushq %r12
movq %rdi, %r12
call create
movq %r12, %rax
popq %r12
ret
对于这个最小的、愚蠢的函数和现实生活中更复杂的函数,都执行了尾调用优化。这让我相信,我一定是错过了什么,这是禁止的。
不用说,对于 SSE 类的类型(例如只有 2 个而不是 3 个双精度),执行尾调用优化(至少通过 gcc 和 clang,live on godbolt):
struct Vec2{
double x, y;
};
struct Vec2 create(void);
struct Vec2 use(){
return create();
}
结果
use:
jmp create
【问题讨论】:
-
至少他们这么想,gcc的优化转储显示“
= create(); [return slot optimization] [tail call]”。
标签: c gcc optimization x86-64 calling-convention