C++ Lambda 生成的 ASM 代码

标签 c++ assembly lambda clang

我有以下初始 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(),这是一个 lambda 函数在你的 compute 函数中。让编译器优化到更高的程度,对于这种情况,将优化调用 - 在我的尝试中,它将完全删除带有 -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

代码生成到

关于C++ Lambda 生成的 ASM 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49294022/

相关文章:

assembly - RAX、RBX、RCX、RDX、RSI、RDI、RBP、RSP、R8-R15 是否可以互换?

xcode - 如何在 OS X 中使用 masm 编译并运行汇编代码?

c - 如何编写接受包含整数和标点符号的用户输入的 C 程序?

c# - 如何使用 lambda 或 linq 从整数列表中的对象列表中收集多个 int 属性?

go - golang发送一个lambda捕获组

C++ 数组大小声明和常量

c++ - ffmpeg:transcoding.c 问题

Java 8 parallelstream 将返回列表收集到一个列表而不是列表列表中

c++ - 在这种情况下如何使用动态多态性?

c++ - 如何包含 C++ 库以便 node-gyp 可以链接?