GCC如何将1到100的求和运算优化为5050。

4

我使用 gcc -O3 -S a.c 编译了以下C程序:

#include <stdio.h>

int main() {
    int sum = 0;
    for (int i = 0; i <= 100; i++) {
        sum += i;
    }
    printf("%d", sum);
}

生成的汇编代码如下:
    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 15    sdk_version 10, 15, 4
    .globl  _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    leaq    L_.str(%rip), %rdi
    movl    $5050, %esi             ## imm = 0x13BA
    xorl    %eax, %eax
    callq   _printf
    xorl    %eax, %eax
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "%d"

.subsections_via_symbols

就像GCC运行代码并注意到循环次数被确定一样,GCC用结果5050替换了整个计算。

movl $5050, %esi

GCC如何进行这种优化?这种优化的学术名称是什么?


可能是以复杂的方式进行的“常量折叠”。 - the busybee
常量传播? - OznOg
1
似乎是一种专门针对算术级数的一次和二次幂求和进行优化的方法。例如,sum += 3*i*i + 2*i + 1; 可以得到优化,但添加三次幂 i*i*i 将运行实际循环。 - dxiv
2个回答

1
我试图找到特定的标志,但没有成功。只使用-O1,我能够重现您的结果,所以我手动添加了所有由-O1启用的标志。但是当我这样做时,无法重现结果。
正如文档中所述:

并非所有优化都直接受标志控制。此部分仅列出具有标志的优化。

因此,我认为这是由-O1添加的某些没有标志的东西完成的。
以下是我的尝试:
$ gcc -fauto-inc-dec  -fbranch-count-reg  -fcombine-stack-adjustments \
-fcompare-elim  -fcprop-registers  -fdce  -fdefer-pop  -fdelayed-branch  -fdse  \
-fforward-propagate  -fguess-branch-probability  -fif-conversion \
-fif-conversion2  -finline-functions-called-once  -fipa-profile  \
-fipa-pure-const  -fipa-reference  -fipa-reference-addressable \
-fmerge-constants  -fmove-loop-invariants  -fomit-frame-pointer \
-freorder-blocks  -fshrink-wrap  -fshrink-wrap-separate  -fsplit-wide-types  \
-fssa-backprop  -fssa-phiopt  -ftree-bit-ccp  -ftree-ccp  -ftree-ch \
-ftree-coalesce-vars  -ftree-copy-prop  -ftree-dce  -ftree-dominator-opts \
-ftree-dse  -ftree-forwprop  -ftree-fre  -ftree-phiprop  -ftree-pta \
-ftree-scev-cprop  -ftree-sink  -ftree-slsr  -ftree-sra  -ftree-ter \
-funit-at-a-time -S k.c 
cc1: warning: this target machine does not have delayed branches
$ grep "5050" k.s
$ gcc -O1 -S k.c
$ grep "5050" k.s
    movl    $5050, %esi
$ 

此外,-Og 不会优化计算,但是关闭所有选项的 -O1 仍然会进行优化,而 -Og 被记录为禁用这些选项。 - dxiv

0

不,即使使用-O1 -fno-unroll-loops -fno-forward-propagate仍然对其进行了优化,而-funroll-loops -fforward-propagate则没有。 - dxiv
https://gcc.gnu.org/wiki/FAQ#optimization-options 提到-O1不等同于单独的优化标志。请参阅 https://gcc.gnu.org/wiki/FAQ#optimization-flags 以了解由单独的优化级别启用的标志。 - yoonghm
没错,但评论的重点是 -funroll-loops-fforward-propagate 对此优化没有影响。该优化是使用 -O1 完成的,而且不管你提到的标志如何,如果没有 -O1,则不会进行优化,因此它们与 OP 的问题无关。 - dxiv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接