Mat*_*att 5 c optimization x86 gcc x86-64
我编译了以下C代码:
typedef struct {
long x, y, z;
} Foo;
long Bar(Foo *f, long i)
{
return f[i].x + f[i].y + f[i].z;
}
Run Code Online (Sandbox Code Playgroud)
用命令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:
Run Code Online (Sandbox Code Playgroud)
关于这个汇编代码,我有几个问题:
pushq %rbp"," movq %rsp, %rbp"和" popq %rbp" 的目的是什么,如果既没有rbp也没有rsp在函数体中使用?rsi并且rdi自动包含C函数的参数(i和f分别)而不从堆栈中读取它们?我尝试将Foo的大小增加到88字节(11 long秒)并且leaq指令变成了imulq.将我的结构设计为具有"圆形"尺寸以避免乘法指令(为了优化阵列访问)是否有意义?该leaq指令被替换为:
imulq $88, %rsi, %rcx
Run Code Online (Sandbox Code Playgroud)该函数只是使用这些指令构建自己的堆栈帧.他们并没有什么不寻常之处.但是,您应该注意,由于此函数的小尺寸,在代码中使用时可能会内联.但是,编译器始终需要生成函数的"正常"版本.另外,@ ouah在他的回答中说.
这是因为AMD64 ABI指定的参数应该传递给函数.
如果类是INTEGER,则使用序列%rdi,%rsi,%rdx,%rcx,%r8和%r9的下一个可用寄存器.
第20页,AMD64 ABI草案0.99.5 - 2010年9月3日
这与结构大小没有直接关系,而是与函数必须访问的绝对地址有关.如果结构的大小是24个字节,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比一系列的shift,adds,leas或其他任何东西都更有效.