为什么空函数不仅仅是返回

时间:2013-08-04 23:29:02

标签: gcc assembly compilation x86

如果我编译一个空的C函数

void nothing(void)
{
}

在MacOS上使用gcc -O2 -S(和clang),它会生成:

_nothing:
    pushq   %rbp
    movq    %rsp, %rbp
    popq    %rbp
    ret

为什么gcc除了ret之外没有删除所有内容?这似乎是一个简单的优化,除非它真的做了一些事情(似乎不是,对我来说)。此模式(在开头按下/移动,在结尾处弹出)在其他非空函数中也可见,其中rbp未被使用。

在Linux上使用更新的gcc(4.4.5)我只看到了

nothing:
    rep
    ret

为什么rep?非空函数中不存在rep

4 个答案:

答案 0 :(得分:3)

  

为什么代表?

原因解释in this blog post。简而言之,直接跳转到单字节ret指令会破坏某些AMD处理器上的分支预测。而不是在nop之前添加ret,而是添加了一个无意义的前缀字节以保存指令解码带宽。

  

非空函数中没有代表。

引用我链接到的博客文章:“[rep ret]优先于简单的ret,当它是任何类型的分支的目标时,条件({{ 1}})或无条件(jne/je/...)“
在空函数的情况下,jmp/call/...将成为ret的直接目标。在非空函数中,它不会是。

  

为什么gcc除了ret之外不会删除所有内容?

即使您已指定call,某些编译器也可能不会省略帧指针代码。至少使用gcc,您可以使用-O2选项明确告诉编译器省略它们。

答案 1 :(得分:2)

正如这里所解释的:http://support.amd.com/us/Processor_TechDocs/25112.PDF,使用了一个双字节的近返回指令(即rep ret),因为在某些情况下,某些amd64处理器上的某些单字节返回值可能会被错误预测就像这个。

如果您使用gcc定位的处理器,您可能会发现可以生成单字节ret-mtune=nocona为我工作。

答案 2 :(得分:1)

我怀疑很早,你的最后一个代码是一个错误。正如johnfound所说。第一个代码是因为所有C编译器必须始终遵循函数意味着的_cdecl调用约定(在英特尔,对不起,我不知道AT& T语法):

功能定义

_functionA:
push   rbp
mov    rbp, rsp
;Some function
pop    rbp
ret

来电者:

call _functionA
sub esp, 0 ; Maybe if it zero, some compiler can strip it

为什么GCC总是遵循_cdecl调用约定而不遵循那个是废话,那就是编译器并不比高级汇编程序员更聪明。所以,它总是不惜一切代价遵循_cdecl。

答案 3 :(得分:-3)

也就是说,因为即使是所谓的“优化编译器”也太笨了,无法生成总是很好的机器代码。

他们无法生成比创作者生成更好的代码。

只要一个空函数是无意义的,他们可能根本就没有费心去优化它甚至检测这种非常特殊的情况。

虽然,单个“rep”前缀可能是一个bug。在没有字符串指令的情况下使用它什么都不做,但无论如何,在一些较新的CPU中它理论上可能会导致异常。 (恕我直言)