这个问题的有趣之处在于它是如此依赖于编译器。使用 Clang/LLVM:
#include <iostream>
using namespace std;
inline int foo(int a) { return a << 1; }
struct Bar
{
int a;
Bar(int x) : a(x) {}
Bar baz() { return a << 1; }
};
void out(int x) __attribute__ ((noinline));
void out(int x) { cout << x; }
void out(Bar x) __attribute__ ((noinline));
void out(Bar x) { cout << x.a; }
void f1(int x) __attribute ((noinline));
void f1(int x) { out(foo(x)); }
void f2(Bar b) __attribute ((noinline));
void f2(Bar b) { out(b.baz()); }
int main(int argc, char** argv)
{
f1(argc);
f2(argc);
}
给出以下 IR:
define void @_Z3outi(i32 %x) uwtable noinline {
%1 = tail call %"class.std::basic_ostream"*
@_ZNSolsEi(%"class.std::basic_ostream"* @_ZSt4cout, i32 %x)
ret void
}
define void @_Z3out3Bar(i32 %x.coerce) uwtable noinline {
%1 = tail call %"class.std::basic_ostream"*
@_ZNSolsEi(%"class.std::basic_ostream"* @_ZSt4cout, i32 %x.coerce)
ret void
}
define void @_Z2f1i(i32 %x) uwtable noinline {
%1 = shl i32 %x, 1
tail call void @_Z3outi(i32 %1)
ret void
}
define void @_Z2f23Bar(i32 %b.coerce) uwtable noinline {
%1 = shl i32 %b.coerce, 1
tail call void @_Z3out3Bar(i32 %1)
ret void
}
不出所料,生成的程序集完全相同:
.globl _Z2f1i
.align 16, 0x90
.type _Z2f1i,@function
_Z2f1i: # @_Z2f1i
.Ltmp6:
.cfi_startproc
# BB#0:
addl %edi, %edi
jmp _Z3outi # TAILCALL
.Ltmp7:
.size _Z2f1i, .Ltmp7-_Z2f1i
.Ltmp8:
.cfi_endproc
.Leh_func_end2:
.globl _Z2f23Bar
.align 16, 0x90
.type _Z2f23Bar,@function
_Z2f23Bar: # @_Z2f23Bar
.Ltmp9:
.cfi_startproc
# BB#0:
addl %edi, %edi
jmp _Z3out3Bar # TAILCALL
.Ltmp10:
.size _Z2f23Bar, .Ltmp10-_Z2f23Bar
.Ltmp11:
.cfi_endproc
.Leh_func_end3:
通常,只要对类的方法进行内联,this
就可以很容易地省略参数和引用。我不太明白 gcc 怎么会搞砸这个。