为什么 ret 会随着优化消失?

Why ret disappear with optimization?

int suma(int* array, int len)
{
    asm("    xor %eax, %eax           # resultado = 0   \n"
        "    xor %edx, %edx           # i = 0           \n"
        "1:  add (%rdi,%rdx,4), %eax  # res += array[i] \n"
        "    inc %edx                 # ++i             \n"
        "    cmp %edx,%esi            # i < len?        \n"
        "    jne 1b                   # repetir         \n"
//      "    ret                                        \n"
       );
}

int main()
{
    int v[100];
    return suma(v, 100);
}

为什么gcc在-O0suma()的末尾插入了ret,而我在-O3的时候却要自己加上?

来自gcc -v

gcc version 8.2.1 20181011 (Red Hat 8.2.1-4) (GCC) 

I assume 64bit..., array in rdi, len in esi.

您正在使用 inline asm,而不是在 __attribute__((naked,noinline)) 函数中,因此编译器可以在它想要的任何上下文中使用 inline asm 模板块。由于您未能使用任何 input/output 约束,并且您在不告知编译器的情况下破坏了寄存器,除非您禁用优化,否则它将完全中断。

为了回答主要问题,编译器简单地将 suma 内联到 main 中。它是隐含的 volatile (因为它是一个基本的 asm 语句)所以它没有被优化掉。

但是执行在非空函数 (suma) 的末尾下降,这是未定义的行为,因此现代 GCC 只是放弃并省略了 ret指令。它假定执行永远不会采用该路径(因为未定义的行为),并且不会为它生成代码。

如果在suma的末尾添加return 0;,那么main将以ret指令结束。

令人惊讶的是,gcc only gives one warning on the Godbolt compiler explorer with -O3 -Wall

<source>: In function 'int suma(int*, int)':
<source>:13:1: warning: no return statement in function returning non-void [-Wreturn-type]
 }
 ^

main 的 asm 输出结果是这样的,这当然是完全错误的,因为 RDI 是 argc;它从未为 int v[100] 保留 space 因为它在 C 源代码中未使用或没有做任何事情。

main:
            xor %eax, %eax           # resultado = 0   
    xor %edx, %edx           # i = 0           
1:  add (%rdi,%rdx,4), %eax  # res += array[i] 
    inc %edx                 # ++i             
    cmp %edx,%esi            # i < len?        
    jne 1b                   # repetir         

suma的末尾有一个return 0;,它和main以xorl %eax, %eax结束; ret,但当然 main 仍然完全损坏,因为内联 asm 不使用任何输入约束。