关于gcc编译的x86_64代码和C代码优化

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)

关于这个汇编代码,我有几个问题:

  1. " pushq %rbp"," movq %rsp, %rbp"和" popq %rbp" 的目的是什么,如果既没有rbp也没有rsp在函数体中使用?
  2. 为什么rsi并且rdi自动包含C函数的参数(if分别)而不从堆栈中读取它们?
  3. 我尝试将Foo的大小增加到88字节(11 long秒)并且leaq指令变成了imulq.将我的结构设计为具有"圆形"尺寸以避免乘法指令(为了优化阵列访问)是否有意义?该leaq指令被替换为:

    imulq   $88, %rsi, %rcx
    
    Run Code Online (Sandbox Code Playgroud)

Dan*_*zar 7

  1. 该函数只是使用这些指令构建自己的堆栈帧.他们并没有什么不寻常之处.但是,您应该注意,由于此函数的小尺寸,在代码中使用时可能会内联.但是,编译器始终需要生成函数的"正常"版本.另外,@ ouah在他的回答中说.

  2. 这是因为AMD64 ABI指定的参数应该传递给函数.

    如果类是INTEGER,则使用序列%rdi,%rsi,%rdx,%rcx,%r8和%r9的下一个可用寄存器.

    第20页,AMD64 ABI草案0.99.5 - 2010年9月3日

  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或其他任何东西都更有效.