我编译了以下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 %rbpmovq %rsp, %rbp,“popq %rbp”,“rbp”和“rsp”的用途是什么?
  • 为什么rsirdi自动包含C函数的参数(分别为if)而不从堆栈中读取它们?
  • 我尝试将Foo的大小增加到88个字节(11 long s),并且leaq指令变成了imulq。将结构设计为具有“更舍入”的大小以避免乘法指令(以优化数组访问)是否有意义? leaq指令被替换为:
    imulq   $88, %rsi, %rcx
    
  • 最佳答案

  • 该函数只是使用这些指令构建自己的堆栈框架。他们真的没有什么不寻常的。但是,您应该注意,由于此函数的大小较小,因此在代码中使用时可能会内联。但是,始终要求编译器产生该功能的“正常”版本。另外,@ ouah在回答中说了什么。
  • 这是因为AMD64 ABI就是这样指定应将参数传递给函数的。



    第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方面比一系列移位,添加,lea或其他任何方式都更有效。
  • 关于c - 关于gcc编译的x86_64代码和C代码优化,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10886823/

    10-11 04:07
    查看更多