aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Dunbar <daniel@zuster.org>2009-04-15 22:08:45 +0000
committerDaniel Dunbar <daniel@zuster.org>2009-04-15 22:08:45 +0000
commit03f5ad9a7707e098f601921fcec17ed65eb355a7 (patch)
tree1bf1048f267fe6b0fa572ddceb7c3b8335c21ccb
parent6d473967121ac70ecede83bb2b47247e9a3766f3 (diff)
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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69231 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/CodeGen/CodeGenModule.cpp68
-rw-r--r--lib/CodeGen/CodeGenModule.h9
-rw-r--r--test/CodeGen/tentative-array.c4
-rw-r--r--test/CodeGen/tentative-decls.c28
-rw-r--r--test/PCH/external-defs.c2
5 files changed, 76 insertions, 35 deletions
diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp
index a62145ce29..81bff667fb 100644
--- a/lib/CodeGen/CodeGenModule.cpp
+++ b/lib/CodeGen/CodeGenModule.cpp
@@ -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*
diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h
index a1fc715344..e7a587fc8c 100644
--- a/lib/CodeGen/CodeGenModule.h
+++ b/lib/CodeGen/CodeGenModule.h
@@ -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);
diff --git a/test/CodeGen/tentative-array.c b/test/CodeGen/tentative-array.c
deleted file mode 100644
index 6879d0f07d..0000000000
--- a/test/CodeGen/tentative-array.c
+++ /dev/null
@@ -1,4 +0,0 @@
-// RUN: clang-cc -emit-llvm < %s -triple=i686-apple-darwin9 | grep "global \[1 x i32\]"
-
-int r[];
-int (*a)[] = &r;
diff --git a/test/CodeGen/tentative-decls.c b/test/CodeGen/tentative-decls.c
new file mode 100644
index 0000000000..4604f2f429
--- /dev/null
+++ b/test/CodeGen/tentative-decls.c
@@ -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
diff --git a/test/PCH/external-defs.c b/test/PCH/external-defs.c
index 6a46f45cc1..251a10d9ce 100644
--- a/test/PCH/external-defs.c
+++ b/test/PCH/external-defs.c
@@ -19,3 +19,5 @@ int incomplete_array3[];
struct S {
int x, y;
};
+
+// XFAIL