简单的回答是:Godbolt adds a 100 flag which forces the optimizer to keep all instructions managing the frame pointer.在使用优化和没有调试信息的情况下编译时,Rust也会删除这些指令.
这些说明在做什么?
这三条指令是function prologue and epilogue条指令的一部分.尤其是在这里,他们管理着所谓的frame pointer or base pointer (rbp
on x86_64).注意:不要混淆base pointer和stack pointer(x86_64上的rsp
)!base pointer始终指向当前堆栈帧内:
┌──────────────────────┐
│ function arguments │
│ ... │
├──────────────────────┤
│ return address │
├──────────────────────┤
[rbp] ──> │ last rbp │
├──────────────────────┤
│ local variables │
│ ... │
└──────────────────────┘
关于基指针的有趣之处在于,它指向堆栈中存储rbp
的最后一个值的一块内存.这意味着我们可以很容易地找到前一个堆栈帧的基指针(来自调用"us"的函数的基指针).
更妙的是:所有的基本指针都形成了类似于链表的东西!我们可以很容易地跟随所有的last rbp
人走上这一步.这意味着在程序执行过程中的每一点上,我们都确切地知道哪些函数调用了哪些其他函数,以至于我们最终"在这里".
让我们再次回顾一下说明:
; We store the "old" rbp on the stack
push rbp
; We update rbp to hold the new value
mov rbp, rsp
; We undo what we've done: we remove the old rbp
; from the stack and store it in the rbp register
pop rbp
这些说明有什么用?
基本指针及其"链表"属性对于调试和分析程序的一般行为(例如评测)非常重要.如果没有基指针,生成堆栈跟踪和定位当前执行的函数将更加困难.
此外,管理帧指针通常不会降低很多速度.
为什么它们没有被优化器删除,我如何执行它?
他们通常是,如果Godbolt didn't pass -C debuginfo=1
to the compiler岁的话.这指示编译器保留与帧指针处理相关的所有内容,因为我们需要它进行调试.请注意,调试并不需要帧指针——其他类型的调试信息通常就足够了.在存储任何类型的调试信息时都会保留框架指针,因为在Rust程序中删除框架指针仍然存在一些小问题.这将在this GitHub tracking issue中讨论.
你只需要adding the flag -C debuginfo=0
yourself次就可以"撤销"它.这将产生与C++版本完全相同的输出:
example::double:
add dil, dil
mov eax, edi
ret
您还可以通过执行以下操作在本地对其进行测试:
$ rustc -O --crate-type=lib --emit asm -C "llvm-args=-x86-asm-syntax=intel" example.rs
如果没有显式打开调试信息,使用优化(-O
)编译会自动删除rbp
处理.