提问者:小点点

gcc如何选择从-feverose-asm对临时变量进行编号?


拥有这个简单的c:

#define _XOPEN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <alloca.h>

int main(){
    char *buf = alloca(600);
    snprintf(buf,600,"hi!, %d, %d, %d\n", 1,2,3);
    puts(buf);
}

$cc-S-fverose-asm a. c
上生成。

text
    .section    .rodata
.LC0:
    .string "hi!, %d, %d, %d\n"
    .text
    .globl  main
    .type   main, @function
main:
    pushq   %rbp    #
    movq    %rsp, %rbp  #,
    subq    $16, %rsp   #,
# a.c:7:    char *buf = alloca(600);
    movl    $16, %eax   #, tmp102
    subq    $1, %rax    #, tmp89
    addq    $608, %rax  #, tmp90
    movl    $16, %ecx   #, tmp103
    movl    $0, %edx    #, tmp93
    divq    %rcx    # tmp103
    imulq   $16, %rax, %rax #, tmp92, tmp94
    subq    %rax, %rsp  # tmp94,
    movq    %rsp, %rax  #, tmp95
    addq    $15, %rax   #, tmp96
    shrq    $4, %rax    #, tmp97
    salq    $4, %rax    #, tmp98
    movq    %rax, -8(%rbp)  # tmp98, buf
# a.c:8:    snprintf(buf,600,"hi!, %d, %d, %d\n", 1,2,3);
   ...

gcc决定对这些临时变量进行编号?(tmp102、tmp89、tmp90、…)?

另外,有人能解释一下,为什么alloca使用%raxaddq 608美元,%rax)来分配内存,而不是%rspsubq 608美元,%rsp)?这就是alloca的用途(根据手册页):alloca()函数在调用者的堆栈框架中分配大小字节的空间。


共1个答案

匿名用户

当大多数变量是直接的时,如何让变量具有中间表示?

在程序逻辑的SSA(静态单一赋值)内部表示(如GCC的GIMPLE)中,每个临时值都有一个单独的名称。当没有直接关联的C变量名时,我会假设这些数字来自自动编号的SSA变量。但是我对GCC内部结构不够熟悉,无法提供更多细节。如果你真的很好奇,你可以自己查看GCC源代码。但是我相当有信心自动编号的SSA变量解释了这一点,并且完全有意义。

数字字面量实际上并没有得到任何名称,例如在优化的GCC输出中(来自戈德博尔特),我们认为这是将参数放入寄存器的一部分:

...
        movl    $3, %r9d        #,
        movl    $2, %r8d        #,
        xorl    %eax, %eax      #
...

re: alloca:在将分配大小四舍五入到16的倍数后,它最终会使用subq%rax,%rsp抵消RSP。

这种舍入保持了堆栈对齐。(请至少尝试自己谷歌一下。当你缺少很多背景知识和概念时,你不能指望答案从头开始完全解释一切。当你不理解某件事的细节时,从搜索被使用的技术术语开始。)

BTW,gcc-O0的asm非常低效!它似乎使用x/16*16而不是x

我猜内置函数的预设逻辑序列是这样写的,出于某种原因,在-O0GCC没有通过它进行恒定传播。但无论如何,这就是它使用RAX的原因。

也许alloca逻辑是用GIMPLE编写的,或者可能RTL代码,直到一些转换通过后才会扩展。这就解释了为什么它优化得如此糟糕,即使它都是单个语句的一部分。gcc-O0对性能非常不利,但是一个64位的div除以16是非常糟糕的,相比之下,一个非常便宜的带有立即操作数的。在asm中看到乘以2的幂作为立即操作数也是非常奇怪的;在正常情况下,编译器会将其优化为shift。

要查看非可怕的asm,请查看启用优化后会发生什么,例如在Goldbolt上。另请参阅如何从GCC/clang程序集输出中去除“噪音”?。然后它只执行sub 616美元,%rsp。但是它会在运行时浪费将指针对齐到该空间的指令(以保证空间将是16字节对齐的),即使在此之后RSP的对齐是静态已知的。

# GCC10.1 -O3 -fverbose-asm with alloca
...
        subq    $616, %rsp           # reserve 600 + 16 bytes
        leaq    15(%rsp), %r12
        andq    $-16, %r12           # get a 16-byte aligned pointer into it
        movq    %r12, %rdi           # save the pointer for later instead of recalc before next call
        call    snprintf        #

愚蠢的编译器,此时%rsp的对齐方式是静态已知的,没有(x 15)

删除alloca并使用普通本地数组提供了更简单的代码:

# GCC10.1 -O3 with char buf[600]
        subq    $616, %rsp
...
        movq    %rsp, %rdi
...
        call    snprintf        #

相关问题