试图了解gcc选项-fomit-frame-pointer

我要求Google给我gcc选项的含义-fomit-frame-pointer ,它将我redirect到下面的语句。

 -fomit帧指针
        不要将帧指针保存在寄存器中 
需要一个。 这避免了保存,设置和恢复帧的说明 
指针; 它也使许多function提供额外的寄存器。 它 
也使一些机器上的debugging成为不可能。

根据我对每个函数的了解,一个激活logging将在进程内存堆栈中创build,以保留所有局部variables和更多信息。 我希望这个帧指针意味着一个函数的激活logging的地址。

在这种情况下,哪些types的函数不需要将帧指针保存在寄存器中? 如果我得到这个信息,我会尝试devise新的函数(如果可能的话),因为如果帧指针没有保存在寄存器中,一些指令在二进制中将被忽略。 这在function很多的应用程序中会显着提高性能。

大多数小function不需要帧指针 – 大function可能需要一个。

实际上,编译器如何pipe理如何使用堆栈以及堆栈中的内容(局部variables,传递给当前函数的参数以及为即将调用的函数准备的参数)。 我不认为很容易描述需要或不需要帧指针的函数[从技术上说,NO函数有一个帧指针 – 更多的情况是“如果编译器认为有必要减less其他代码的复杂性“]。

我不认为你应该“尝试使函数没有框架指针”作为你的编码策略的一部分 – 就像我所说的,简单的函数不需要它们,所以使用-fomit-frame-pointer,会得到一个寄存器分配器的寄存器,并保存1-3条指令进入/退出函数。 如果你的函数需要一个帧指针,那是因为编译器决定比使用帧指针更好的select。 没有框架指针的function不是一个目标,它的目标是使代码正确和快速地工作。

请注意,“没有framepointer”应该会有更好的性能,但是这不是一个能够带来巨大改进的灵丹妙药 – 特别是在x86-64上已经有了16个寄存器。 在32位x86上,由于它只有8个寄存器,其中一个是堆栈指针,占用另外一个作为帧指针,占用寄存器空间的25%。 把它改成12.5%是一个很大的改进。 当然,编译64位也会有很大的帮助。

这是关于英特尔平台上的BP / EBP / RBP注册。 该寄存器默认为堆栈段(不需要特殊的前缀来访问堆栈段)。

EBP是访问数据结构,variables和dynamic分配堆栈中工作空间的最佳select。 EBP通常用于访问堆栈上相对于堆栈上的固定点的元素,而不是相对于当前的TOS。 它通常标识为当前过程build立的当前栈帧的基地址。 当在偏移量计算中使用EBP作为基址寄存器时,在当前的堆栈段(即当前由SSselect的段)中自动计算偏移量。 由于不需要明确指定SS,所以在这种情况下的指令编码更有效率。 EBP也可以用于索引到可通过其他段寄存器寻址的段。

(来源 – http://css.csail.mit.edu/6.858/2017/readings/i386/s02_03.htm

由于在大多数32位平台上数据段和堆栈段是相同的,所以EBP / RBP与堆栈的关联不再是问题。 所以,在64位平台上:AMD在2003年推出的x86-64架构已经大大地降低了对64位模式分段的支持:CS,SS,DS和ES中的四个段寄存器被强制为0 。x86 32位和64位平台的这些情况基本上意味着可以在访问存储器的处理器指令中使用EBP / RBP寄存器,而不用任何前缀。

所以你写的编译器选项允许BP / EBP / RBP用于其他方式,例如保存一个局部variables。

通过“这避免了保存,设置和恢复帧指针的指令”是指在每个函数的input上避免以下代码:

 push ebp mov ebp, esp 

enter指令,这在Intel 80286和80386处理器上非常有用。

另外,在函数返回之前,使用下面的代码:

 mov esp, ebp pop ebp 

leave指示。

debugging工具可以扫描堆栈数据,并在查找call sites时使用这些推送的EBP寄存器数据,即按照分层调用的顺序显示函数名称和参数。

程序员可能会对堆栈帧有一个广义的问题(它是堆栈中的一个实体,它仅仅是一个函数调用,并且保留了返回地址,参数和局部variables),但是狭义上说 – 当stack frames是在编译器选项的上下文中提到。 从编译器的angular度来看,一个堆栈框架就是这个例程入口和出口代码 ,它将一个锚点推入堆栈 – 也可以用于debugging和exception处理。 debugging工具可以扫描堆栈数据,并使用这些锚点进行回溯,同时在堆栈中查找call sites ,即按照分层调用的顺序显示函数的名称。

这就是为什么了解程序员对于编译器选项来说栈帧是多么重要的原因 – 因为编译器可以控制是否生成这个代码。

在某些情况下,编译器可以省略堆栈帧(例程的入口和出口代码),variables将直接通过堆栈指针(SP / ESP / RSP)而不是方便的基址指针(BP / ESP / RSP)。 编译器为某些函数省略堆栈帧的条件可能不同,例如:(1)函数是叶函数(即不调用其他函数的terminal实体); (2)不使用例外; (3)堆栈上没有传出参数的例程; (4)该function没有参数。

省略堆栈帧(例程的入口和出口代码)可以使代码更小更快,但是也可能会对debugging器在堆栈中追溯数据并将其显示给编程人员的能力产生负面影响。 这些编译器选项决定了函数应该满足哪些条件,以便编译器使用堆栈帧入口和出口代码进行赋值。 例如,在下列情况下,编译器可能会有选项将这种进入和退出代码添加到函数中:(a)总是,(b)从不,(c)需要时(指定条件)。

从一般性回归到特殊性:如果您将使用-fomit-frame-pointer GCC编译器选项,则可以赢得例程的入口代码和退出代码,并获得一个额外的寄存器(除非它已经默认打开在这种情况下,您已经从使用EBP / RBP寄存器中获益,如果隐式地指定了这个选项,将不会获得额外的收益)。 但请注意,在16位和32位模式下,BP寄存器无法像AX那样访问8位部分(AL和AH)。

由于此选项除了允许编译器在优化中使用EBP作为通用寄存器外,还可以防止生成堆栈帧的退出和input代码,这会使debugging复杂化 – 这就是为什么GCC文档明确指出(exception强调粗体样式),使这个选项使一些机器上的debugging不可能

另请注意,与debugging或优化有关的其他编译器选项可隐式地将-fomit-frame-pointer选项打开或closures。

我没有在gcc.gnu.org上find任何官方信息,关于其他选项如何影响x86平台上的 -fomit-frame-pointerhttps://gcc.gnu.org/onlinedocs/gcc-3.4.4/gcc /Optimize-Options.html只声明以下内容:

-O还打开机器上的-fomit-frame-pointer,这样做不会干扰debugging。

所以从文档本身来看 ,如果在x86平台上只用一个-O选项进行编译,是否会打开-fomit-frame-pointer 。 这可能是经验性的testing,但在这种情况下,海湾合作委员会的开发人员没有承诺在未来不会改变这个选项的行为,恕不另行通知。

但是, Peter Cordes在评论中指出,x86-16平台和x86-32 / 64平台之间的-fomit-frame-pointer的默认设置是不同的。

这个选项 – -fomit-frame-pointer – 也与英特尔C ++编译器15.0相关 ,不仅适用于GCC:

对于Intel编译器,这个选项有一个别名/Oy

这是英特尔写到的:

这些选项决定在优化中EBP是否被用作通用寄存器。 选项-fomit-frame-pointer和/ Oy允许使用。 选项-fno-omit-frame-pointer和/ Oy-禁止它。

一些debugging器希望EBP被用作堆栈帧指针,除非这样,否则不能产生堆栈回溯。 -fno-omit-frame-pointer和/ Oy-选项指示编译器生成代码,该代码将EBP作为所有函数的堆栈帧指针进行维护和使用,以便debugging器仍然可以生成堆栈回溯,而无需执行以下操作:

对于-fno-omit-frame-pointer:使用-O0closures优化For / Oy-:closures/ O1,/ O2或/ O3优化当指定选项-fno-omit-frame-pointer时, O0或-g选项。 指定选项-O1,-O2或-O3时,将设置-fomit-frame-pointer选项。

/ Oy选项在您指定/ O1,/ O2或/ O3选项时设置。 选项/ Oy-在您指定/ Od选项时设置。

使用-fno-omit-frame-pointer或/ Oy-选项将可用通用寄存器的数量减less1,并且可能导致效率稍低的代码。

注意对于Linux *系统:GCC 3.2exception处理目前存在问题。 因此,英特尔编译器在为C ++安装GCC 3.2并且打开exception处理(默认设置)时会忽略此选项。

请注意,以上报价仅适用于英特尔C ++ 15编译器,不适用于GCC。