Defer generation of tentative definitions.
- PR3980. - <rdar://problem/6762287> [irgen] crash when generating tentative definition of incomplete structure - This also avoids creating common definitions for things which are later overwritten. - XFAIL'ed external-defs.c, it isn't completing types properly yet. llvm-svn: 69231
This commit is contained in:
parent
455b8f4106
commit
7dd749e6fd
|
@ -423,6 +423,11 @@ void CodeGenModule::EmitDeferred() {
|
|||
// Otherwise, emit the definition and move on to the next one.
|
||||
EmitGlobalDefinition(D);
|
||||
}
|
||||
|
||||
// Emit any tentative definitions.
|
||||
for (std::vector<const VarDecl*>::iterator it = TentativeDefinitions.begin(),
|
||||
ie = TentativeDefinitions.end(); it != ie; ++it)
|
||||
EmitTentativeDefinition(*it);
|
||||
}
|
||||
|
||||
/// EmitAnnotateAttr - Generate the llvm::ConstantStruct which contains the
|
||||
|
@ -512,9 +517,16 @@ void CodeGenModule::EmitGlobal(const ValueDecl *Global) {
|
|||
const VarDecl *VD = cast<VarDecl>(Global);
|
||||
assert(VD->isFileVarDecl() && "Cannot emit local var decl as global.");
|
||||
|
||||
// Forward declarations are emitted lazily on first use.
|
||||
if (!VD->getInit() && VD->hasExternalStorage())
|
||||
// If this isn't a definition, defer code generation.
|
||||
if (!VD->getInit()) {
|
||||
// If this is a tentative definition, remember it so that we can
|
||||
// emit the common definition if needed. It is important to
|
||||
// defer tentative definitions, since they may have incomplete
|
||||
// type.
|
||||
if (!VD->hasExternalStorage())
|
||||
TentativeDefinitions.push_back(VD);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Defer code generation when possible if this is a static definition, inline
|
||||
|
@ -708,21 +720,35 @@ CodeGenModule::CreateRuntimeVariable(const llvm::Type *Ty,
|
|||
return GetOrCreateLLVMGlobal(Name, llvm::PointerType::getUnqual(Ty), 0);
|
||||
}
|
||||
|
||||
void CodeGenModule::EmitTentativeDefinition(const VarDecl *D) {
|
||||
assert(!D->getInit() && "Cannot emit definite definitions here!");
|
||||
|
||||
// See if we have already defined this (as a variable), if so we do
|
||||
// not need to do anything.
|
||||
llvm::GlobalValue *GV = GlobalDeclMap[getMangledName(D)];
|
||||
if (llvm::GlobalVariable *Var = dyn_cast_or_null<llvm::GlobalVariable>(GV))
|
||||
if (Var->hasInitializer())
|
||||
return;
|
||||
|
||||
EmitGlobalVarDefinition(D);
|
||||
}
|
||||
|
||||
void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
|
||||
llvm::Constant *Init = 0;
|
||||
QualType ASTTy = D->getType();
|
||||
|
||||
if (D->getInit() == 0) {
|
||||
// This is a tentative definition; tentative definitions are
|
||||
// implicitly initialized with { 0 }
|
||||
const llvm::Type *InitTy = getTypes().ConvertTypeForMem(ASTTy);
|
||||
if (ASTTy->isIncompleteArrayType()) {
|
||||
// An incomplete array is normally [ TYPE x 0 ], but we need
|
||||
// to fix it to [ TYPE x 1 ].
|
||||
const llvm::ArrayType* ATy = cast<llvm::ArrayType>(InitTy);
|
||||
InitTy = llvm::ArrayType::get(ATy->getElementType(), 1);
|
||||
}
|
||||
Init = llvm::Constant::getNullValue(InitTy);
|
||||
// implicitly initialized with { 0 }.
|
||||
//
|
||||
// Note that tentative definitions are only emitted at the end of
|
||||
// a translation unit, so they should never have incomplete
|
||||
// type. In addition, EmitTentativeDefinition makes sure that we
|
||||
// never attempt to emit a tentative definition if a real one
|
||||
// exists. A use may still exists, however, so we still may need
|
||||
// to do a RAUW.
|
||||
assert(!ASTTy->isIncompleteType() && "Unexpected incomplete type");
|
||||
Init = llvm::Constant::getNullValue(getTypes().ConvertTypeForMem(ASTTy));
|
||||
} else {
|
||||
Init = EmitConstantExpr(D->getInit(), D->getType());
|
||||
if (!Init) {
|
||||
|
@ -744,26 +770,6 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
|
|||
// Entry is now either a Function or GlobalVariable.
|
||||
llvm::GlobalVariable *GV = dyn_cast<llvm::GlobalVariable>(Entry);
|
||||
|
||||
// If we already have this global and it has an initializer, then
|
||||
// we are in the rare situation where we emitted the defining
|
||||
// declaration of the global and are now being asked to emit a
|
||||
// definition which would be common. This occurs, for example, in
|
||||
// the following situation because statics can be emitted out of
|
||||
// order:
|
||||
//
|
||||
// static int x;
|
||||
// static int *y = &x;
|
||||
// static int x = 10;
|
||||
// int **z = &y;
|
||||
//
|
||||
// Bail here so we don't blow away the definition. Note that if we
|
||||
// can't distinguish here if we emitted a definition with a null
|
||||
// initializer, but this case is safe.
|
||||
if (GV && GV->hasInitializer() && !GV->getInitializer()->isNullValue()) {
|
||||
assert(!D->getInit() && "Emitting multiple definitions of a decl!");
|
||||
return;
|
||||
}
|
||||
|
||||
// We have a definition after a declaration with the wrong type.
|
||||
// We must make a new GlobalVariable* and update everything that used OldGV
|
||||
// (a declaration or tentative definition) with the new GlobalVariable*
|
||||
|
|
|
@ -117,6 +117,14 @@ class CodeGenModule : public BlockModule {
|
|||
/// that *are* actually referenced. These get code generated when the module
|
||||
/// is done.
|
||||
std::vector<const ValueDecl*> DeferredDeclsToEmit;
|
||||
|
||||
/// TentativeDefinitions - A list of declarations which are
|
||||
/// tentative definitions. Code generation for these must be
|
||||
/// deferred because they are allowed to have incomplete type when
|
||||
/// they are seen. This also allows us to avoid generating an extra
|
||||
/// common definiton in situations where the tentative definition is
|
||||
/// followed by an actual definition.
|
||||
std::vector<const VarDecl*> TentativeDefinitions;
|
||||
|
||||
/// LLVMUsed - List of global values which are required to be
|
||||
/// present in the object file; bitcast to i8*. This is used for
|
||||
|
@ -357,6 +365,7 @@ private:
|
|||
void EmitGlobalDefinition(const ValueDecl *D);
|
||||
|
||||
void EmitGlobalFunctionDefinition(const FunctionDecl *D);
|
||||
void EmitTentativeDefinition(const VarDecl *D);
|
||||
void EmitGlobalVarDefinition(const VarDecl *D);
|
||||
void EmitAliasDefinition(const ValueDecl *D);
|
||||
void EmitObjCPropertyImplementations(const ObjCImplementationDecl *D);
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
// RUN: clang-cc -emit-llvm < %s -triple=i686-apple-darwin9 | grep "global \[1 x i32\]"
|
||||
|
||||
int r[];
|
||||
int (*a)[] = &r;
|
|
@ -0,0 +1,28 @@
|
|||
// RUN: clang-cc -emit-llvm -o %t %s &&
|
||||
|
||||
// RUN: grep '@r = common global \[1 x .*\] zeroinitializer' %t &&
|
||||
|
||||
int r[];
|
||||
int (*a)[] = &r;
|
||||
|
||||
struct s0;
|
||||
struct s0 x;
|
||||
// RUN: grep '@x = common global .struct.s0 zeroinitializer' %t &&
|
||||
|
||||
struct s0 y;
|
||||
// RUN: grep '@y = common global .struct.s0 zeroinitializer' %t &&
|
||||
struct s0 *f0() {
|
||||
return &y;
|
||||
}
|
||||
|
||||
struct s0 {
|
||||
int x;
|
||||
};
|
||||
|
||||
// RUN: grep '@b = common global \[1 x .*\] zeroinitializer' %t &&
|
||||
int b[];
|
||||
int *f1() {
|
||||
return b;
|
||||
}
|
||||
|
||||
// RUN: true
|
|
@ -19,3 +19,5 @@ int incomplete_array3[];
|
|||
struct S {
|
||||
int x, y;
|
||||
};
|
||||
|
||||
// XFAIL
|
||||
|
|
Loading…
Reference in New Issue