gcc如何选择从-fverbose-asm中编号临时变量?

问题描述 投票:0回答:1

具有这个简单的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 -fverbose-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()函数在stack frame中分配大小字节的空间 来电者。

assembly gcc compiler-construction x86-64
1个回答
0
投票

当变量中的大多数是立即数时,如何具有中间表示形式的变量?

在程序逻辑的SSA (Static Single Assignment)内部表示形式中(例如GCC的GIMPLE),每个临时值都有一个单独的名称。我假设没有直接关联的C变量名称时,数字来自自动编号的SSA变量。但是我对GCC内部并不十分熟悉,无法提供更多详细信息。如果您真的很好奇,可以随时亲自浏览GCC源代码。但是我相当有信心,自动编号的SSA变量可以解释这一点,并且很有意义。

数字文字实际上并没有以-fverbose-asm命名。例如在优化的GCC输出(from Godbolt)中,我们将其视为将args放入寄存器的一部分:

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

re:alloca:在将分配大小四舍五入到16的倍数后,它最终用subq %rax, %rsp抵消RSP。此舍入保持堆栈对齐。 (请至少尝试自己动手搜索它。当您缺少许多背景知识和概念时,您将无法期望答案能从头开始全面解释所有内容。当您不了解某些事物的细节时,请从头开始。通过搜索已习惯的技术术语。)

顺便说一句,从gcc -O0开始,这是效率极低的组件!

将分配大小四舍五入为16的倍数

似乎使用了x / 16 * 16而不是x & 0xFFFF...F0。 (如果您单步调试器,则可以看到divimul的顺序。)我猜内置函数的固定逻辑顺序是出于某种原因而写的,并且在-O0时,GCC并没有通过它进行恒定的传播。但是无论如何,这就是为什么它使用RAX。

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

要查看不可怕的汇编,请查看启用优化后会发生什么情况,例如在Godbolt上。

另请参见How to remove "noise" from GCC/clang assembly output?。然后,它仅执行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 #
Silly编译器,此时静态地知道%rsp的对齐方式,不需要(x+15) & -16。请注意,-16 = 0xFFFFFFFFFFFFFFF0用64位2的补码表示,因此这是一种表达AND掩码以清除一些低位的简便方法。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.