为什么 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在-O0
的suma()
的末尾插入了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 不使用任何输入约束。
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在-O0
的suma()
的末尾插入了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 不使用任何输入约束。