5

取一个空程序

//demo.c

int main(void)
{

}

以默认优化编译程序。

gcc -S  demo.c -o dasm.asm 

我得到的汇编输出为

//Removed labels and directive which are not relevant

main:

pushl   %ebp                  // prologue of main
movl    %esp, %ebp            // prologue of main
popl    %ebp                  // epilogue of main
ret

现在在 -O2 优化下编译程序。

gcc -O2 -S  demo.c -o dasm.asm 

我得到了优化的程序集

main:

rep
ret

在我最初的搜索中,我发现优化标志-fomit-frame-pointer负责删除序言和结尾。

我在gcc 编译器手册中找到了有关该标志的更多信息。但无法理解手册给出的以下原因,用于删除序言和结尾。

不要将帧指针保存在不需要的函数的寄存器中。

有没有其他办法,提出上述原因?

"rep"指令的原因是什么,出现在-02优化?

为什么 main 函数不需要堆栈帧初始化?

如果框架指针的设置不是在主函数中完成的,那么谁来做这个工作?

它是由操作系统完成的还是硬件的功能?

4

1 回答 1

5

编译器变得越来越聪明,它知道您不需要将堆栈帧指针存储在寄存器中,因为您放入 main() 函数的任何内容都不会使用堆栈。

至于rep ret:

这是原理。处理器尝试获取接下来要执行的几条指令,以便它可以开始解码和执行它们的过程。它甚至通过跳转和返回指令来做到这一点,猜测程序接下来会去哪里。

AMD 在这里说的是,如果 ret 指令紧跟在条件跳转指令之后,他们的预测器无法确定 ret 指令的去向。预取必须停止,直到 ret 实际执行,然后才能再次开始向前看。

“rep ret”技巧显然可以解决这个问题,并让预测器完成它的工作。“rep”对指令没有影响。

来源:某论坛,google一句找到。

需要注意的一点是,没有序言并不意味着没有堆栈,您仍然可以轻松地推送和弹出,只是复杂的堆栈操作会很困难。

没有序言/结尾的函数通常被称为函数。黑客非常喜欢使用它们,因为当你跳转到它们时它们不会污染堆栈,我必须承认我知道它们除了优化之外没有其他用途。在 Visual Studio 中,它是通过以下方式完成的:

__declspec(naked)
于 2013-03-22T05:43:36.820 回答