我编译了以下C代码:
typedef struct {
long x, y, z;
} Foo;
long Bar(Foo *f, long i)
{
return f[i].x + f[i].y + f[i].z;
}
使用命令
gcc -S -O3 test.c
。这是输出中的Bar函数: .section __TEXT,__text,regular,pure_instructions
.globl _Bar
.align 4, 0x90
_Bar:
Leh_func_begin1:
pushq %rbp
Ltmp0:
movq %rsp, %rbp
Ltmp1:
leaq (%rsi,%rsi,2), %rcx
movq 8(%rdi,%rcx,8), %rax
addq (%rdi,%rcx,8), %rax
addq 16(%rdi,%rcx,8), %rax
popq %rbp
ret
Leh_func_end1:
我对此汇编代码有一些疑问:
pushq %rbp
和movq %rsp, %rbp
,“popq %rbp
”,“rbp
”和“rsp
”的用途是什么? rsi
和rdi
自动包含C函数的参数(分别为i
和f
)而不从堆栈中读取它们? long
s),并且leaq
指令变成了imulq
。将结构设计为具有“更舍入”的大小以避免乘法指令(以优化数组访问)是否有意义? leaq
指令被替换为:imulq $88, %rsi, %rcx
最佳答案
第20页,AMD64 ABI草案0.99.5 – 2010年9月3日
f
是包含该结构的数组的地址,i
是必须访问该数组的索引,则每个结构的字节偏移量是i*24
。在这种情况下,乘以24是通过lea
和SIB寻址的组合来实现的。第一条lea
指令只计算i*3
,然后每条后续指令都使用该i*3
并将其进一步乘以8,因此以所需的绝对字节偏移量访问数组,然后使用立即位移来访问各个结构成员((%rdi,%rcx,8)
,8(%rdi,%rcx,8)
和16(%rdi,%rcx,8)
)。如果将结构的大小设置为88字节,则无法通过lea
和任何类型的地址的组合来快速地执行此操作。编译器只是假设简单的imull
在计算i*88
方面比一系列移位,添加,lea
或其他任何方式都更有效。 关于c - 关于gcc编译的x86_64代码和C代码优化,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10886823/