【发布时间】:2020-01-27 14:56:51
【问题描述】:
我正在调查本地二进制文件的一些问题。我注意到 g++ 创建了很多对我来说似乎没有必要的 ASM 输出。以-O0 为例:
Derived::Derived():
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp <--- just need 8 bytes for the movq to -8(%rbp), why -16?
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, %rdi <--- now we have moved rdi onto itself.
call Base::Base()
leaq 16+vtable for Derived(%rip), %rdx
movq -8(%rbp), %rax <--- effectively %edi, does not point into this area of the stack
movq %rdx, (%rax) <--- thus this wont change -8(%rbp)
movq -8(%rbp), %rax <--- so this statement is unnecessary
movl $4712, 12(%rax)
nop
leave
ret
选项-O1 -fno-inline -fno-elide-constructors -fno-omit-frame-pointer:
Derived::Derived():
pushq %rbp
movq %rsp, %rbp
pushq %rbx
subq $8, %rsp <--- reserve some stack space and never use it.
movq %rdi, %rbx
call Base::Base()
leaq 16+vtable for Derived(%rip), %rax
movq %rax, (%rbx)
movl $4712, 12(%rbx)
addq $8, %rsp <--- release unused stack space.
popq %rbx
popq %rbp
ret
此代码用于Derived 的构造函数,该构造函数调用Base 基本构造函数,然后覆盖位置0 处的vtable 指针,并将常量值设置为它所包含的int 成员以及Base 包含的内容。
问题:
- 我能否在翻译程序时尽量减少优化并去掉这些东西?我必须设置哪些选项?还是编译器无法使用
-O0或-O1检测到这些情况,并且无法绕过它们? - 为什么会生成
subq $8, %rsp语句?您不能优化一开始就没有意义的语句。为什么编译器会生成它呢?即使使用 O0,寄存器分配算法也不应该为不存在的东西生成代码。那为什么会这样呢?
【问题讨论】:
-
AFAIK,编译器专门这样做是为了改善您的调试体验。
-
没有效果的循环移动如何改善调试体验。请详细说明。
-
调试器可以查看
-8(%rbp)以查看%rdi(某个局部变量?)的值,即使%rdi稍后被重用(以保存一些其他局部变量)。至少如果我正确解释了程序集(我不习惯这种语法)。调试器此时更改此值也很简单,因为它会被再次读回。 -
%rdi实际上是指向堆栈上方的Derived对象的指针。该函数没有局部变量(或者不应该有——谁知道g++在内部做了什么)。 -
改进C调试。如果您正在阅读/调试 asm,请至少使用
-Og。顺便说一句,你的函数是一个非静态类成员函数,所以它有一个隐含的 arg:rdi中的this。由于-O0,哪个 g++ 溢出到堆栈中。请参阅Why does clang produce inefficient asm with -O0 (for this simple floating point sum)? 了解原因。
标签: c++ assembly compiler-construction g++