理解gcc -Og和-Os循环输出的直觉

问题描述

背景

我假设gcc会将while循环转换为do-while形式。 (请参见Why are loops always compiled into "do...while" style (tail jump)?

还有-O0用于while循环...

while (test-expr)
    body-statement

..将以跳到中间执行时

的形式生成代码
    goto test;
loop:
    body-statement
test:
    if (test-expr) goto loop;

并且gcc -O2将在守卫的同时执行

if (test-expr)
    goto done;
loop:
    body-statement
    if (test-expr) goto loop;
done:

具体示例

Here are godbolt examples of functions for which gcc generates the kind of control flow I'm describing above(我使用for循环,但while循环将提供相同的代码)。

这个简单的功能...

int sum1(int a[],size_t N) {
    int s = 0;
    for (size_t i = 0; i < N; i++) {
        s += a[i];
    }
    return s;
}

-O0生成跳转到中间代码

```sum1:
        push    rbp
        mov     rbp,rsp
        mov     QWORD PTR [rbp-24],rdi
        mov     QWORD PTR [rbp-32],rsi
        mov     DWORD PTR [rbp-4],0
        mov     QWORD PTR [rbp-16],0
        jmp     .L2
.L3:
        mov     rax,QWORD PTR [rbp-16]
        lea     rdx,[0+rax*4]
        mov     rax,QWORD PTR [rbp-24]
        add     rax,rdx
        mov     eax,DWORD PTR [rax]
        add     DWORD PTR [rbp-4],eax
        add     QWORD PTR [rbp-16],1
.L2:
        mov     rax,QWORD PTR [rbp-16]
        cmp     rax,QWORD PTR [rbp-32]
        jb      .L3
        mov     eax,DWORD PTR [rbp-4]
        pop     rbp
        ret

-O2会生成守卫代码

sum1:
        test    rsi,rsi
        je      .L4
        lea     rdx,[rdi+rsi*4]
        xor     eax,eax
.L3:
        add     eax,DWORD PTR [rdi]
        add     rdi,4
        cmp     rdi,rdx
        jne     .L3
        ret
.L4:
        xor     eax,eax
        ret

我的问题

我所追求的是在查看-Os循环时要应用的手工波浪规则。我更习惯查看-O2代码,现在我在-Os更流行的嵌入式领域工作,我对看到的循环形式感到惊讶。

似乎gcc -Og-Os都在底部jmp和顶部if() break生成代码。另一方面,Clang生成边做边做 A godbolt link to gcc and clang output

以下是上述功能的gcc -Os输出示例:

sum1:
        xor     eax,eax
        xor     r8d,r8d
.L2:
        cmp     rax,rsi
        je      .L5
        add     r8d,DWORD PTR [rdi+rax*4]
        inc     rax
        jmp     .L2
.L5:
        mov     eax,r8d
        ret
  1. 我是否可以假设gcc -Og-Os以我上面描述的形式生成代码
  2. 是否有人拥有描述-Og-Os使用while表单的原理的资源?是通过设计还是通过意外的形式优化传递的组织方式。
  3. 我认为将循环转换为do-while形式是编译器完成的早期规范化的一部分吗? gcc -O0如何生成do-while但gcc -Og给出while循环?规范化仅在启用优化后才会发生吗?

边注:由于没有太多不同的编译器标记,我惊讶于-Os和-O2生成代码有多少不同。也许很多通过检查了tradeoff_speed_vs_space的某个变量。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)