Lambdas作为闭包采取环境.RIP寄存器的关键作用
Lambdas as closures taking environment. The crucial role of RIP register
我查看了以下代码段的汇编程序输出,我惊呆了:
int x=0, y=0; // global
// r1, r2 are ints, local.
std::thread t([&x, &y, &r1, &r2](){
x = 1;
r1 = y;
});
!std::thread t([&x, &y, &r1, &r2](){
<lambda()>::operator()(void) const+0: push %rbp
<lambda()>::operator()(void) const+1: mov %rsp,%rbp
<lambda()>::operator()(void) const+4: mov %rdi,-0x8(%rbp)
<lambda()>::operator()(void) const+18: mov -0x8(%rbp),%rax
<lambda()>::operator()(void) const+22: mov (%rax),%rax
! x = 1;
<lambda()>::operator()(void) const()
<lambda()>::operator()(void) const+8: movl $0x1,0x205362(%rip) # 0x6062ac <x>
! r1 = y;
<lambda()>::operator()(void) const+25: mov 0x205359(%rip),%edx # 0x6062b0 <y>
<lambda()>::operator()(void) const+31: mov %edx,(%rax)
!
!});
<lambda()>::operator()(void) const+33: nop
<lambda()>::operator()(void) const+34: pop %rbp
<lambda()>::operator()(void) const+35: retq
为什么x
的地址,y
确定与RIP
有关。RIP
是一个指令指针,所以它似乎是狂野的。特别是,我从未见过这样的事情。(也许我没有看到很多东西:))。
我脑海中浮现的唯一解释是这样一个事实,即lambda是一个闭包,并且从特定位置获取环境变量与RIP
有一些共同之处。
代码在运行时不会移动,一旦加载了代码部分,例程就不会被复制或移动。
静态数据在加载其部分后也会占用相同的地址。
因此,指令和静态变量之间的距离在编译时是已知的,并且在模块基的重新定位下它是不变的(因为指令和数据都以相同的量转换)。
因此,RIP相对寻址不仅不疯狂,而且长期以来一直缺少该功能。
在 32 位代码中,像mov eax, [var]
这样的指令是无害的,但在没有 RIP 相对寻址的 64 位中,它需要 9 个字节,1 个用于操作码,8 个用于即时。 使用 RIP 相对寻址时,即时寻址仍然是 32 位。
C++ lamdba 是函数对象的语法糖,其中捕获的变量成为实例变量。
引用捕获的变量作为指针/引用处理。
全局变量在捕获时不需要任何特殊处理,因为它们已经可以访问。
您正确地指出,x
和y
分别作为0x205362(%rip)
和0x205359(%rip)
进行访问。
由于它们是全局的,因此它们的地址在运行时是固定的,并且使用 RIP 相对寻址来访问它们。
但是,您忘了检查如何访问局部捕获的变量r1
。
它与(%rax)
一起存储,rax
之前被加载为(优化)movq (%rdi), %rax
。%rdi
是方法operator()
的第一个参数,所以this
,刚才提到的指令将第一个实例变量加载到rax
中,然后使用该值访问r1
。
简单地说,它是指向r1
的指针(或更好的引用),因为r1
存在于堆栈上,其地址在运行时是动态的(这取决于堆栈的状态)。
因此,lambda 同时使用间接寻址和 RIP 相对寻址,从而与 RIP 相对寻址在某种程度上很特殊的假设相矛盾。
请注意,捕获机制不会延长捕获变量的生命周期(如在 ECMAScript 中),因此在 lambda 中通过引用捕获本地变量以进行std::thread
几乎总是一个坏主意。
- 本质:使用__128寄存器
- 将寄存器设计成可由C和C++访问的外设的最佳实践
- 在模拟器中使用并集来模拟CPU寄存器有多合适
- 使用英特尔 PIN 修改寄存器
- AVX 指令中寄存器和指针之间的客观差异
- 如何确定我的处理器有多少个 AVX 寄存器?
- 除非使用某些寄存器,否则函数挂钩会崩溃
- 寄存器上的管道计算
- 其中关于内存和寄存器的左值和右值
- 有没有办法强制C++编译器将变量存储在寄存器中?
- "变量":函数中函数作用域不允许初始化的自动或寄存器变量'naked'
- Atmel Studio:返回一个包含数组的寄存器
- 使用 googletest 测试嵌入式C++代码时处理外设寄存器的重复符号
- 移位寄存器74HC595输出电流
- 超过255的Modbus寄存器无法访问SimpleModbus
- 如何在程序集函数中将元素数组作为参数传递时转发 ARM 寄存器的地址指针
- xmm 寄存器中的__m128何时?
- 是否可以在 GCC 中使用带有 C++17 的显式寄存器变量?
- Lambdas作为闭包采取环境.RIP寄存器的关键作用
- Eclipse 中的 Arduino 端口寄存器不起作用