为什么clang -S命令不为C++类成员函数生成汇编代码?
问题:为什么类成员函数与独立函数的汇编生成结果不同?
假设我们想要查看clang为以下代码中Node::Destroy成员函数生成的汇编:
struct Node { Node* next = nullptr; int x; ~Node() { delete next; } void Destroy() { delete this; } };
执行clang++ -S foo.cc后,得到的输出几乎是空的:
.text .file "foo.cc" .ident "Debian clang version 14.0.6-2" .section ".note.GNU-stack","",@progbits .addrsig
但如果将Destroy改为接收Node*参数的独立函数,clang则会生成对应的汇编代码:
.text .file "foo.cc" .globl _Z7DestroyP4Node # -- Begin function _Z7DestroyP4Node .p2align 4, 0x90 .type _Z7DestroyP4Node,@function _Z7DestroyP4Node: # @_Z7DestroyP4Node .cfi_startproc # %bb.0: pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset %rbp, -16 movq %rsp, %rbp .cfi_def_cfa_register %rbp subq $16, %rsp movq %rdi, -8(%rbp) [...]
在Compiler Explorer中也存在同样的现象,请问产生这种差异的原因是什么?
答案
核心差异源于函数的链接属性以及编译器的死代码消除优化策略:
1. 类内定义的成员函数默认是隐式内联函数
C++标准规定,在类内部定义的非虚成员函数会被隐式视为inline函数。内联函数的链接属性为内部链接(internal),即该函数的作用域仅局限于当前编译单元。
编译器在编译时发现这个Destroy函数没有被任何代码调用,会直接将其判定为无用的死代码,在生成汇编阶段直接剔除,因此不会产生对应的汇编指令。
2. 独立全局函数默认是外部链接属性
改为独立的全局函数后,该函数默认拥有外部链接(external)属性。编译器无法预知这个函数是否会被其他编译单元的代码调用,因此即使当前编译单元内没有引用它,也会保留其汇编代码,不会执行死代码消除。
验证方式
- 若要强制保留类内成员函数的汇编,可添加
__attribute__((used))属性,告诉编译器该函数需要被保留:
此时执行struct Node { // ... 其他成员 void Destroy() __attribute__((used)) { delete this; } };clang++ -S foo.cc就能看到Node::Destroy的汇编代码。 - 若给独立全局函数添加
static修饰(转为内部链接):
编译器同样会因为函数未被调用而剔除,不会生成汇编。static void Destroy(Node* node) { delete node; }
内容的提问来源于stack exchange,提问作者jacobsa
相关产品推荐
相关产品推荐

