【问题标题】:How is done compile-time calculation/math operation in gcc?如何在 gcc 中完成编译时计算/数学运算?
【发布时间】:2020-06-13 10:36:58
【问题描述】:

如果我用-O2 优化编译一个程序,那么任何带有数学运算的函数都会“内联”完成,而在汇编enter link description here 中没有物理cpu 指令。所以在c

#include <stdio.h>
#include <stdlib.h>

int f(int a, int b, int c){
    return a*b+c;
}
int main(){
    printf("%i\n",f(1,2,4));
    return 0;
}

并编译为cc -O2 -fverbose-asm -S a.c,气体输出:

.text
    .p2align 4,,15
    .globl  f
    .type   f, @function
f:
.LFB22:
# a.c:5:    return a*b+c;
    imull   %esi, %edi  # b, tmp93
# a.c:5:    return a*b+c;
    leal    (%rdi,%rdx), %eax   #, tmp92
# a.c:6: }
    ret 
.LFE22:
    .size   f, .-f
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%i\n"
    .section    .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB23:
    subq    $8, %rsp    #,
# a.c:8:    printf("%i\n",f(1,2,4));
    movl    $6, %esi    # HERE, I can change the value and gcc will not even notice
    leaq    .LC0(%rip), %rdi    #,
    xorl    %eax, %eax  #
    call    printf@PLT  #
# a.c:10: }
    xorl    %eax, %eax  #
    addq    $8, %rsp    # 
    ret 
.LFE23:
    .size   main, .-main
    .ident  "GCC: (Debian 8.3.0-6) 8.3.0"
    .section    .note.GNU-stack,"",@progbits

您可以看到,f 函数甚至不会从 main 调用。所以这个函数被简单地避免了,它的返回值在编译时以某种方式计算到一些指令周期。但是在compile-time中,当编译器不直接对CPU进行操作时,数学计算是如何完成的,而只是汇编呢?还是编译时编译器在编译为 asm 之前执行一些 cpu 任务?那么为什么还需要asm呢,编译器可以直接翻译成opcode并执行程序而无需汇编器的“一步一步”?

【问题讨论】:

  • 编译器除了在你的 CPU 上还能在哪里运行? Calculator.exe 或 Excel 可以,为什么它不能做数学?
  • @SamiKuhmonen 如果您在不进行任何优化的情况下进行编译,那么将有一个用于该计算的 asm 生成源。 asm 是一对一映射到 CPU 的操作码生成。编译器不是。因此,如果编译器通过“内联”绕过 asm 生成,则必须有一些编译器间指令代替 asm 来完成这项工作。所以我想知道那个跨编译器的源代码。而且有趣的是,只有在-O2 上它才会执行,在较少或没有优化的情况下,它通常会生成到 asm(如前所述),所以这个跨编译器指令并不总是执行
  • Assembly 并不总是一对一地映射到操作码。不需要任何“跨编译器”的东西。如果您要求,编译器会对其进行优化。编译器已经把你写的代码转换成二进制了,为什么认为它也可以转换常量这么奇怪呢?它知道 2*4 是 8,所以它只能说 8。是的,只有在要求优化时才会进行优化。编译器做用户没有要求它做的事情是不好的。
  • 汇编怎么知道 2*4 是 8? Excel 怎么知道是这样的?它不会在任何地方进行任何组装。它计算它,就像编译器一样。在输出操作码之前无需生成汇编。汇编只是人类可读的形式,编译器不需要它。我的意思是编译器已经接受了你编写的所有代码,并且知道如何基于它为多个处理器输出操作码,但它可以计算东西很奇怪?除非被询问,否则不进行优化的原因是调试,例如。
  • Gcc 确实会生成汇编代码,但其他人不会。例如,clang 直接生成机器码,只有在你明确请求时才输出汇编代码。但这不是重点:像常量折叠和内联这样的优化在生成任何程序集或机器代码之前很久就发生在中间表示上。这种中间表示是编译器内部的,通常不会暴露给用户。

标签: assembly gcc x86-64


【解决方案1】:

编译器是一个程序,它只是执行所需的计算。它不会模拟 CPU 来执行此操作,并且此步骤(称为常量折叠)在生成任何指令之前很久就发生了。

【讨论】:

  • 那为什么我可以看到 without 优化一个物理 asm 指令来执行计算,并且通过优化,它甚至没有调用 f 函数就完成了“内联” ,所以最后,f 函数是多余的。你提到的constant folding做了什么?为什么会在 -O2 而不是 -O1 中调用它(没有优化)?
  • @autistic456 常量折叠是一种编译器优化,编译器只计算涉及常量的表达式,而不是为它们生成代码。在您的代码中,这只有在f内联 后才有可能,因此您只能在启用内联时观察效果。这发生在-O2 或更好,但不是-O1
  • 请注意,-O1 不是“没有优化”(即-O0)。相反,它是“优化级别 1”,即“少量优化”。请参阅手册了解不同的优化级别及其效果。
  • 那么还有另一个概念——inlining。它是如何在 gcc 中完成的,为什么它只在 -O2 或更高版本上执行?而且 - compiler evaluates expressions - 仍然,它是如何完成的,没有用于算术运算的 cpu-opcode?这仅意味着,编译器在 cpu 上执行一些“指令间”调用,而不生成 asm。所以 gcc 内部调用绕过 asm 并做一些自己的工作。这怎么可能?
  • 将您提到的编译器inline-ing 解释为您的答案就足够了,因为这可以解释编译器在优化中做了什么
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-04
  • 2019-04-10
  • 2013-05-30
相关资源
最近更新 更多