From: Douglas Gregor Date: Tue, 14 Apr 2009 00:24:19 +0000 (+0000) Subject: When writing a PCH file, keep track of all of the non-static, X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fdd0172ca1b3c837f8c2b37d69cc2085234e09fa;p=clang When writing a PCH file, keep track of all of the non-static, non-inline external definitions (and tentative definitions) that are found at the top level. The corresponding declarations are stored in a record in the PCH file, so that they can be provided to the ASTConsumer (via HandleTopLevelDecl) when the PCH file is read. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69005 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ExternalASTSource.h b/include/clang/AST/ExternalASTSource.h index f8b3c4db4d..54fe3639ad 100644 --- a/include/clang/AST/ExternalASTSource.h +++ b/include/clang/AST/ExternalASTSource.h @@ -18,6 +18,7 @@ #include "llvm/ADT/SmallVector.h" namespace clang { +class ASTConsumer; class Decl; class DeclContext; @@ -83,6 +84,10 @@ public: virtual bool ReadDeclsVisibleInContext(DeclContext *DC, llvm::SmallVectorImpl & Decls) = 0; + /// \brief Function that will be invoked when we begin parsing a new + /// translation unit involving this external AST source. + virtual void StartTranslationUnit(ASTConsumer *Consumer); + /// \brief Print any statistics that have been gathered regarding /// the external AST source. virtual void PrintStats(); diff --git a/include/clang/Frontend/PCHBitCodes.h b/include/clang/Frontend/PCHBitCodes.h index 95916c0dae..bda1370c6f 100644 --- a/include/clang/Frontend/PCHBitCodes.h +++ b/include/clang/Frontend/PCHBitCodes.h @@ -130,7 +130,17 @@ namespace clang { /// between offsets (for unresolved identifier IDs) and /// IdentifierInfo pointers (for already-resolved identifier /// IDs). - IDENTIFIER_TABLE = 6 + IDENTIFIER_TABLE = 6, + + /// \brief Record code for the array of external definitions. + /// + /// The PCH file contains a list of all of the external + /// definitions present within the parsed headers, stored as an + /// array of declaration IDs. These external definitions will be + /// reported to the AST consumer after the PCH file has been + /// read, since their presence can affect the semantics of the + /// program (e.g., for code generation). + EXTERNAL_DEFINITIONS = 7 }; /// \brief Record types used within a source manager block. @@ -300,9 +310,6 @@ namespace clang { TYPE_OBJC_QUALIFIED_CLASS = 24 }; - /// \brief Record code for the offsets of each type. - /// - /// \brief Record codes for each kind of declaration. /// /// These constants describe the records that can occur within a diff --git a/include/clang/Frontend/PCHReader.h b/include/clang/Frontend/PCHReader.h index 6b67f2ef30..6928d26b0c 100644 --- a/include/clang/Frontend/PCHReader.h +++ b/include/clang/Frontend/PCHReader.h @@ -120,6 +120,10 @@ private: /// is set) or is an IdentifierInfo* that has already been resolved. llvm::SmallVector IdentifierData; + /// \brief The set of external definitions stored in the the PCH + /// file. + llvm::SmallVector ExternalDefinitions; + PCHReadResult ReadPCHBlock(); bool CheckPredefinesBuffer(const char *PCHPredef, unsigned PCHPredefLen, @@ -188,6 +192,13 @@ public: virtual bool ReadDeclsVisibleInContext(DeclContext *DC, llvm::SmallVectorImpl & Decls); + /// \brief Function that will be invoked when we begin parsing a new + /// translation unit involving this external AST source. + /// + /// This function will provide all of the external definitions to + /// the ASTConsumer. + virtual void StartTranslationUnit(ASTConsumer *Consumer); + /// \brief Print some statistics about PCH usage. virtual void PrintStats(); diff --git a/include/clang/Frontend/PCHWriter.h b/include/clang/Frontend/PCHWriter.h index a899fb43e6..a887e71899 100644 --- a/include/clang/Frontend/PCHWriter.h +++ b/include/clang/Frontend/PCHWriter.h @@ -21,6 +21,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include +#include namespace llvm { class APInt; @@ -85,6 +86,20 @@ class PCHWriter { /// IdentifierInfo. llvm::DenseMap IdentifierIDs; + /// \brief Declarations encountered that might be external + /// definitions. + /// + /// We keep track of external definitions (as well as tentative + /// definitions) as we are emitting declarations to the PCH + /// file. The PCH file contains a separate record for these external + /// definitions, which are provided to the AST consumer by the PCH + /// reader. This is behavior is required to properly cope with, + /// e.g., tentative variable definitions that occur within + /// headers. The declarations themselves are stored as declaration + /// IDs, since they will be written out to an EXTERNAL_DEFINITIONS + /// record. + llvm::SmallVector ExternalDefinitions; + void WriteTargetTriple(const TargetInfo &Target); void WriteLanguageOptions(const LangOptions &LangOpts); void WriteSourceManagerBlock(SourceManager &SourceMgr); diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp index 328ed841ce..f5f6f5d41c 100644 --- a/lib/Frontend/PCHReader.cpp +++ b/lib/Frontend/PCHReader.cpp @@ -12,8 +12,10 @@ //===----------------------------------------------------------------------===// #include "clang/Frontend/PCHReader.h" #include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclGroup.h" #include "clang/AST/Type.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/Preprocessor.h" @@ -663,6 +665,14 @@ PCHReader::PCHReadResult PCHReader::ReadPCHBlock() { } #endif break; + + case pch::EXTERNAL_DEFINITIONS: + if (!ExternalDefinitions.empty()) { + Error("Duplicate EXTERNAL_DEFINITIONS record in PCH file"); + return Failure; + } + ExternalDefinitions.swap(Record); + break; } } @@ -1276,6 +1286,17 @@ bool PCHReader::ReadDeclsVisibleInContext(DeclContext *DC, return false; } +void PCHReader::StartTranslationUnit(ASTConsumer *Consumer) { + if (!Consumer) + return; + + for (unsigned I = 0, N = ExternalDefinitions.size(); I != N; ++I) { + Decl *D = GetDecl(ExternalDefinitions[I]); + DeclGroupRef DG(D); + Consumer->HandleTopLevelDecl(DG); + } +} + void PCHReader::PrintStats() { std::fprintf(stderr, "*** PCH Statistics:\n"); diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp index 738e5c1879..c7bfa0b74e 100644 --- a/lib/Frontend/PCHWriter.cpp +++ b/lib/Frontend/PCHWriter.cpp @@ -934,8 +934,29 @@ void PCHWriter::WriteDeclsBlock(ASTContext &Context) { W.Code = (pch::DeclCode)0; W.Visit(D); if (DC) W.VisitDeclContext(DC, LexicalOffset, VisibleOffset); - assert(W.Code && "Visitor did not set record code"); + assert(W.Code && "Unhandled declaration kind while generating PCH"); S.EmitRecord(W.Code, Record); + + // Note external declarations so that we can add them to a record + // in the PCH file later. + if (isa(D)) + ExternalDefinitions.push_back(ID); + else if (VarDecl *Var = dyn_cast(D)) { + if (// Non-static file-scope variables with initializers or that + // are tentative definitions. + (Var->isFileVarDecl() && + (Var->getInit() || Var->getStorageClass() == VarDecl::None)) || + // Out-of-line definitions of static data members (C++). + (Var->getDeclContext()->isRecord() && + !Var->getLexicalDeclContext()->isRecord() && + Var->getStorageClass() == VarDecl::Static)) + ExternalDefinitions.push_back(ID); + } else if (FunctionDecl *Func = dyn_cast(D)) { + if (Func->isThisDeclarationADefinition() && + Func->getStorageClass() != FunctionDecl::Static && + !Func->isInline()) + ExternalDefinitions.push_back(ID); + } } // Exit the declarations block @@ -1013,9 +1034,11 @@ void PCHWriter::WritePCH(ASTContext &Context, const Preprocessor &PP) { WritePreprocessor(PP); WriteTypesBlock(Context); WriteDeclsBlock(Context); + WriteIdentifierTable(); S.EmitRecord(pch::TYPE_OFFSET, TypeOffsets); S.EmitRecord(pch::DECL_OFFSET, DeclOffsets); - WriteIdentifierTable(); + if (!ExternalDefinitions.empty()) + S.EmitRecord(pch::EXTERNAL_DEFINITIONS, ExternalDefinitions); S.ExitBlock(); } diff --git a/lib/Sema/ParseAST.cpp b/lib/Sema/ParseAST.cpp index 4bcb478e89..bb5acb0ee8 100644 --- a/lib/Sema/ParseAST.cpp +++ b/lib/Sema/ParseAST.cpp @@ -14,6 +14,7 @@ #include #include "clang/Sema/ParseAST.h" #include "clang/AST/ASTConsumer.h" +#include "clang/AST/ExternalASTSource.h" #include "clang/AST/Stmt.h" #include "Sema.h" #include "clang/Parse/Parser.h" @@ -44,6 +45,9 @@ void clang::ParseAST(Preprocessor &PP, ASTConsumer *Consumer, Consumer->Initialize(Ctx); + if (Ctx.getExternalSource()) + Ctx.getExternalSource()->StartTranslationUnit(Consumer); + Parser::DeclGroupPtrTy ADecl; while (!P.ParseTopLevelDecl(ADecl)) { // Not end of file. diff --git a/test/PCH/external-defs.c b/test/PCH/external-defs.c new file mode 100644 index 0000000000..7be6c73bc7 --- /dev/null +++ b/test/PCH/external-defs.c @@ -0,0 +1,8 @@ +// Test with pch. +// RUN: clang-cc -triple x86_64-apple-darwin9 -emit-pch -o %t.pch %S/external-defs.h && +// RUN: clang-cc -triple x86_64-apple-darwin9 -include-pch %t.pch -emit-llvm -o %t %s && + +// RUN: grep "@x = common global i32 0, align 4" %t | count 1 && +// FIXME below: should be i32 17, but we don't serialize y's value yet +// RUN: grep "@y = common global i32 0, align 4" %t | count 1 && +// RUN: grep "@z" %t | count 0 diff --git a/test/PCH/external-defs.h b/test/PCH/external-defs.h new file mode 100644 index 0000000000..29345e9c69 --- /dev/null +++ b/test/PCH/external-defs.h @@ -0,0 +1,10 @@ +// Helper for external-defs.c test + +// Tentative definition +int x; + +// FIXME: check this, once we actually serialize it +int y = 17; + +// Should not show up +static int z;