关于C#:为什么ret会随着优化而消失?

Why ret disappear with optimization?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int suma(int* array, int len)
{
    asm("    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        
"

//     "    ret                                        
"
       );
}

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

为什么GCC在-O0suma()的末尾插入ret,但我必须自己添加到-O3上?

来自gcc -v

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


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

您使用的是内联asm,而不是__attribute__((naked,noinline))函数,因此编译器可以在任何上下文中使用内联asm模板块。由于您未能使用任何输入/输出约束,并且在不通知编译器的情况下删除寄存器,除非禁用优化,否则它将完全中断。

为了回答这个主要问题,编译器简单地将suma插入到main中。它是隐式的volatile(因为它是一个基本的asm语句),所以它不会被优化掉。

但是执行是从一个非空函数(suma的末尾掉下来的,这是一个未定义的行为,所以现代的gcc只是放弃并省略了ret指令。它假定执行永远不能采用该路径(因为未定义的行为),并且不麻烦为它生成代码。

如果在EDOCX1 OR 1的结尾添加EDCOX1×6,那么EDCOX1×2将以EDCOX1×5指令结束。

令人惊讶的是,gcc在godbolt编译器资源管理器上只给出了一个带有-o3-墙的警告:

1
2
3
4
<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]保留空间,因为它在c源中未使用或做了任何事。

1
2
3
4
5
6
7
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;,在xorl %eax, %eax的末尾有一个return 0;;在ret的末尾有一个return 0;,但当然,main仍然是完全中断的,因为内联asm不使用任何输入约束。