首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >理解GCC的alloca()对齐与看似漏掉的优化

理解GCC的alloca()对齐与看似漏掉的优化
EN

Stack Overflow用户
提问于 2018-09-26 20:40:02
回答 2查看 1.2K关注 0票数 3

考虑以下通过alloca()函数在堆栈上分配内存的玩具示例:

代码语言:javascript
复制
#include <alloca.h>

void foo() {
    volatile int *p = alloca(4);
    *p = 7;
}

使用gcc 8.2用-O3编译上述函数将得到以下汇编代码:

代码语言:javascript
复制
foo:
   pushq   %rbp
   movq    %rsp, %rbp
   subq    $16, %rsp
   leaq    15(%rsp), %rax
   andq    $-16, %rax
   movl    $7, (%rax)
   leave
   ret

老实说,我本以为会有一个更紧凑的汇编代码。

分配内存的16字节对齐

上述代码中的指令andq $-16, %rax导致rax包含(仅)16字节对齐地址( rsprsp + 15 (均含))。

这种对齐执行是我首先不明白的:为什么alloca()要将分配的内存与16字节的边界对齐?

可能错过了优化?

无论如何,让我们考虑一下,我们希望alloca()分配的内存是16字节对齐的。尽管如此,在上面的汇编代码中,请记住GCC假设堆栈在执行函数调用(即call foo)时将对齐到一个16字节的边界,如果我们在推动rbp寄存器之后注意rbp内部堆栈的状态:

代码语言:javascript
复制
Size          Stack          RSP mod 16      Description
-----------------------------------------------------------------------------------
        ------------------
        |       .        |
        |       .        | 
        |       .        |            
        ------------------........0          at "call foo" (stack 16-byte aligned)
8 bytes | return address |
        ------------------........8          at foo entry
8 bytes |   saved RBP    |
        ------------------........0  <-----  RSP is 16-byte aligned!!!

我认为,通过利用https://en.wikipedia.org/wiki/Red_zone_(computing) (即无需修改rsp)和rsp已经包含16字节对齐地址的事实,可以使用以下代码:

代码语言:javascript
复制
foo:
   pushq   %rbp
   movq    %rsp, %rbp
   movl    $7, -16(%rbp)
   leave
   ret

寄存器rbp中包含的地址是16字节对齐的,因此rbp - 16也将对齐到16字节的边界。

更好的是,可以优化新堆栈框架的创建,因为rsp没有被修改:

代码语言:javascript
复制
foo:
   movl    $7, -8(%rsp)
   ret

这只是一个漏掉的优化,还是我在这里遗漏了其他的东西?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-09-26 20:56:45

x86-64系统VLAs要求C99可变长度数组(VLAs)对齐16字节,对于>= 16字节的自动/静态数组也是如此。

看起来gcc正在把alloca当作一个VLA,并且没有对每个函数调用只运行一次的alloca进行常量传播。(或者它在内部使用alloca作为VLAs。)

如果运行时值大于128个字节,一般的alloca / VLA不能使用红色区域。GCC还使用RBP创建了一个堆栈框架,而不是保存分配大小并在稍后执行add rsp, rdx

,所以asm看起来就像函数、arg或其他运行时变量,而不是常量,这就是我得出这个结论的原因。

也是alignof(maxalign_t) == 16,但是allocamalloc可以满足这样的要求,即返回对小于16字节的对象没有16字节对齐的任何对象可用的内存。在x86-64 SysV中,没有一个标准类型的对齐要求比它们的大小更宽。

你说得对,它应该能把它优化成这样:

代码语言:javascript
复制
void foo() {
    alignas(16) int dummy[1];
    volatile int *p = dummy;   // alloca(4)
    *p = 7;
}

并将其编译到您建议的movl $7, -8(%rsp)ret中。

对于alloca来说,alignas(16)可能是可选的。

如果您真的需要gcc发出更好的代码,当常量传播使arg到alloca成为编译时常量时,您可以考虑首先使用VLA。GNU C++在C++模式下支持C99风格的VLAs,但ISO C++ (和MSVC)不支持。

或者可能使用if(__builtin_constant_p(size)) { VLA version } else { alloca version },但是VLA的作用域意味着您不能从if的作用域返回VLA,该范围检测到我们被编译时常量size内联。所以你必须复制需要指针的代码。

票数 4
EN

Stack Overflow用户

发布于 2018-09-26 22:12:30

这是gcc (部分)错过了优化。Clang按预期做了。

我之所以这样说,部分是因为如果你知道你要使用gcc,你可以使用内置的函数(用条件编译,让gcc和其他编译器有可移植的代码)。

__builtin_alloca_with_align是你的朋友;)

下面是一个示例(修改后的编译器不会将函数调用减少到单个ret):

代码语言:javascript
复制
#include <alloca.h>

volatile int* p;

void foo() 
{
    p = alloca(4) ;
    *p = 7;
}

void zoo() 
{
    // aligment is 16 bits, not bytes
    p = __builtin_alloca_with_align(4,16) ;
    *p = 7;
}

int main()
{
  foo();
  zoo();
}

反汇编代码(使用objdump -d -w --insn-width=12 -M intel)

Clang将生成以下代码(clang -O3 test.c) --这两个函数看起来都很相似

代码语言:javascript
复制
0000000000400480 <foo>:
  400480:       48 8d 44 24 f8                          lea    rax,[rsp-0x8]
  400485:       48 89 05 a4 0b 20 00                    mov    QWORD PTR [rip+0x200ba4],rax        # 601030 <p>
  40048c:       c7 44 24 f8 07 00 00 00                 mov    DWORD PTR [rsp-0x8],0x7
  400494:       c3                                      ret    

00000000004004a0 <zoo>:
  4004a0:       48 8d 44 24 fc                          lea    rax,[rsp-0x4]
  4004a5:       48 89 05 84 0b 20 00                    mov    QWORD PTR [rip+0x200b84],rax        # 601030 <p>
  4004ac:       c7 44 24 fc 07 00 00 00                 mov    DWORD PTR [rsp-0x4],0x7
  4004b4:       c3                                      ret    

GCC这个(gcc -g -O3 -fno-stack-protector)

代码语言:javascript
复制
0000000000000620 <foo>:
 620:   55                                      push   rbp
 621:   48 89 e5                                mov    rbp,rsp
 624:   48 83 ec 20                             sub    rsp,0x20
 628:   48 8d 44 24 0f                          lea    rax,[rsp+0xf]
 62d:   48 83 e0 f0                             and    rax,0xfffffffffffffff0
 631:   48 89 05 e0 09 20 00                    mov    QWORD PTR [rip+0x2009e0],rax        # 201018 <p>
 638:   c7 00 07 00 00 00                       mov    DWORD PTR [rax],0x7
 63e:   c9                                      leave  
 63f:   c3                                      ret    

0000000000000640 <zoo>:
 640:   48 8d 44 24 fc                          lea    rax,[rsp-0x4]
 645:   c7 44 24 fc 07 00 00 00                 mov    DWORD PTR [rsp-0x4],0x7
 64d:   48 89 05 c4 09 20 00                    mov    QWORD PTR [rip+0x2009c4],rax        # 201018 <p>
 654:   c3                                      ret    

正如您所看到的,动物园现在看起来像预期的,并且类似于clang代码。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52525744

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档