【问题标题】:Function Prologue and Epilogue removed by GCC OptimizationGCC 优化删除了函数序言和结语
【发布时间】:2013-03-11 21:38:06
【问题描述】:

取一个空程序

//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 compiler manual 中找到了有关该标志的更多信息。但是无法理解以下手册中给出的删除序言和尾声的原因。

不要将帧指针保存在寄存器中 需要一个。

有没有其他方法可以说明上述原因?

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

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

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

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

【问题讨论】:

标签: c optimization gcc assembly x86


【解决方案1】:

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

至于rep ret:

这是原则。处理器尝试获取接下来的几个 要执行的指令,以便它可以启动进程 解码和执行它们。它甚至通过跳跃和返回来做到这一点 指令,猜测程序下一步将走向何方。

AMD 在这里说的是,如果 ret 指令紧跟在 条件跳转指令,它们的预测器无法确定在哪里 ret 指令正在进行。预取必须停止,直到 ret 实际执行,然后才能开始查找 再次领先。

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

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

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

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

__declspec(naked)

【讨论】:

  • 现在我尝试了,在 main 中声明一个变量,并编写了一些操作它并使用 printf 的语句,现在我得到了一个序言,但没有结尾(没有弹出指令)
  • 我会冒险猜测 main() 函数根本不需要一个,因为它是路的尽头。尝试创建一个函数并在那里执行 printf()。
  • 仅仅因为您正在使用堆栈并不意味着您需要一个堆栈框架,现在您只需要一个框架,如果您使用 VLA 或_alloca。另外,应该注意的是,在这种情况下,正确的助记符是PAUSEREP 在技术上是一个前缀......
  • pauserep noprep ret 是不同的。
  • @BarathBushan 堆栈调整是通过从堆栈指针中加减来完成的,而不是通过将数据移入/移出内存(即昂贵,并且弹出数据只是为了丢弃它没有意义)。
猜你喜欢
  • 1970-01-01
  • 2021-06-10
  • 2022-01-15
  • 2013-01-23
  • 1970-01-01
  • 2017-12-24
  • 2010-12-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多