为何仅返回有状态lambda的C++函数会编译出汇编代码?
为什么返回闭包/仿函数的函数会生成仅
ret的汇编? 先看两个示例代码:
示例1:返回有状态lambda的函数
auto foo(double a) { return [a](double b) -> double { return a + b; }; }
编译后汇编:
foo(double): ret
示例2:返回自定义仿函数的函数
struct Bar { double a; double operator()(double b) const { return a + b; } }; Bar bar(double a) { return Bar{a}; }
编译后同样生成仅ret的汇编。
核心原因解析
外部链接属性的约束
默认情况下,foo和bar都是外部链接的函数。编译器不会主动删除有外部链接的函数——虽然从代码结构看,其他翻译单元(TU)根本无法声明并调用这些函数(lambda闭包类型是匿名的、仅当前TU可见;Bar也是当前TU内定义的类型,其他TU无法识别返回值类型),但编译器不会做这种跨TU的语义分析,它会假设这个函数可能被其他TU引用,因此必须保留函数的符号和最简化的函数体。返回值优化与调用约定的叠加效果
无论是lambda生成的闭包类型,还是Bar结构体,都是仅包含一个double成员的平凡可复制类型。在x86-64的System V调用约定中:- 函数参数
double a通过XMM0寄存器传入 - 返回大小不超过16字节的平凡结构体时,会直接用寄存器传递返回值(这里恰好也是XMM0寄存器)
- 构造
Bar{a}或者lambda闭包的过程,本质上只是把输入的a赋值给返回结构体的成员,而因为调用约定的匹配,这个赋值操作完全不需要额外指令——直接把XMM0寄存器的值原封不动返回即可,所以函数体不需要任何逻辑,仅需ret指令。
- 函数参数
如何让函数被完全优化掉?
如果希望编译器彻底删除未被调用的foo/bar,可以给函数加上static修饰,将其改为内部链接:
static auto foo(double a) { return [a](double b) -> double { return a + b; }; } static Bar bar(double a) { return Bar{a}; }
此时编译器明确知道该函数仅在当前TU可见,且如果没有被调用,就会完全优化掉,不会生成任何汇编代码。
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

