block literal irgen: several improvements on naming block

literal helper functions. All helper functions (global
and locals) use block_invoke as their prefix. Local literal
helper names are prefixed by their enclosing mangled function
names. Blocks in non-local initializers (e.g. a global variable 
or a C++11 field) are prefixed by their mangled variable name. 
The descriminator number added to end of the name starts off 
with blank (for first block) and _<N> (for the N+2-th block).

llvm-svn: 159206
This commit is contained in:
Fariborz Jahanian 2012-06-26 16:06:38 +00:00
parent 4196046714
commit 6362803cfe
22 changed files with 109 additions and 55 deletions

View File

@ -121,6 +121,7 @@ public:
raw_ostream &) = 0;
void mangleGlobalBlock(const BlockDecl *BD,
const NamedDecl *ID,
raw_ostream &Out);
void mangleCtorBlock(const CXXConstructorDecl *CD, CXXCtorType CT,
const BlockDecl *BD, raw_ostream &Out);
@ -129,7 +130,8 @@ public:
void mangleBlock(const DeclContext *DC, const BlockDecl *BD,
raw_ostream &Out);
// Do the right thing.
void mangleBlock(const BlockDecl *BD, raw_ostream &Out);
void mangleBlock(const BlockDecl *BD, raw_ostream &Out,
const NamedDecl *ID=0);
void mangleObjCMethodName(const ObjCMethodDecl *MD,
raw_ostream &);

View File

@ -40,7 +40,11 @@ static void mangleFunctionBlock(MangleContext &Context,
StringRef Outer,
const BlockDecl *BD,
raw_ostream &Out) {
Out << "__" << Outer << "_block_invoke_" << Context.getBlockId(BD, true);
unsigned discriminator = Context.getBlockId(BD, true);
if (discriminator == 0)
Out << "__" << Outer << "_block_invoke";
else
Out << "__" << Outer << "_block_invoke_" << discriminator+1;
}
static void checkMangleDC(const DeclContext *DC, const BlockDecl *BD) {
@ -62,8 +66,20 @@ static void checkMangleDC(const DeclContext *DC, const BlockDecl *BD) {
void MangleContext::anchor() { }
void MangleContext::mangleGlobalBlock(const BlockDecl *BD,
const NamedDecl *ID,
raw_ostream &Out) {
Out << "__block_global_" << getBlockId(BD, false);
unsigned discriminator = getBlockId(BD, false);
if (ID) {
if (shouldMangleDeclName(ID))
mangleName(ID, Out);
else {
Out << ID->getIdentifier()->getName();
}
}
if (discriminator == 0)
Out << "_block_invoke";
else
Out << "_block_invoke_" << discriminator+1;
}
void MangleContext::mangleCtorBlock(const CXXConstructorDecl *CD,
@ -99,8 +115,8 @@ void MangleContext::mangleBlock(const DeclContext *DC, const BlockDecl *BD,
mangleObjCMethodName(Method, Stream);
} else {
const NamedDecl *ND = cast<NamedDecl>(DC);
if (IdentifierInfo *II = ND->getIdentifier())
Stream << II->getName();
if (!shouldMangleDeclName(ND) && ND->getIdentifier())
Stream << ND->getIdentifier()->getName();
else {
// FIXME: We were doing a mangleUnqualifiedName() before, but that's
// a private member of a class that will soon itself be private to the
@ -131,12 +147,13 @@ void MangleContext::mangleObjCMethodName(const ObjCMethodDecl *MD,
}
void MangleContext::mangleBlock(const BlockDecl *BD,
raw_ostream &Out) {
raw_ostream &Out,
const NamedDecl *ID) {
const DeclContext *DC = BD->getDeclContext();
while (isa<BlockDecl>(DC) || isa<EnumDecl>(DC))
DC = DC->getParent();
if (DC->isFunctionOrMethod())
mangleBlock(DC, BD, Out);
else
mangleGlobalBlock(BD, Out);
mangleGlobalBlock(BD, ID, Out);
}

View File

@ -630,7 +630,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
// Using the computed layout, generate the actual block function.
bool isLambdaConv = blockInfo.getBlockDecl()->isConversionFromLambda();
llvm::Constant *blockFn
= CodeGenFunction(CGM).GenerateBlockFunction(CurGD, blockInfo,
= CodeGenFunction(CGM, true).GenerateBlockFunction(CurGD, blockInfo,
CurFuncDecl, LocalDeclMap,
isLambdaConv);
blockFn = llvm::ConstantExpr::getBitCast(blockFn, VoidPtrTy);
@ -1003,7 +1003,8 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD,
// Check if we should generate debug info for this block function.
if (CGM.getModuleDebugInfo())
DebugInfo = CGM.getModuleDebugInfo();
CurGD = GD;
BlockInfo = &blockInfo;
// Arrange for local static and local extern declarations to appear

View File

@ -28,7 +28,7 @@
using namespace clang;
using namespace CodeGen;
CodeGenFunction::CodeGenFunction(CodeGenModule &cgm)
CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext)
: CodeGenTypeCache(cgm), CGM(cgm),
Target(CGM.getContext().getTargetInfo()),
Builder(cgm.getModule().getContext()),
@ -42,7 +42,8 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm)
TerminateHandler(0), TrapBB(0) {
CatchUndefined = getContext().getLangOpts().CatchUndefined;
CGM.getCXXABI().getMangleContext().startNewFunction();
if (!suppressNewContext)
CGM.getCXXABI().getMangleContext().startNewFunction();
}
CodeGenFunction::~CodeGenFunction() {

View File

@ -1198,7 +1198,7 @@ private:
llvm::BasicBlock *TrapBB;
public:
CodeGenFunction(CodeGenModule &cgm);
CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext=false);
~CodeGenFunction();
CodeGenTypes &getTypes() const { return CGM.getTypes(); }

View File

@ -347,7 +347,8 @@ StringRef CodeGenModule::getMangledName(GlobalDecl GD) {
else if (const CXXDestructorDecl *D = dyn_cast<CXXDestructorDecl>(ND))
getCXXABI().getMangleContext().mangleCXXDtor(D, GD.getDtorType(), Out);
else if (const BlockDecl *BD = dyn_cast<BlockDecl>(ND))
getCXXABI().getMangleContext().mangleBlock(BD, Out);
getCXXABI().getMangleContext().mangleBlock(BD, Out,
dyn_cast_or_null<VarDecl>(initializedGlobalDecl.getDecl()));
else
getCXXABI().getMangleContext().mangleName(ND, Out);
@ -368,7 +369,8 @@ void CodeGenModule::getBlockMangledName(GlobalDecl GD, MangleBuffer &Buffer,
const Decl *D = GD.getDecl();
llvm::raw_svector_ostream Out(Buffer.getBuffer());
if (D == 0)
MangleCtx.mangleGlobalBlock(BD, Out);
MangleCtx.mangleGlobalBlock(BD,
dyn_cast_or_null<VarDecl>(initializedGlobalDecl.getDecl()), Out);
else if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(D))
MangleCtx.mangleCtorBlock(CD, GD.getCtorType(), BD, Out);
else if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(D))
@ -1551,8 +1553,10 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
// FIXME: It does so in a global constructor, which is *not* what we
// want.
if (!Init)
if (!Init) {
initializedGlobalDecl = GlobalDecl(D);
Init = EmitConstantInit(*InitDecl);
}
if (!Init) {
QualType T = InitExpr->getType();
if (D->getType()->isReferenceType())

View File

@ -351,6 +351,8 @@ class CodeGenModule : public CodeGenTypeCache {
struct {
int GlobalUniqueCount;
} Block;
GlobalDecl initializedGlobalDecl;
/// @}
public:
@ -596,7 +598,7 @@ public:
/// getUniqueBlockCount - Fetches the global unique block count.
int getUniqueBlockCount() { return ++Block.GlobalUniqueCount; }
/// getBlockDescriptorType - Fetches the type of a generic block
/// descriptor.
llvm::Type *getBlockDescriptorType();

View File

@ -3,7 +3,7 @@
namespace PR12746 {
// CHECK: define zeroext i1 @_ZN7PR127462f1EPi
bool f1(int *x) {
// CHECK: store i8* bitcast (i1 (i8*)* @__f1_block_invoke_0 to i8*)
// CHECK: store i8* bitcast (i1 (i8*)* @___ZN7PR127462f1EPi_block_invoke to i8*)
bool (^outer)() = ^ {
auto inner = [&]() -> bool {
return x == 0;
@ -13,8 +13,8 @@ namespace PR12746 {
return outer();
}
// CHECK: define internal zeroext i1 @__f1_block_invoke_0
// CHECK: call zeroext i1 @"_ZNK7PR127462f119__f1_block_invoke_03$_0clEv"
// CHECK: define internal zeroext i1 @___ZN7PR127462f1EPi_block_invoke
// CHECK: call zeroext i1 @"_ZNK7PR127462f132___ZN7PR127462f1EPi_block_invoke3$_0clEv"
bool f2(int *x) {
auto outer = [&]() -> bool {

View File

@ -12,7 +12,7 @@ struct s0 {
int a[64];
};
// CHECK: define internal void @__f2_block_invoke_0(%struct.s0* noalias sret {{%.*}}, i8* {{%.*}}, %struct.s0* byval align 4 {{.*}})
// CHECK: define internal void @__f2_block_invoke(%struct.s0* noalias sret {{%.*}}, i8* {{%.*}}, %struct.s0* byval align 4 {{.*}})
struct s0 f2(struct s0 a0) {
return ^(struct s0 a1){ return a1; }(a0);
}

View File

@ -8,7 +8,7 @@
// X32: [[STR1:@.*]] = private unnamed_addr constant [6 x i8] c"v4@?0\00"
// X32: @__block_descriptor_tmp = internal constant [[FULL_DESCRIPTOR_T:.*]] { i32 0, i32 20, i8* getelementptr inbounds ([6 x i8]* [[STR1]], i32 0, i32 0), i8* null }
// X32: @__block_literal_global = internal constant [[GLOBAL_LITERAL_T:.*]] { i8** @_NSConcreteGlobalBlock, i32 1342177280, i32 0, i8* bitcast (void (i8*)* @__block_global_{{.*}} to i8*), [[DESCRIPTOR_T:%.*]]* bitcast ([[FULL_DESCRIPTOR_T]]* @__block_descriptor_tmp to {{%.*}}*) }
// X32: @__block_literal_global = internal constant [[GLOBAL_LITERAL_T:.*]] { i8** @_NSConcreteGlobalBlock, i32 1342177280, i32 0, i8* bitcast (void (i8*)* @global_block_invoke{{.*}} to i8*), [[DESCRIPTOR_T:%.*]]* bitcast ([[FULL_DESCRIPTOR_T]]* @__block_descriptor_tmp to {{%.*}}*) }
// X32: [[STR2:@.*]] = private unnamed_addr constant [11 x i8] c"i12@?0c4f8\00"
// X32: @__block_descriptor_tmp{{.*}} = internal constant [[FULL_DESCRIPTOR_T]] { i32 0, i32 24, i8* getelementptr inbounds ([11 x i8]* [[STR2]], i32 0, i32 0), i8* null }
// X32: store i32 1073741824, i32*

View File

@ -1,17 +1,17 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fblocks -emit-llvm -o - %s | FileCheck %s
// pr8707
// CHECK: @__block_global_0.test = internal global i32
// CHECK: @block_block_invoke.test = internal global i32
int (^block)(void) = ^ {
static int test=0;
return test;
};
// CHECK: @__block_global_1.test = internal global i32
// CHECK: @block1_block_invoke_2.test = internal global i32
void (^block1)(void) = ^ {
static int test = 2;
return;
};
// CHECK: @__block_global_2.test = internal global i32
// CHECK: @block2_block_invoke_3.test = internal global i32
int (^block2)(void) = ^ {
static int test = 5;
return test;

View File

@ -12,7 +12,7 @@ int main ()
b();
}
// CHECK: define internal void @__main_block_invoke_0
// CHECK: define internal void @__main_block_invoke
// CHECK: [[C1:%.*]] = alloca { double, double }, align 8
// CHECK: [[RP:%.*]] = getelementptr inbounds { double, double }* [[C1]], i32 0, i32 0
// CHECK-NEXT: [[R:%.*]] = load double* [[RP]]

View File

@ -15,5 +15,5 @@ int main()
return 0; // not reached
}
// CHECK: @__func__.__main_block_invoke_0 = private unnamed_addr constant [22 x i8] c"__main_block_invoke_0\00"
// CHECK: call void @PRINTF({{.*}}@__func__.__main_block_invoke_
// CHECK: @__func__.__main_block_invoke = private unnamed_addr constant [20 x i8] c"__main_block_invoke\00"
// CHECK: call void @PRINTF({{.*}}@__func__.__main_block_invoke

View File

@ -36,13 +36,13 @@ X::~X() {
};
// CHECK: define internal void @___ZN4ZoneC2Ev_block_invoke
// CHECK: define internal void @___ZN4ZoneC2Ev_block_invoke_
// CHECK: define internal void @___ZN4ZoneC2Ev_block_invoke_
// CHECK: define internal void @___ZN4ZoneD2Ev_block_invoke_
// CHECK: define internal void @___ZN4ZoneD2Ev_block_invoke
// CHECK: define internal void @___ZN4ZoneD2Ev_block_invoke_
// CHECK: define internal void @___ZN1XC1Ev_block_invoke
// CHECK: define internal void @___ZN1XC1Ev_block_invoke_
// CHECK: define internal void @___ZN1XC1Ev_block_invoke_
// CHECK: define internal void @___ZN1XC2Ev_block_invoke_
// CHECK: define internal void @___ZN1XC2Ev_block_invoke
// CHECK: define internal void @___ZN1XC2Ev_block_invoke_
// CHECK: define internal void @___ZN1XD2Ev_block_invoke_
// CHECK: define internal void @___ZN1XD2Ev_block_invoke
// CHECK: define internal void @___ZN1XD2Ev_block_invoke_

View File

@ -2,8 +2,8 @@
namespace test0 {
// CHECK: define void @_ZN5test04testEi(
// CHECK: define internal void @__test_block_invoke_{{.*}}(
// CHECK: define internal void @__block_global_{{.*}}(
// CHECK: define internal void @___ZN5test04testEi_block_invoke{{.*}}(
// CHECK: define internal void @___ZN5test04testEi_block_invoke_2{{.*}}(
void test(int x) {
^{ ^{ (void) x; }; };
}
@ -119,7 +119,7 @@ namespace test4 {
consume(^{ return foo(A()); });
}
// CHECK: define void @_ZN5test44testEv()
// CHECK: define internal void @__test_block_invoke
// CHECK: define internal void @___ZN5test44testEv_block_invoke
// CHECK: [[TMP:%.*]] = alloca [[A:%.*]], align 1
// CHECK-NEXT: bitcast i8*
// CHECK-NEXT: call void @_ZN5test41AC1Ev([[A]]* [[TMP]])

View File

@ -0,0 +1,27 @@
// RUN: %clang_cc1 -std=c++11 -emit-llvm -fblocks -o - -triple x86_64-apple-darwin10 %s | FileCheck %s
// rdar://11343499
namespace N {
typedef void (^BL)();
int func(BL, BL, BL);
// CHECK: define internal void @_ZN1N8ArrBlockE_block_invoke(
// CHECK: define internal void @_ZN1N8ArrBlockE_block_invoke_2(
// CHECK: define internal void @_ZN1N8ArrBlockE_block_invoke_3
BL ArrBlock [] = { ^{}, ^{}, ^{} };
// CHECK: define internal void @_ZN1N4ivalE_block_invoke_4(
// CHECK: define internal void @_ZN1N4ivalE_block_invoke_5(
// CHECK: define internal void @_ZN1N4ivalE_block_invoke_6(
int ival = func(^{}, ^{}, ^{});
// CHECK: define internal void @_ZN1N9gvarlobalE_block_invoke_7(
void (^gvarlobal)(void) = ^{};
struct S {
BL field = ^{};
};
// CHECK: define internal void @_ZN1N3blfE_block_invoke_8(
S blf;
};

View File

@ -124,7 +124,7 @@ void test4(void) {
// CHECK-NEXT: [[T1:%.*]] = load i8** [[T0]]
// CHECK-NEXT: call void @objc_release(i8* [[T1]])
// CHECK: define internal void @__test4_block_invoke_
// CHECK: define internal void @__test4_block_invoke
// CHECK: [[SLOT:%.*]] = getelementptr inbounds {{.*}}, i32 0, i32 6
// CHECK-NEXT: [[T0:%.*]] = load i8** [[SLOT]], align 8
// CHECK-NEXT: store i8* null, i8** [[SLOT]],
@ -200,7 +200,7 @@ void test6(void) {
// CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6
// CHECK-NEXT: call void @objc_destroyWeak(i8** [[T0]])
// CHECK: define internal void @__test6_block_invoke_
// CHECK: define internal void @__test6_block_invoke
// CHECK: [[SLOT:%.*]] = getelementptr inbounds {{.*}}, i32 0, i32 6
// CHECK-NEXT: call i8* @objc_storeWeak(i8** [[SLOT]], i8* null)
// CHECK-NEXT: ret void
@ -238,7 +238,7 @@ void test7(void) {
// CHECK-NEXT: call void @objc_destroyWeak(i8** [[VAR]])
// CHECK-NEXT: ret void
// CHECK: define internal void @__test7_block_invoke_
// CHECK: define internal void @__test7_block_invoke
// CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* {{%.*}}, i32 0, i32 5
// CHECK-NEXT: [[T0:%.*]] = call i8* @objc_loadWeak(i8** [[SLOT]])
// CHECK-NEXT: call void @test7_consume(i8* [[T0]])

View File

@ -4,7 +4,7 @@
// CHECK: define i8* @{{.*}}test0
// CHECK: define internal void @__test0_block_invoke_0(
// CHECK: define internal void @{{.*}}_block_invoke(
// CHECK: call i8* @objc_assign_strongCast(
// CHECK-NEXT: ret void
id test0(id x) {

View File

@ -18,7 +18,7 @@ void foo(T *P) {
-(void) im0;
@end
// CHECK: define internal i32 @"__8-[A im0]_block_invoke_0"(
// CHECK: define internal i32 @"__8-[A im0]_block_invoke"(
@implementation A
-(void) im0 {
(void) ^{ return 1; }();
@ -91,7 +91,7 @@ void test2(Test2 *x) {
// rdar://problem/9124263
// In the test above, check that the use in the invocation function
// doesn't require a read barrier.
// CHECK: define internal void @__test2_block_invoke_
// CHECK: define internal void @__test2_block_invoke
// CHECK: [[BLOCK:%.*]] = bitcast i8* {{%.*}} to [[BLOCK_T]]*
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
// CHECK-NEXT: [[T1:%.*]] = load i8** [[T0]]

View File

@ -9,7 +9,7 @@ fp f() { auto x = []{ return 3; }; return x; }
// MRC: define i32 ()* @_Z1fv(
// MRC: define internal i32 ()* @"_ZZ1fvENK3$_0cvU13block_pointerFivEEv"
// MRC: store i8* bitcast (i8** @_NSConcreteStackBlock to i8*)
// MRC: store i8* bitcast (i32 (i8*)* @"___ZZ1fvENK3$_0cvU13block_pointerFivEEv_block_invoke_0" to i8*)
// MRC: store i8* bitcast (i32 (i8*)* @"___ZZ1fvENK3$_0cvU13block_pointerFivEEv_block_invoke" to i8*)
// MRC: call i32 ()* (i8*, i8*)* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i32 ()* (i8*, i8*)*)
// MRC: call i32 ()* (i8*, i8*)* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i32 ()* (i8*, i8*)*)
// MRC: ret i32 ()*
@ -17,7 +17,7 @@ fp f() { auto x = []{ return 3; }; return x; }
// ARC: define i32 ()* @_Z1fv(
// ARC: define internal i32 ()* @"_ZZ1fvENK3$_0cvU13block_pointerFivEEv"
// ARC: store i8* bitcast (i8** @_NSConcreteStackBlock to i8*)
// ARC: store i8* bitcast (i32 (i8*)* @"___ZZ1fvENK3$_0cvU13block_pointerFivEEv_block_invoke_0" to i8*)
// ARC: store i8* bitcast (i32 (i8*)* @"___ZZ1fvENK3$_0cvU13block_pointerFivEEv_block_invoke" to i8*)
// ARC: call i8* @objc_retainBlock
// ARC: call i8* @objc_autoreleaseReturnValue
@ -26,14 +26,14 @@ fp global;
void f2() { global = []{ return 3; }; }
// MRC: define void @_Z2f2v() nounwind {
// MRC: store i8* bitcast (i32 (i8*)* @__f2_block_invoke_0 to i8*),
// MRC: store i8* bitcast (i32 (i8*)* @___Z2f2v_block_invoke to i8*),
// MRC-NOT: call
// MRC: ret void
// ("global" contains a dangling pointer after this function runs.)
// ARC: define void @_Z2f2v() nounwind {
// ARC: store i8* bitcast (i32 (i8*)* @__f2_block_invoke_0 to i8*),
// ARC: store i8* bitcast (i32 (i8*)* @___Z2f2v_block_invoke to i8*),
// ARC: call i8* @objc_retainBlock
// ARC: call void @objc_release
// ARC: define internal i32 @__f2_block_invoke_0
// ARC: define internal i32 @___Z2f2v_block_invoke
// ARC: call i32 @"_ZZ2f2vENK3$_1clEv

View File

@ -1,19 +1,19 @@
// RUN: %clang_cc1 -emit-llvm -fblocks -o - -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 %s | FileCheck %s
// CHECK: @_ZGVN3foo20__foo_block_invoke_05valueE = internal global i64 0
// CHECK: @_ZGVN3foo22___Z3foov_block_invoke5valueE = internal global i64 0
int f();
void foo() {
// CHECK: define internal i32 @__foo_block_invoke_0
// CHECK: call i32 @__cxa_guard_acquire(i64* @_ZGVN3foo20__foo_block_invoke_05value
// CHECK: define internal i32 @___Z3foov_block_invoke
// CHECK: call i32 @__cxa_guard_acquire(i64* @_ZGVN3foo22___Z3foov_block_invoke5valueE
(void)^(int x) {
static int value = f();
return x + value;
};
}
// CHECK: define internal i32 @__block_global_0
// CHECK: define internal i32 @i_block_invoke
int i = ^(int x) { return x;}(i);
@interface A
@ -22,9 +22,9 @@ int i = ^(int x) { return x;}(i);
@implementation A
- (void)method {
// CHECK: define internal signext i8 @"__11-[A method]_block_invoke_0"
// CHECK: define internal signext i8 @"__11-[A method]_block_invoke"
(void)^(int x) {
// CHECK: @"_ZN11-[A method]30__11-[A method]_block_invoke_04nameE"
// CHECK: @"_ZN11-[A method]28__11-[A method]_block_invoke4nameE"
static const char *name = "hello";
return name[x];
};
@ -39,10 +39,10 @@ void foo(int) {
}
namespace N {
// CHECK: define internal signext i8 @__bar_block_invoke_0
// CHECK: define internal signext i8 @___Z3fooi_block_invoke
void bar() {
(void)^(int x) {
// CHECK: @_ZN1N3bar20__bar_block_invoke_04nameE
// CHECK: @_ZN1N3bar26___ZN1N3barEv_block_invoke4nameE
static const char *name = "hello";
return name[x];
};

View File

@ -22,7 +22,7 @@ struct X {
X blocksNRVO() {
return ^{
// CHECK: define internal void @__blocksNRVO_block_invoke_0
// CHECK: define internal void @___Z10blocksNRVOv_block_invoke
X x;
// CHECK: tail call void @_ZN1XC1Ev
// CHECK-NEXT: ret void