Revert r153723, and its follow-ups r153728 and r153733.

These patches cause us to miscompile and/or reject code with static
function-local variables in an extern-C context. Previously, we were
papering over this as long as the variables are within the same
translation unit, and had not seen any failures in the wild. We still
need a proper fix, which involves mangling static locals inside of an
extern-C block (as GCC already does), but this patch causes pretty
widespread regressions. Firefox, and many other applications no longer
build.

Lots of test cases have been posted to the list in response to this
commit, so there should be no problem reproducing the issues.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@153768 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Chandler Carruth 2012-03-30 19:44:53 +00:00
Родитель 40afb7d4f3
Коммит 0f30a12ce7
9 изменённых файлов: 106 добавлений и 244 удалений

Просмотреть файл

@ -172,7 +172,7 @@ void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
void CGCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
const VarDecl &D,
llvm::Constant *GV,
llvm::GlobalVariable *GV,
bool PerformInit) {
ErrorUnsupportedABI(CGF, "static local variable initialization");
}

Просмотреть файл

@ -246,9 +246,8 @@ public:
/// The variable may be:
/// - a static local variable
/// - a static data member of a class template instantiation
/// In either case, it will be a (possibly casted) llvm::GlobalVariable.
virtual void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
llvm::Constant *addr, bool PerformInit);
llvm::GlobalVariable *DeclPtr, bool PerformInit);
};

Просмотреть файл

@ -169,24 +169,7 @@ static std::string GetStaticDeclName(CodeGenFunction &CGF, const VarDecl &D,
return ContextName + Separator + D.getNameAsString();
}
/// We wanted to make a variable of one type, but the variable already
/// exists with another. Is that type good enough?
///
/// The problem we're working around here is that giving a global
/// variable an initializer can require changing its type in some
/// convoluted circumstances.
static bool isExistingVarAdequate(CodeGenModule &CGM,
llvm::Type *existing, llvm::Type *desired) {
// Equality makes for a good fast path.
if (existing == desired) return true;
// Otherwise, just require them to have the same size.
return (CGM.getTargetData().getTypeStoreSize(existing)
== CGM.getTargetData().getTypeStoreSize(desired));
}
llvm::Constant *
llvm::GlobalVariable *
CodeGenFunction::CreateStaticVarDecl(const VarDecl &D,
const char *Separator,
llvm::GlobalValue::LinkageTypes Linkage) {
@ -201,37 +184,12 @@ CodeGenFunction::CreateStaticVarDecl(const VarDecl &D,
Name = GetStaticDeclName(*this, D, Separator);
llvm::Type *LTy = CGM.getTypes().ConvertTypeForMem(Ty);
unsigned addrspace = CGM.getContext().getTargetAddressSpace(Ty);
// In C++, there are strange possibilities here involving the
// double-emission of constructors and destructors.
if (CGM.getLangOpts().CPlusPlus) {
llvm::GlobalValue *value = CGM.getModule().getNamedValue(Name);
if (value && isa<llvm::GlobalVariable>(value)) {
// Check that the type is compatible with the type we want. The
// simple equality check isn't good enough because initializers
// can force the changing of a type (e.g. with unions).
if (value->getType()->getAddressSpace() == addrspace &&
isExistingVarAdequate(CGM, value->getType()->getElementType(), LTy))
return llvm::ConstantExpr::getBitCast(value,
LTy->getPointerTo(addrspace));
}
if (value) {
CGM.Error(D.getLocation(),
"problem emitting static variable '" + Name +
"': already present as different kind of symbol");
// Fall through and implicitly give it a uniqued name.
}
}
llvm::GlobalVariable *GV =
new llvm::GlobalVariable(CGM.getModule(), LTy,
Ty.isConstant(getContext()), Linkage,
CGM.EmitNullConstant(D.getType()), Name, 0,
D.isThreadSpecified(),
addrspace);
CGM.getContext().getTargetAddressSpace(Ty));
GV->setAlignment(getContext().getDeclAlign(&D).getQuantity());
if (Linkage != llvm::GlobalValue::InternalLinkage)
GV->setVisibility(CurFn->getVisibility());
@ -246,85 +204,80 @@ static bool hasNontrivialDestruction(QualType T) {
return RD && !RD->hasTrivialDestructor();
}
/// AddInitializerToStaticVarDecl - Add the initializer for 'D' to
/// the global variable that has already been created for it. If
/// the initializer has a different type than GV does, this may
/// force the underlying variable to change. Otherwise it just
/// returns it.
///
/// The argument must be a (potentially casted) global variable,
/// and the result will be one, too.
llvm::Constant *
/// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the
/// global variable that has already been created for it. If the initializer
/// has a different type than GV does, this may free GV and return a different
/// one. Otherwise it just returns GV.
llvm::GlobalVariable *
CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
llvm::Constant *addr) {
llvm::Constant *init = CGM.EmitConstantInit(D, this);
llvm::GlobalVariable *var =
cast<llvm::GlobalVariable>(addr->stripPointerCasts());
llvm::GlobalVariable *GV) {
llvm::Constant *Init = CGM.EmitConstantInit(D, this);
// If constant emission failed, then this should be a C++ static
// initializer.
if (!init) {
if (!Init) {
if (!getContext().getLangOpts().CPlusPlus)
CGM.ErrorUnsupported(D.getInit(), "constant l-value expression");
else if (Builder.GetInsertBlock()) {
// Since we have a static initializer, this global variable can't
// be constant.
var->setConstant(false);
GV->setConstant(false);
EmitCXXGuardedInit(D, addr, /*PerformInit*/true);
EmitCXXGuardedInit(D, GV, /*PerformInit*/true);
}
return addr;
return GV;
}
// The initializer may differ in type from the global. Rewrite
// the global to match the initializer. (We have to do this
// because some types, like unions, can't be completely represented
// in the LLVM type system.)
if (var->getType()->getElementType() != init->getType()) {
llvm::GlobalVariable *newVar
= new llvm::GlobalVariable(CGM.getModule(), init->getType(),
var->isConstant(),
var->getLinkage(), init, "",
/*InsertBefore*/ var,
D.isThreadSpecified(),
var->getType()->getAddressSpace());
newVar->setVisibility(var->getVisibility());
if (GV->getType()->getElementType() != Init->getType()) {
llvm::GlobalVariable *OldGV = GV;
GV = new llvm::GlobalVariable(CGM.getModule(), Init->getType(),
OldGV->isConstant(),
OldGV->getLinkage(), Init, "",
/*InsertBefore*/ OldGV,
D.isThreadSpecified(),
CGM.getContext().getTargetAddressSpace(D.getType()));
GV->setVisibility(OldGV->getVisibility());
// Steal the name of the old global
newVar->takeName(var);
GV->takeName(OldGV);
// Replace all uses of the old global with the new global
addr = llvm::ConstantExpr::getBitCast(newVar, addr->getType());
var->replaceAllUsesWith(addr);
llvm::Constant *NewPtrForOldDecl =
llvm::ConstantExpr::getBitCast(GV, OldGV->getType());
OldGV->replaceAllUsesWith(NewPtrForOldDecl);
// Erase the old global, since it is no longer used.
var->eraseFromParent();
var = newVar;
OldGV->eraseFromParent();
}
var->setConstant(CGM.isTypeConstant(D.getType(), true));
var->setInitializer(init);
GV->setConstant(CGM.isTypeConstant(D.getType(), true));
GV->setInitializer(Init);
if (hasNontrivialDestruction(D.getType())) {
// We have a constant initializer, but a nontrivial destructor. We still
// need to perform a guarded "initialization" in order to register the
// destructor.
EmitCXXGuardedInit(D, addr, /*PerformInit*/false);
EmitCXXGuardedInit(D, GV, /*PerformInit*/false);
}
return addr;
return GV;
}
void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D,
llvm::GlobalValue::LinkageTypes Linkage) {
llvm::Value *&DMEntry = LocalDeclMap[&D];
assert(DMEntry == 0 && "Decl already exists in localdeclmap!");
llvm::Constant *addr = CreateStaticVarDecl(D, ".", Linkage);
llvm::GlobalVariable *GV = CreateStaticVarDecl(D, ".", Linkage);
// Store into LocalDeclMap before generating initializer to handle
// circular references.
assert(!LocalDeclMap.count(&D) && "Decl already exists in localdeclmap!");
LocalDeclMap[&D] = addr;
DMEntry = GV;
// We can't have a VLA here, but we can have a pointer to a VLA,
// even though that doesn't really make any sense.
@ -334,34 +287,40 @@ void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D,
// Local static block variables must be treated as globals as they may be
// referenced in their RHS initializer block-literal expresion.
CGM.setStaticLocalDeclAddress(&D, addr);
CGM.setStaticLocalDeclAddress(&D, GV);
// If this value has an initializer, emit it.
// This can leave us with a casted pointer.
if (D.getInit())
addr = AddInitializerToStaticVarDecl(D, addr);
GV = AddInitializerToStaticVarDecl(D, GV);
llvm::GlobalVariable *var =
cast<llvm::GlobalVariable>(addr->stripPointerCasts());
var->setAlignment(getContext().getDeclAlign(&D).getQuantity());
GV->setAlignment(getContext().getDeclAlign(&D).getQuantity());
if (D.hasAttr<AnnotateAttr>())
CGM.AddGlobalAnnotations(&D, var);
CGM.AddGlobalAnnotations(&D, GV);
if (const SectionAttr *SA = D.getAttr<SectionAttr>())
var->setSection(SA->getName());
GV->setSection(SA->getName());
if (D.hasAttr<UsedAttr>())
CGM.AddUsedGlobal(var);
CGM.AddUsedGlobal(GV);
LocalDeclMap[&D] = addr;
CGM.setStaticLocalDeclAddress(&D, addr);
// We may have to cast the constant because of the initializer
// mismatch above.
//
// FIXME: It is really dangerous to store this in the map; if anyone
// RAUW's the GV uses of this constant will be invalid.
llvm::Type *LTy = CGM.getTypes().ConvertTypeForMem(D.getType());
llvm::Type *LPtrTy =
LTy->getPointerTo(CGM.getContext().getTargetAddressSpace(D.getType()));
llvm::Constant *CastedVal = llvm::ConstantExpr::getBitCast(GV, LPtrTy);
DMEntry = CastedVal;
CGM.setStaticLocalDeclAddress(&D, CastedVal);
// Emit global variable debug descriptor for static vars.
CGDebugInfo *DI = getDebugInfo();
if (DI) {
DI->setLocation(D.getLocation());
DI->EmitGlobalVariable(var, &D);
DI->EmitGlobalVariable(static_cast<llvm::GlobalVariable *>(GV), &D);
}
}

Просмотреть файл

@ -179,7 +179,7 @@ CodeGenFunction::EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn,
}
void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D,
llvm::Constant *addr,
llvm::GlobalVariable *DeclPtr,
bool PerformInit) {
// If we've been asked to forbid guard variables, emit an error now.
// This diagnostic is hard-coded for Darwin's use case; we can find
@ -189,7 +189,7 @@ void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D,
"this initialization requires a guard variable, which "
"the kernel does not support");
CGM.getCXXABI().EmitGuardedInit(*this, D, addr, PerformInit);
CGM.getCXXABI().EmitGuardedInit(*this, D, DeclPtr, PerformInit);
}
static llvm::Function *

Просмотреть файл

@ -2392,17 +2392,17 @@ public:
/// CreateStaticVarDecl - Create a zero-initialized LLVM global for
/// a static local variable.
llvm::Constant *CreateStaticVarDecl(const VarDecl &D,
const char *Separator,
llvm::GlobalValue::LinkageTypes Linkage);
llvm::GlobalVariable *CreateStaticVarDecl(const VarDecl &D,
const char *Separator,
llvm::GlobalValue::LinkageTypes Linkage);
/// AddInitializerToStaticVarDecl - Add the initializer for 'D' to
/// the global variable that has already been created for it. If
/// the initializer has a different type than GV does, this may
/// force the underlying variable to change. Otherwise it just
/// returns it.
llvm::Constant *
AddInitializerToStaticVarDecl(const VarDecl &D, llvm::Constant *GV);
/// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the
/// global variable that has already been created for it. If the initializer
/// has a different type than GV does, this may free GV and return a different
/// one. Otherwise it just returns GV.
llvm::GlobalVariable *
AddInitializerToStaticVarDecl(const VarDecl &D,
llvm::GlobalVariable *GV);
/// EmitCXXGlobalVarDeclInit - Create the initializer for a C++
@ -2420,7 +2420,7 @@ public:
/// possible to prove that an initialization will be done exactly
/// once, e.g. with a static local variable or a static data member
/// of a class template.
void EmitCXXGuardedInit(const VarDecl &D, llvm::Constant *addr,
void EmitCXXGuardedInit(const VarDecl &D, llvm::GlobalVariable *DeclPtr,
bool PerformInit);
/// GenerateCXXGlobalInitFunc - Generates code for initializing global

Просмотреть файл

@ -197,9 +197,8 @@ bool CodeGenModule::isTargetDarwin() const {
return getContext().getTargetInfo().getTriple().isOSDarwin();
}
void CodeGenModule::Error(SourceLocation loc, const Twine &error) {
unsigned diagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error,
error.str());
void CodeGenModule::Error(SourceLocation loc, StringRef error) {
unsigned diagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, error);
getDiags().Report(Context.getFullLoc(loc), diagID);
}

Просмотреть файл

@ -733,7 +733,7 @@ public:
llvm::Constant *EmitNullConstantForBase(const CXXRecordDecl *Record);
/// Error - Emit a general error that something can't be done.
void Error(SourceLocation loc, const Twine &error);
void Error(SourceLocation loc, StringRef error);
/// ErrorUnsupported - Print out an error that codegen doesn't support the
/// specified stmt yet.

Просмотреть файл

@ -123,7 +123,7 @@ public:
llvm::Value *&AllocPtr, CharUnits &CookieSize);
void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
llvm::Constant *addr, bool PerformInit);
llvm::GlobalVariable *DeclPtr, bool PerformInit);
};
class ARMCXXABI : public ItaniumCXXABI {
@ -1051,7 +1051,7 @@ static llvm::Constant *getGuardAbortFn(CodeGenModule &CGM,
namespace {
struct CallGuardAbort : EHScopeStack::Cleanup {
llvm::GlobalVariable *Guard;
CallGuardAbort(llvm::GlobalVariable *guard) : Guard(guard) {}
CallGuardAbort(llvm::GlobalVariable *Guard) : Guard(Guard) {}
void Emit(CodeGenFunction &CGF, Flags flags) {
CGF.Builder.CreateCall(getGuardAbortFn(CGF.CGM, Guard->getType()), Guard)
@ -1064,7 +1064,7 @@ namespace {
/// just special-case it at particular places.
void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
const VarDecl &D,
llvm::Constant *varAddr,
llvm::GlobalVariable *GV,
bool PerformInit) {
CGBuilderTy &Builder = CGF.Builder;
@ -1073,60 +1073,35 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
bool threadsafe =
(getContext().getLangOpts().ThreadsafeStatics && D.isLocalVarDecl());
llvm::IntegerType *guardTy;
// Find the underlying global variable for linkage purposes.
// This may not have the right type for actual evaluation purposes.
llvm::GlobalVariable *var =
cast<llvm::GlobalVariable>(varAddr->stripPointerCasts());
llvm::IntegerType *GuardTy;
// If we have a global variable with internal linkage and thread-safe statics
// are disabled, we can just let the guard variable be of type i8.
bool useInt8GuardVariable = !threadsafe && var->hasInternalLinkage();
bool useInt8GuardVariable = !threadsafe && GV->hasInternalLinkage();
if (useInt8GuardVariable) {
guardTy = CGF.Int8Ty;
GuardTy = CGF.Int8Ty;
} else {
// Guard variables are 64 bits in the generic ABI and 32 bits on ARM.
guardTy = (IsARM ? CGF.Int32Ty : CGF.Int64Ty);
GuardTy = (IsARM ? CGF.Int32Ty : CGF.Int64Ty);
}
llvm::PointerType *guardPtrTy = guardTy->getPointerTo();
llvm::PointerType *GuardPtrTy = GuardTy->getPointerTo();
// Create the guard variable.
SmallString<256> guardName;
{
llvm::raw_svector_ostream out(guardName);
getMangleContext().mangleItaniumGuardVariable(&D, out);
out.flush();
}
SmallString<256> GuardVName;
llvm::raw_svector_ostream Out(GuardVName);
getMangleContext().mangleItaniumGuardVariable(&D, Out);
Out.flush();
// There are strange possibilities here involving the
// double-emission of constructors and destructors.
llvm::GlobalVariable *guard = 0;
if (llvm::GlobalValue *existingGuard
= CGM.getModule().getNamedValue(guardName.str())) {
if (isa<llvm::GlobalVariable>(existingGuard) &&
existingGuard->getType() == guardPtrTy) {
guard = cast<llvm::GlobalVariable>(existingGuard); // okay
} else {
CGM.Error(D.getLocation(), "problem emitting static variable '"
+ guardName.str() +
"': already present as different kind of symbol");
// Just absorb linkage and visibility from the variable.
llvm::GlobalVariable *GuardVariable =
new llvm::GlobalVariable(CGM.getModule(), GuardTy,
false, GV->getLinkage(),
llvm::ConstantInt::get(GuardTy, 0),
GuardVName.str());
GuardVariable->setVisibility(GV->getVisibility());
// Fall through and implicitly give it a uniqued name.
}
}
if (!guard) {
// Just absorb linkage and visibility from the variable.
guard = new llvm::GlobalVariable(CGM.getModule(), guardTy,
false, var->getLinkage(),
llvm::ConstantInt::get(guardTy, 0),
guardName.str());
guard->setVisibility(var->getVisibility());
}
// Test whether the variable has completed initialization.
llvm::Value *isInitialized;
llvm::Value *IsInitialized;
// ARM C++ ABI 3.2.3.1:
// To support the potential use of initialization guard variables
@ -1140,9 +1115,9 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
// ...
// }
if (IsARM && !useInt8GuardVariable) {
llvm::Value *V = Builder.CreateLoad(guard);
llvm::Value *V = Builder.CreateLoad(GuardVariable);
V = Builder.CreateAnd(V, Builder.getInt32(1));
isInitialized = Builder.CreateIsNull(V, "guard.uninitialized");
IsInitialized = Builder.CreateIsNull(V, "guard.uninitialized");
// Itanium C++ ABI 3.3.2:
// The following is pseudo-code showing how these functions can be used:
@ -1160,9 +1135,10 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
// }
} else {
// Load the first byte of the guard variable.
llvm::LoadInst *load =
Builder.CreateLoad(Builder.CreateBitCast(guard, CGM.Int8PtrTy));
load->setAlignment(1);
llvm::Type *PtrTy = Builder.getInt8PtrTy();
llvm::LoadInst *LI =
Builder.CreateLoad(Builder.CreateBitCast(GuardVariable, PtrTy));
LI->setAlignment(1);
// Itanium ABI:
// An implementation supporting thread-safety on multiprocessor
@ -1171,16 +1147,16 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
//
// In LLVM, we do this by marking the load Acquire.
if (threadsafe)
load->setAtomic(llvm::Acquire);
LI->setAtomic(llvm::Acquire);
isInitialized = Builder.CreateIsNull(load, "guard.uninitialized");
IsInitialized = Builder.CreateIsNull(LI, "guard.uninitialized");
}
llvm::BasicBlock *InitCheckBlock = CGF.createBasicBlock("init.check");
llvm::BasicBlock *EndBlock = CGF.createBasicBlock("init.end");
// Check if the first byte of the guard variable is zero.
Builder.CreateCondBr(isInitialized, InitCheckBlock, EndBlock);
Builder.CreateCondBr(IsInitialized, InitCheckBlock, EndBlock);
CGF.EmitBlock(InitCheckBlock);
@ -1188,7 +1164,7 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
if (threadsafe) {
// Call __cxa_guard_acquire.
llvm::Value *V
= Builder.CreateCall(getGuardAcquireFn(CGM, guardPtrTy), guard);
= Builder.CreateCall(getGuardAcquireFn(CGM, GuardPtrTy), GuardVariable);
llvm::BasicBlock *InitBlock = CGF.createBasicBlock("init");
@ -1196,22 +1172,22 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
InitBlock, EndBlock);
// Call __cxa_guard_abort along the exceptional edge.
CGF.EHStack.pushCleanup<CallGuardAbort>(EHCleanup, guard);
CGF.EHStack.pushCleanup<CallGuardAbort>(EHCleanup, GuardVariable);
CGF.EmitBlock(InitBlock);
}
// Emit the initializer and add a global destructor if appropriate.
CGF.EmitCXXGlobalVarDeclInit(D, varAddr, PerformInit);
CGF.EmitCXXGlobalVarDeclInit(D, GV, PerformInit);
if (threadsafe) {
// Pop the guard-abort cleanup if we pushed one.
CGF.PopCleanupBlock();
// Call __cxa_guard_release. This cannot throw.
Builder.CreateCall(getGuardReleaseFn(CGM, guardPtrTy), guard);
Builder.CreateCall(getGuardReleaseFn(CGM, GuardPtrTy), GuardVariable);
} else {
Builder.CreateStore(llvm::ConstantInt::get(guardTy, 1), guard);
Builder.CreateStore(llvm::ConstantInt::get(GuardTy, 1), GuardVariable);
}
CGF.EmitBlock(EndBlock);

Просмотреть файл

@ -3,7 +3,6 @@
// CHECK: @_ZZ1hvE1i = internal global i32 0, align 4
// CHECK: @base_req = global [4 x i8] c"foo\00", align 1
// CHECK: @_ZZN5test31BC1EvE1u = internal global { i8, [3 x i8] } { i8 97, [3 x i8] undef }, align 4
// CHECK: @_ZZN5test1L6getvarEiE3var = internal constant [4 x i32] [i32 1, i32 0, i32 2, i32 4], align 16
// CHECK: @_ZZ2h2vE1i = linkonce_odr global i32 0
// CHECK: @_ZGVZ2h2vE1i = linkonce_odr global i64 0
@ -80,73 +79,3 @@ namespace union_static_local {
c::main();
}
}
// rdar://problem/11091093
// Static variables should be consistent across constructor
// or destructor variants.
namespace test2 {
struct A {
A();
~A();
};
struct B : virtual A {
B();
~B();
};
// If we ever implement this as a delegate ctor call, just change
// this to take variadic arguments or something.
extern int foo();
B::B() {
static int x = foo();
}
// CHECK: define void @_ZN5test21BC1Ev
// CHECK: load atomic i8* bitcast (i64* @_ZGVZN5test21BC1EvE1x to i8*) acquire,
// CHECK: call i32 @__cxa_guard_acquire(i64* @_ZGVZN5test21BC1EvE1x)
// CHECK: [[T0:%.*]] = call i32 @_ZN5test23fooEv()
// CHECK: store i32 [[T0]], i32* @_ZZN5test21BC1EvE1x,
// CHECK: call void @__cxa_guard_release(i64* @_ZGVZN5test21BC1EvE1x)
// CHECK: define void @_ZN5test21BC2Ev
// CHECK: load atomic i8* bitcast (i64* @_ZGVZN5test21BC1EvE1x to i8*) acquire,
// CHECK: call i32 @__cxa_guard_acquire(i64* @_ZGVZN5test21BC1EvE1x)
// CHECK: [[T0:%.*]] = call i32 @_ZN5test23fooEv()
// CHECK: store i32 [[T0]], i32* @_ZZN5test21BC1EvE1x,
// CHECK: call void @__cxa_guard_release(i64* @_ZGVZN5test21BC1EvE1x)
// This is just for completeness, because we actually emit this
// using a delegate dtor call.
B::~B() {
static int y = foo();
}
// CHECK: define void @_ZN5test21BD1Ev(
// CHECK: call void @_ZN5test21BD2Ev(
// CHECK: define void @_ZN5test21BD2Ev(
// CHECK: load atomic i8* bitcast (i64* @_ZGVZN5test21BD1EvE1y to i8*) acquire,
// CHECK: call i32 @__cxa_guard_acquire(i64* @_ZGVZN5test21BD1EvE1y)
// CHECK: [[T0:%.*]] = call i32 @_ZN5test23fooEv()
// CHECK: store i32 [[T0]], i32* @_ZZN5test21BD1EvE1y,
// CHECK: call void @__cxa_guard_release(i64* @_ZGVZN5test21BD1EvE1y)
}
// This shouldn't error out.
namespace test3 {
struct A {
A();
~A();
};
struct B : virtual A {
B();
~B();
};
B::B() {
union U { char x; int i; };
static U u = { 'a' };
}
// CHECK: define void @_ZN5test31BC1Ev(
// CHECK: define void @_ZN5test31BC2Ev(
}