diff options
author | Daniel Dunbar <daniel@zuster.org> | 2009-04-15 22:08:45 +0000 |
---|---|---|
committer | Daniel Dunbar <daniel@zuster.org> | 2009-04-15 22:08:45 +0000 |
commit | 03f5ad9a7707e098f601921fcec17ed65eb355a7 (patch) | |
tree | 1bf1048f267fe6b0fa572ddceb7c3b8335c21ccb | |
parent | 6d473967121ac70ecede83bb2b47247e9a3766f3 (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.cpp | 68 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenModule.h | 9 | ||||
-rw-r--r-- | test/CodeGen/tentative-array.c | 4 | ||||
-rw-r--r-- | test/CodeGen/tentative-decls.c | 28 | ||||
-rw-r--r-- | test/PCH/external-defs.c | 2 |
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 |