C++ Lambda 生成的 ASM 代码



我有以下初始C++代码:

class Lambda
{
public:
int compute(int &value){
auto get = [&value]() -> int {
return 11 * value;
};
return get();
}
};
int main(){
Lambda lambda;
int value = 77;
return lambda.compute(value);
}

使用clang编译(使用-O1)生成以下ASM:

main: # @main
push rax
mov dword ptr [rsp + 4], 77
mov rdi, rsp
lea rsi, [rsp + 4]
call Lambda::compute(int&)
pop rcx
ret
Lambda::compute(int&): # @Lambda::compute(int&)
push rax
mov qword ptr [rsp], rsi
mov rdi, rsp
call Lambda::compute(int&)::{lambda()#1}::operator()() const
pop rcx
ret
Lambda::compute(int&)::{lambda()#1}::operator()() const: # @Lambda::compute(int&)::{lambda()#1}::operator()() const
mov rax, qword ptr [rdi]
mov eax, dword ptr [rax]
lea ecx, [rax + 4*rax]
lea eax, [rax + 2*rcx]
ret

问题:

  1. ASM中出现的{lambda()#1}是什么?据我所知,它可能是封装函数对象(即 lambda 体)的闭包。 如果是这样,请确认。
  2. 每次触发compute()时是否都会生成新的闭包?还是同一个实例?
  1. 是的,调用lambda函数需要生成闭包[除非编译器可以推断它实际上没有被使用]

  2. 通过此优化,每次调用都将是对compute的调用,而 又调用内部函数get(),这是compute函数中的 lambda 函数。让编译器优化到更高的程度,对于这种情况,将优化调用 - 在我的尝试中,它将完全删除整个调用-O2,并返回预先计算的常量 847 - 正如您所期望的那样。对于更复杂的情况,它可能会也可能不会内联 lambda 部分,但保留外部调用,反之亦然。这在很大程度上取决于所涉及的功能内部所发生事件的确切细节。

    需要明确的是,编译器正在执行您要求的操作:调用函数compute,而函数又调用函数get

添加

int value2 = 88;
int tmp = lambda.compute(value2);

进入原始问题中的main函数,基本上会对生成的代码进行这种更改(在 Linux 上使用 clang++):

main:                                   # @main
pushq   %rbx
subq    $16, %rsp
movl    $77, 12(%rsp)
## new line to set value2
movl    $88, 8(%rsp)
movq    %rsp, %rbx
## New line, passing reference of `value2` to lambda.compute
leaq    8(%rsp), %rsi
movq    %rbx, %rdi
## Call lambda.compute
callq   _ZN6Lambda7computeERi
## Same as before.
leaq    12(%rsp), %rsi
movq    %rbx, %rdi
callq   _ZN6Lambda7computeERi
addq    $16, %rsp
popq    %rbx
retq

生成的代码

  1. 它是您在compute中声明的 lambda 函数的主体(实现)。
  2. 是的,
  3. 每次调用计算时,无论是在概念上还是在实践中(在此优化级别1),都会在堆栈上创建一个新的闭包,并使用指向该闭包的指针调用关联的 lambda 函数(作为rdi传递,即作为第一个参数,方式与指向成员函数的this相同)。

1"在此优化级别"部分非常重要。这里没有任何内容实际上需要编译器生成闭包或单独的 lambda 函数。例如,在-O2,clang 优化了所有这些,并直接在main()中将答案作为常量返回。GCC 即使在-O1.

最新更新