diff options
-rw-r--r-- | clang/include/clang/AST/ASTContext.h | 14 | ||||
-rw-r--r-- | clang/include/clang/AST/DeclBase.h | 43 | ||||
-rw-r--r-- | clang/include/clang/AST/DeclContextInternals.h | 170 | ||||
-rw-r--r-- | clang/include/clang/AST/ExternalASTSource.h | 94 | ||||
-rw-r--r-- | clang/include/clang/Frontend/PCHBitCodes.h | 239 | ||||
-rw-r--r-- | clang/include/clang/Frontend/PCHReader.h | 169 | ||||
-rw-r--r-- | clang/include/clang/Frontend/PCHWriter.h | 114 | ||||
-rw-r--r-- | clang/lib/AST/ASTContext.cpp | 18 | ||||
-rw-r--r-- | clang/lib/AST/DeclBase.cpp | 112 | ||||
-rw-r--r-- | clang/lib/Frontend/PCHReader.cpp | 602 | ||||
-rw-r--r-- | clang/lib/Frontend/PCHWriter.cpp | 636 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 6 | ||||
-rw-r--r-- | clang/lib/Sema/SemaLookup.cpp | 11 | ||||
-rw-r--r-- | clang/test/PCH/variables.c | 6 | ||||
-rw-r--r-- | clang/test/PCH/variables.h | 5 | ||||
-rwxr-xr-x | clang/test/TestRunner.sh | 3 | ||||
-rw-r--r-- | clang/tools/clang-cc/ASTConsumers.h | 5 | ||||
-rw-r--r-- | clang/tools/clang-cc/GeneratePCH.cpp | 78 | ||||
-rw-r--r-- | clang/tools/clang-cc/clang-cc.cpp | 23 |
19 files changed, 2300 insertions, 48 deletions
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 873806ffd2d..ee9f99c57ed 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -24,6 +24,7 @@ #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/OwningPtr.h" #include "llvm/Bitcode/SerializationFwd.h" #include "llvm/Support/Allocator.h" #include <vector> @@ -36,6 +37,7 @@ namespace clang { class FileManager; class ASTRecordLayout; class Expr; + class ExternalASTSource; class IdentifierTable; class SelectorTable; class SourceManager; @@ -146,6 +148,7 @@ public: IdentifierTable &Idents; SelectorTable &Selectors; DeclarationNameTable DeclarationNames; + llvm::OwningPtr<ExternalASTSource> ExternalSource; SourceManager& getSourceManager() { return SourceMgr; } const SourceManager& getSourceManager() const { return SourceMgr; } @@ -190,6 +193,17 @@ public: ~ASTContext(); + /// \brief Attach an external AST source to the AST context. + /// + /// The external AST source provides the ability to load parts of + /// the abstract syntax tree as needed from some external storage, + /// e.g., a precompiled header. + void setExternalSource(llvm::OwningPtr<ExternalASTSource> &Source); + + /// \brief Retrieve a pointer to the external AST source associated + /// with this AST context, if any. + ExternalASTSource *getExternalSource() const { return ExternalSource.get(); } + void PrintStats() const; const std::vector<Type*>& getTypes() const { return Types; } diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 3244c631466..46af4ec6b60 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -349,24 +349,36 @@ class DeclContext { /// DeclKind - This indicates which class this is. Decl::Kind DeclKind : 8; + /// \brief Whether this declaration context also has some external + /// storage that contains additional declarations that are lexically + /// part of this context. + mutable bool ExternalLexicalStorage : 1; + + /// \brief Whether this declaration context also has some external + /// storage that contains additional declarations that are visible + /// in this context. + mutable bool ExternalVisibleStorage : 1; + /// \brief Pointer to the data structure used to lookup declarations /// within this context, which is a DenseMap<DeclarationName, /// StoredDeclsList>. - void* LookupPtr; + mutable void* LookupPtr; /// FirstDecl - The first declaration stored within this declaration /// context. - Decl *FirstDecl; + mutable Decl *FirstDecl; /// LastDecl - The last declaration stored within this declaration /// context. FIXME: We could probably cache this value somewhere /// outside of the DeclContext, to reduce the size of DeclContext by /// another pointer. - Decl *LastDecl; + mutable Decl *LastDecl; protected: DeclContext(Decl::Kind K) - : DeclKind(K), LookupPtr(0), FirstDecl(0), LastDecl(0) { } + : DeclKind(K), ExternalLexicalStorage(false), + ExternalVisibleStorage(false), LookupPtr(0), FirstDecl(0), + LastDecl(0) { } void DestroyDecls(ASTContext &C); @@ -751,6 +763,26 @@ public: /// \brief Retrieve the internal representation of the lookup structure. void* getLookupPtr() const { return LookupPtr; } + /// \brief Whether this DeclContext has external storage containing + /// additional declarations that are lexically in this context. + bool hasExternalLexicalStorage() const { return ExternalLexicalStorage; } + + /// \brief State whether this DeclContext has external storage for + /// declarations lexically in this context. + void setHasExternalLexicalStorage(bool ES = true) { + ExternalLexicalStorage = ES; + } + + /// \brief Whether this DeclContext has external storage containing + /// additional declarations that are visible in this context. + bool hasExternalVisibleStorage() const { return ExternalVisibleStorage; } + + /// \brief State whether this DeclContext has external storage for + /// declarations visible in this context. + void setHasExternalVisibleStorage(bool ES = true) { + ExternalVisibleStorage = ES; + } + static bool classof(const Decl *D); static bool classof(const DeclContext *D) { return true; } #define DECL_CONTEXT(Name) \ @@ -758,6 +790,9 @@ public: #include "clang/AST/DeclNodes.def" private: + void LoadLexicalDeclsFromExternalStorage(ASTContext &Context) const; + void LoadVisibleDeclsFromExternalStorage(ASTContext &Context) const; + void buildLookup(ASTContext &Context, DeclContext *DCtx); void makeDeclVisibleInContextImpl(ASTContext &Context, NamedDecl *D); diff --git a/clang/include/clang/AST/DeclContextInternals.h b/clang/include/clang/AST/DeclContextInternals.h index 9341a7de370..7489413be89 100644 --- a/clang/include/clang/AST/DeclContextInternals.h +++ b/clang/include/clang/AST/DeclContextInternals.h @@ -19,54 +19,128 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallVector.h" -#include <functional> +#include <algorithm> namespace clang { /// StoredDeclsList - This is an array of decls optimized a common case of only /// containing one entry. struct StoredDeclsList { + /// The kind of data encoded in this list. + enum DataKind { + /// \brief The data is a NamedDecl*. + DK_Decl = 0, + /// \brief The data is a declaration ID (an unsigned value), + /// shifted left by 2 bits. + DK_DeclID = 1, + /// \brief The data is a pointer to a vector (of type VectorTy) + /// that contains declarations. + DK_Decl_Vector = 2, + /// \brief The data is a pointer to a vector (of type VectorTy) + /// that contains declaration ID. + DK_ID_Vector = 3 + }; + /// VectorTy - When in vector form, this is what the Data pointer points to. - typedef llvm::SmallVector<NamedDecl*, 4> VectorTy; + typedef llvm::SmallVector<uintptr_t, 4> VectorTy; + + /// \brief The stored data, which will be either a declaration ID, a + /// pointer to a NamedDecl, or a pointer to a vector. + uintptr_t Data; - /// Data - Union of NamedDecl*/VectorTy*. - llvm::PointerUnion<NamedDecl*, VectorTy*> Data; public: - StoredDeclsList() {} + StoredDeclsList() : Data(0) {} + StoredDeclsList(const StoredDeclsList &RHS) : Data(RHS.Data) { - if (isVector()) - Data = new VectorTy(*Data.get<VectorTy*>()); + if (VectorTy *RHSVec = RHS.getAsVector()) { + VectorTy *New = new VectorTy(*RHSVec); + Data = reinterpret_cast<uintptr_t>(New) | (Data & 0x03); + } } ~StoredDeclsList() { // If this is a vector-form, free the vector. - if (isVector()) - delete Data.get<VectorTy*>(); + if (VectorTy *Vector = getAsVector()) + delete Vector; } StoredDeclsList &operator=(const StoredDeclsList &RHS) { - if (isVector()) - delete Data.get<VectorTy*>(); + if (VectorTy *Vector = getAsVector()) + delete Vector; Data = RHS.Data; - if (isVector()) - Data = new VectorTy(*Data.get<VectorTy*>()); + if (VectorTy *RHSVec = RHS.getAsVector()) { + VectorTy *New = new VectorTy(*RHSVec); + Data = reinterpret_cast<uintptr_t>(New) | (Data & 0x03); + } return *this; } - bool isVector() const { return Data.is<VectorTy*>(); } - bool isInline() const { return Data.is<NamedDecl*>(); } - bool isNull() const { return Data.isNull(); } + bool isNull() const { return (Data & ~0x03) == 0; } + NamedDecl *getAsDecl() const { + if ((Data & 0x03) != DK_Decl) + return 0; + + return reinterpret_cast<NamedDecl *>(Data & ~0x03); + } + + VectorTy *getAsVector() const { + if ((Data & 0x03) != DK_ID_Vector && (Data & 0x03) != DK_Decl_Vector) + return 0; + + return reinterpret_cast<VectorTy *>(Data & ~0x03); + } + void setOnlyValue(NamedDecl *ND) { - assert(isInline() && "Not inline"); - Data = ND; + assert(!getAsVector() && "Not inline"); + Data = reinterpret_cast<uintptr_t>(ND); + } + + void setFromDeclIDs(const llvm::SmallVectorImpl<unsigned> &Vec) { + if (Vec.size() > 1) { + VectorTy *Vector = getAsVector(); + if (!Vector) { + Vector = new VectorTy; + Data = reinterpret_cast<uintptr_t>(Vector) | DK_Decl_Vector; + } + + Vector->resize(Vec.size()); + std::copy(Vec.begin(), Vec.end(), Vector->begin()); + return; + } + + if (VectorTy *Vector = getAsVector()) + delete Vector; + + if (Vec.size() == 0) + Data = 0; + else + Data = (Vec[0] << 2) | DK_DeclID; + } + + /// \brief Force the stored declarations list to contain actual + /// declarations. + /// + /// This routine will resolve any declaration IDs for declarations + /// that may not yet have been loaded from external storage. + void materializeDecls(ASTContext &Context); + + bool hasDeclarationIDs() const { + DataKind DK = (DataKind)(Data & 0x03); + return DK == DK_DeclID || DK == DK_ID_Vector; } /// getLookupResult - Return an array of all the decls that this list /// represents. DeclContext::lookup_result getLookupResult(ASTContext &Context) { - // If we have a single inline unit, return it. - if (isInline()) { + if (isNull()) + return DeclContext::lookup_result(0, 0); + + if (hasDeclarationIDs()) + materializeDecls(Context); + + // If we have a single NamedDecl, return it. + if (getAsDecl()) { assert(!isNull() && "Empty list isn't allowed"); // Data is a raw pointer to a NamedDecl*, return it. @@ -74,57 +148,67 @@ public: return DeclContext::lookup_result((NamedDecl**)Ptr, (NamedDecl**)Ptr+1); } + assert(getAsVector() && "Must have a vector at this point"); + VectorTy &Vector = *getAsVector(); + // Otherwise, we have a range result. - VectorTy &V = *Data.get<VectorTy*>(); - return DeclContext::lookup_result(&V[0], &V[0]+V.size()); + return DeclContext::lookup_result((NamedDecl **)&Vector[0], + (NamedDecl **)&Vector[0]+Vector.size()); } /// HandleRedeclaration - If this is a redeclaration of an existing decl, /// replace the old one with D and return true. Otherwise return false. bool HandleRedeclaration(ASTContext &Context, NamedDecl *D) { + if (hasDeclarationIDs()) + materializeDecls(Context); + // Most decls only have one entry in their list, special case it. - if (isInline()) { - if (!D->declarationReplaces(Data.get<NamedDecl*>())) + if (NamedDecl *OldD = getAsDecl()) { + if (!D->declarationReplaces(OldD)) return false; setOnlyValue(D); return true; } // Determine if this declaration is actually a redeclaration. - VectorTy &Vec = *Data.get<VectorTy*>(); - VectorTy::iterator RDI - = std::find_if(Vec.begin(), Vec.end(), - std::bind1st(std::mem_fun(&NamedDecl::declarationReplaces), - D)); - if (RDI == Vec.end()) - return false; - *RDI = D; - return true; + VectorTy &Vec = *getAsVector(); + for (VectorTy::iterator OD = Vec.begin(), ODEnd = Vec.end(); + OD != ODEnd; ++OD) { + NamedDecl *OldD = reinterpret_cast<NamedDecl *>(*OD); + if (D->declarationReplaces(OldD)) { + *OD = reinterpret_cast<uintptr_t>(D); + return true; + } + } + + return false; } /// AddSubsequentDecl - This is called on the second and later decl when it is /// not a redeclaration to merge it into the appropriate place in our list. /// void AddSubsequentDecl(NamedDecl *D) { + assert(!hasDeclarationIDs() && "Must materialize before adding decls"); + // If this is the second decl added to the list, convert this to vector // form. - if (isInline()) { - NamedDecl *OldD = Data.get<NamedDecl*>(); + if (NamedDecl *OldD = getAsDecl()) { VectorTy *VT = new VectorTy(); - VT->push_back(OldD); - Data = VT; + VT->push_back(reinterpret_cast<uintptr_t>(OldD)); + Data = reinterpret_cast<uintptr_t>(VT) | DK_Decl_Vector; } - VectorTy &Vec = *Data.get<VectorTy*>(); + VectorTy &Vec = *getAsVector(); if (isa<UsingDirectiveDecl>(D) || D->getIdentifierNamespace() == Decl::IDNS_Tag) - Vec.push_back(D); - else if (Vec.back()->getIdentifierNamespace() == Decl::IDNS_Tag) { - NamedDecl *TagD = Vec.back(); - Vec.back() = D; + Vec.push_back(reinterpret_cast<uintptr_t>(D)); + else if (reinterpret_cast<NamedDecl *>(Vec.back()) + ->getIdentifierNamespace() == Decl::IDNS_Tag) { + uintptr_t TagD = Vec.back(); + Vec.back() = reinterpret_cast<uintptr_t>(D); Vec.push_back(TagD); } else - Vec.push_back(D); + Vec.push_back(reinterpret_cast<uintptr_t>(D)); } }; diff --git a/clang/include/clang/AST/ExternalASTSource.h b/clang/include/clang/AST/ExternalASTSource.h new file mode 100644 index 00000000000..17fa72612ff --- /dev/null +++ b/clang/include/clang/AST/ExternalASTSource.h @@ -0,0 +1,94 @@ +//===--- ExternalASTSource.h - Abstract External AST Interface --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ExternalASTSource interface, +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_AST_EXTERNAL_AST_SOURCE_H +#define LLVM_CLANG_AST_EXTERNAL_AST_SOURCE_H + +#include "clang/AST/DeclarationName.h" +#include "clang/AST/Type.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang { + +class Decl; +class DeclContext; + +/// \brief The deserialized representation of a set of declarations +/// with the same name that are visible in a given context. +struct VisibleDeclaration { + /// \brief The name of the declarations. + DeclarationName Name; + + /// \brief The ID numbers of all of the declarations with this name. + /// + /// These declarations have not necessarily been de-serialized. + llvm::SmallVector<unsigned, 4> Declarations; +}; + +/// \brief Abstract interface for external sources of AST nodes. +/// +/// External AST sources provide AST nodes constructed from some +/// external source, such as a precompiled header. External AST +/// sources can resolve types and declarations from abstract IDs into +/// actual type and declaration nodes, and read parts of declaration +/// contexts. +class ExternalASTSource { +public: + virtual ~ExternalASTSource(); + + /// \brief Resolve a type ID into a type, potentially building a new + /// type. + virtual QualType GetType(unsigned ID) = 0; + + /// \brief Resolve a declaration ID into a declaration, potentially + /// building a new declaration. + virtual Decl *GetDecl(unsigned ID) = 0; + + /// \brief Read all of the declarations lexically stored in a + /// declaration context. + /// + /// \param DC The declaration context whose declarations will be + /// read. + /// + /// \param Decls Vector that will contain the declarations loaded + /// from the external source. The caller is responsible for merging + /// these declarations with any declarations already stored in the + /// declaration context. + /// + /// \returns true if there was an error while reading the + /// declarations for this declaration context. + virtual bool ReadDeclsLexicallyInContext(DeclContext *DC, + llvm::SmallVectorImpl<unsigned> &Decls) = 0; + + /// \brief Read all of the declarations visible from a declaration + /// context. + /// + /// \param DC The declaration context whose visible declarations + /// will be read. + /// + /// \param Decls A vector of visible declaration structures, + /// providing the mapping from each name visible in the declaration + /// context to the declaration IDs of declarations with that name. + /// + /// \returns true if there was an error while reading the + /// declarations for this declaration context. + virtual bool ReadDeclsVisibleInContext(DeclContext *DC, + llvm::SmallVectorImpl<VisibleDeclaration> & Decls) = 0; + + /// \brief Print any statistics that have been gathered regarding + /// the external AST source. + virtual void PrintStats(); +}; + +} // end namespace clang + +#endif // LLVM_CLANG_AST_EXTERNAL_AST_SOURCE_H diff --git a/clang/include/clang/Frontend/PCHBitCodes.h b/clang/include/clang/Frontend/PCHBitCodes.h new file mode 100644 index 00000000000..88350983619 --- /dev/null +++ b/clang/include/clang/Frontend/PCHBitCodes.h @@ -0,0 +1,239 @@ +//===- PCHBitCodes.h - Enum values for the PCH bitcode format ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This header defines Bitcode enum values for Clang precompiled header files. +// +// The enum values defined in this file should be considered permanent. If +// new features are added, they should have values added at the end of the +// respective lists. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_FRONTEND_PCHBITCODES_H +#define LLVM_CLANG_FRONTEND_PCHBITCODES_H + +#include "llvm/Bitcode/BitCodes.h" +#include "llvm/Support/DataTypes.h" + +namespace clang { + namespace pch { + const int IDBits = 32; + typedef uint32_t ID; + + /// \brief Describes the various kinds of blocks that occur within + /// a PCH file. + enum BlockIDs { + /// \brief The PCH block, which acts as a container around the + /// full PCH block. + PCH_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID, + + /// \brief The block containing the definitions of all of the + /// types used within the PCH file. + TYPES_BLOCK_ID, + + /// \brief The block containing the offsets of all of the types + /// used within the PCH. + /// + /// The offsets in this block point into the block identified by + /// TYPES_BLOCK_ID, and are indexed by the type ID. + TYPE_OFFSETS_BLOCK_ID, + + /// \brief The block containing the definitions of all of the + /// declarations stored in the PCH file. + DECLS_BLOCK_ID, + + /// \brief The block containing the offsets of all of the + /// declarations stored within the PCH file. + /// + /// The offsets in this block point into the block identified by + /// DECLS_BLOCK_ID, and are indexed by the decaration ID. + DECL_OFFSETS_BLOCK_ID + }; + + /// \brief Predefined type IDs. + /// + /// These type IDs correspond to predefined types in the AST + /// context, such as built-in types (int) and special place-holder + /// types (the <overload> and <dependent> type markers). Such + /// types are never actually serialized, since they will be built + /// by the AST context when it is created. + enum PredefinedTypeIDs { + /// \brief The NULL type. + PREDEF_TYPE_NULL_ID = 0, + /// \brief The void type. + PREDEF_TYPE_VOID_ID = 1, + /// \brief The 'bool' or '_Bool' type. + PREDEF_TYPE_BOOL_ID = 2, + /// \brief The 'char' type, when it is unsigned. + PREDEF_TYPE_CHAR_U_ID = 3, + /// \brief The 'unsigned char' type. + PREDEF_TYPE_UCHAR_ID = 4, + /// \brief The 'unsigned short' type. + PREDEF_TYPE_USHORT_ID = 5, + /// \brief The 'unsigned int' type. + PREDEF_TYPE_UINT_ID = 6, + /// \brief The 'unsigned long' type. + PREDEF_TYPE_ULONG_ID = 7, + /// \brief The 'unsigned long long' type. + PREDEF_TYPE_ULONGLONG_ID = 8, + /// \brief The 'char' type, when it is signed. + PREDEF_TYPE_CHAR_S_ID = 9, + /// \brief The 'signed char' type. + PREDEF_TYPE_SCHAR_ID = 10, + /// \brief The C++ 'wchar_t' type. + PREDEF_TYPE_WCHAR_ID = 11, + /// \brief The (signed) 'short' type. + PREDEF_TYPE_SHORT_ID = 12, + /// \brief The (signed) 'int' type. + PREDEF_TYPE_INT_ID = 13, + /// \brief The (signed) 'long' type. + PREDEF_TYPE_LONG_ID = 14, + /// \brief The (signed) 'long long' type. + PREDEF_TYPE_LONGLONG_ID = 15, + /// \brief The 'float' type. + PREDEF_TYPE_FLOAT_ID = 16, + /// \brief The 'double' type. + PREDEF_TYPE_DOUBLE_ID = 17, + /// \brief The 'long double' type. + PREDEF_TYPE_LONGDOUBLE_ID = 18, + /// \brief The placeholder type for overloaded function sets. + PREDEF_TYPE_OVERLOAD_ID = 19, + /// \brief The placeholder type for dependent types. + PREDEF_TYPE_DEPENDENT_ID = 20 + }; + + /// \brief The number of predefined type IDs that are reserved for + /// the PREDEF_TYPE_* constants. + /// + /// Type IDs for non-predefined types will start at + /// NUM_PREDEF_TYPE_IDs. + const unsigned NUM_PREDEF_TYPE_IDS = 100; + + /// \brief Record codes for each kind of type. + /// + /// These constants describe the records that can occur within a + /// block identified by TYPES_BLOCK_ID in the PCH file. Each + /// constant describes a record for a specific type class in the + /// AST. + enum TypeCode { + /// \brief An ExtQualType record. + TYPE_EXT_QUAL = 1, + /// \brief A FixedWidthIntType record. + TYPE_FIXED_WIDTH_INT = 2, + /// \brief A ComplexType record. + TYPE_COMPLEX = 3, + /// \brief A PointerType record. + TYPE_POINTER = 4, + /// \brief A BlockPointerType record. + TYPE_BLOCK_POINTER = 5, + /// \brief An LValueReferenceType record. + TYPE_LVALUE_REFERENCE = 6, + /// \brief An RValueReferenceType record. + TYPE_RVALUE_REFERENCE = 7, + /// \brief A MemberPointerType record. + TYPE_MEMBER_POINTER = 8, + /// \brief A ConstantArrayType record. + TYPE_CONSTANT_ARRAY = 9, + /// \brief An IncompleteArrayType record. + TYPE_INCOMPLETE_ARRAY = 10, + /// \brief A VariableArrayType record. + TYPE_VARIABLE_ARRAY = 11, + /// \brief A VectorType record. + TYPE_VECTOR = 12, + /// \brief An ExtVectorType record. + TYPE_EXT_VECTOR = 13, + /// \brief A FunctionNoProtoType record. + TYPE_FUNCTION_NO_PROTO = 14, + /// \brief A FunctionProtoType record. + TYPE_FUNCTION_PROTO = 15, + /// \brief A TypedefType record. + TYPE_TYPEDEF = 16, + /// \brief A TypeOfExprType record. + TYPE_TYPEOF_EXPR = 17, + /// \brief A TypeOfType record. + TYPE_TYPEOF = 18, + /// \brief A RecordType record. + TYPE_RECORD = 19, + /// \brief An EnumType record. + TYPE_ENUM = 20, + /// \brief An ObjCInterfaceType record. + TYPE_OBJC_INTERFACE = 21, + /// \brief An ObjCQualifiedInterfaceType record. + TYPE_OBJC_QUALIFIED_INTERFACE = 22, + /// \brief An ObjCQualifiedIdType record. + TYPE_OBJC_QUALIFIED_ID = 23, + /// \brief An ObjCQualifiedClassType record. + TYPE_OBJC_QUALIFIED_CLASS = 24 + }; + + /// \brief Record code for the offsets of each type. + /// + /// The TYPE_OFFSET constant describes the record that occurs + /// within the block identified by TYPE_OFFSETS_BLOCK_ID within + /// the PCH file. The record itself is an array of offsets that + /// point into the types block (identified by TYPES_BLOCK_ID in + /// the PCH file). The index into the array is based on the ID of + /// a type. For a given type ID @c T, the lower three bits of @c T + /// are its qualifiers (const, volatile, restrict), as in the + /// QualType class. The upper bits, after being shifted and + /// subtracting NUM_PREDEF_TYPE_IDS, are used to index into the + /// TYPE_OFFSET block to determine the offset of that type's + /// corresponding record within the TYPES_BLOCK_ID block. + enum TypeOffsetCode { + TYPE_OFFSET = 1 + }; + + /// \brief Record codes for each kind of declaration. + /// + /// These constants describe the records that can occur within a + /// declarations block (identified by DECLS_BLOCK_ID). Each + /// constant describes a record for a specific declaration class + /// in the AST. + enum DeclCode { + /// \brief A TranslationUnitDecl record. + DECL_TRANSLATION_UNIT = 1, + /// \brief A TypedefDecl record. + DECL_TYPEDEF, + /// \brief A VarDecl record. + DECL_VAR, + /// \brief A record that stores the set of declarations that are + /// lexically stored within a given DeclContext. + /// + /// The record itself is an array of declaration IDs, in the + /// order in which those declarations were added to the + /// declaration context. This data is used when iterating over + /// the contents of a DeclContext, e.g., via + /// DeclContext::decls_begin()/DeclContext::decls_end(). + DECL_CONTEXT_LEXICAL, + /// \brief A record that stores the set of declarations that are + /// visible from a given DeclContext. + /// + /// The record itself stores a set of mappings, each of which + /// associates a declaration name with one or more declaration + /// IDs. This data is used when performing qualified name lookup + /// into a DeclContext via DeclContext::lookup. + DECL_CONTEXT_VISIBLE + }; + + /// \brief Record code for the offsets of each decl. + /// + /// The DECL_OFFSET constant describes the record that occurs + /// within the block identifier by DECL_OFFSETS_BLOCK_ID within + /// the PCH file. The record itself is an array of offsets that + /// point into the declarations block (identified by + /// DECLS_BLOCK_ID). The declaration ID is an index into this + /// record, after subtracting one to account for the use of + /// declaration ID 0 for a NULL declaration pointer. Index 0 is + /// reserved for the translation unit declaration. + enum DeclOffsetCode { + DECL_OFFSET = 1 + }; + } +} // end namespace clang + +#endif diff --git a/clang/include/clang/Frontend/PCHReader.h b/clang/include/clang/Frontend/PCHReader.h new file mode 100644 index 00000000000..1338d3c03b6 --- /dev/null +++ b/clang/include/clang/Frontend/PCHReader.h @@ -0,0 +1,169 @@ +//===--- PCHReader.h - Precompiled Headers Reader ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the PCHReader class, which reads a precompiled header. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_FRONTEND_PCH_READER_H +#define LLVM_CLANG_FRONTEND_PCH_READER_H + +#include "clang/AST/DeclarationName.h" +#include "clang/AST/ExternalASTSource.h" +#include "clang/AST/Type.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Support/DataTypes.h" +#include <string> +#include <utility> +#include <vector> + +namespace llvm { + class MemoryBuffer; +} + +namespace clang { + +class ASTContext; +class Decl; +class DeclContext; + +/// \brief Reads a precompiled head containing the contents of a +/// translation unit. +/// +/// The PCHReader class reads a bitstream (produced by the PCHWriter +/// class) containing the serialized representation of a given +/// abstract syntax tree and its supporting data structures. An +/// instance of the PCHReader can be attached to an ASTContext object, +/// which will provide access to the contents of the PCH file. +/// +/// The PCH reader provides lazy de-serialization of declarations, as +/// required when traversing the AST. Only those AST nodes that are +/// actually required will be de-serialized. +class PCHReader : public ExternalASTSource { + /// \brief The AST context into which we'll read the PCH file. + ASTContext &Context; + + /// \brief The bitstream reader from which we'll read the PCH file. + llvm::BitstreamReader Stream; + + /// \brief The memory buffer that stores the data associated with + /// this PCH file. + llvm::OwningPtr<llvm::MemoryBuffer> Buffer; + + /// \brief Offset of each type within the bitstream, indexed by the + /// type ID, or the representation of a Type*. + llvm::SmallVector<uint64_t, 16> TypeOffsets; + + /// \brief Whether the type with a given index has already been loaded. + /// + /// When the bit at a given index I is true, then TypeOffsets[I] is + /// the already-loaded Type*. Otherwise, TypeOffsets[I] is the + /// location of the type's record in the PCH file. + /// + /// FIXME: We can probably eliminate this, e.g., by bitmangling the + /// values in TypeOffsets. + std::vector<bool> TypeAlreadyLoaded; + + /// \brief Offset of each declaration within the bitstream, indexed + /// by the declaration ID. + llvm::SmallVector<uint64_t, 16> DeclOffsets; + + /// \brief Whether the declaration with a given index has already + /// been loaded. + /// + /// When the bit at the given index I is true, then DeclOffsets[I] + /// is the already-loaded Decl*. Otherwise, DeclOffsets[I] is the + /// location of the declaration's record in the PCH file. + /// + /// FIXME: We can probably eliminate this, e.g., by bitmangling the + /// values in DeclOffsets. + std::vector<bool> DeclAlreadyLoaded; + + typedef llvm::DenseMap<const DeclContext *, std::pair<uint64_t, uint64_t> > + DeclContextOffsetsMap; + + /// \brief Offsets of the lexical and visible declarations for each + /// DeclContext. + DeclContextOffsetsMap DeclContextOffsets; + + bool ReadPCHBlock(); + bool ReadTypeOffsets(); + bool ReadDeclOffsets(); + + QualType ReadTypeRecord(uint64_t Offset); + void LoadedDecl(unsigned Index, Decl *D); + Decl *ReadDeclRecord(uint64_t Offset, unsigned Index); + + PCHReader(const PCHReader&); // do not implement + PCHReader &operator=(const PCHReader &); // do not implement + +public: + typedef llvm::SmallVector<uint64_t, 64> RecordData; + + PCHReader(ASTContext &Context) : Context(Context), Buffer() { } + ~PCHReader(); + + bool ReadPCH(const std::string &FileName); + + /// \brief Resolve a type ID into a type, potentially building a new + /// type. + virtual QualType GetType(unsigned ID); + + /// \brief Resolve a declaration ID into a declaration, potentially + /// building a new declaration. + virtual Decl *GetDecl(unsigned ID); + + /// \brief Read all of the declarations lexically stored in a + /// declaration context. + /// + /// \param DC The declaration context whose declarations will be + /// read. + /// + /// \param Decls Vector that will contain the declarations loaded + /// from the external source. The caller is responsible for merging + /// these declarations with any declarations already stored in the + /// declaration context. + /// + /// \returns true if there was an error while reading the + /// declarations for this declaration context. + virtual bool ReadDeclsLexicallyInContext(DeclContext *DC, + llvm::SmallVectorImpl<unsigned> &Decls); + + /// \brief Read all of the declarations visible from a declaration + /// context. + /// + /// \param DC The declaration context whose visible declarations + /// will be read. + /// + /// \param Decls A vector of visible declaration structures, + /// providing the mapping from each name visible in the declaration + /// context to the declaration IDs of declarations with that name. + /// + /// \returns true if there was an error while reading the + /// declarations for this declaration context. + /// + /// FIXME: Using this intermediate data structure results in an + /// extraneous copying of the data. Could we pass in a reference to + /// the StoredDeclsMap instead? + virtual bool ReadDeclsVisibleInContext(DeclContext *DC, + llvm::SmallVectorImpl<VisibleDeclaration> & Decls); + + /// \brief Print some statistics about PCH usage. + virtual void PrintStats(); + + const IdentifierInfo *GetIdentifierInfo(const RecordData &Record, + unsigned &Idx); + DeclarationName ReadDeclarationName(const RecordData &Record, unsigned &Idx); +}; + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Frontend/PCHWriter.h b/clang/include/clang/Frontend/PCHWriter.h new file mode 100644 index 00000000000..99876e90020 --- /dev/null +++ b/clang/include/clang/Frontend/PCHWriter.h @@ -0,0 +1,114 @@ +//===--- PCHWriter.h - Precompiled Headers Writer ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the PCHWriter class, which writes a precompiled +// header containing a serialized representation of a translation +// unit. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_FRONTEND_PCH_WRITER_H +#define LLVM_CLANG_FRONTEND_PCH_WRITER_H + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclarationName.h" +#include "clang/Frontend/PCHBitCodes.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include <queue> + +namespace llvm { + class APInt; + class BitstreamWriter; +} + +namespace clang { + +class ASTContext; + +/// \brief Writes a precompiled header containing the contents of a +/// translation unit. +/// +/// The PCHWriter class produces a bitstream containing the serialized +/// representation of a given abstract syntax tree and its supporting +/// data structures. This bitstream can be de-serialized via an +/// instance of the PCHReader class. +class PCHWriter { + /// \brief The bitstream writer used to emit this precompiled header. + llvm::BitstreamWriter &S; + + /// \brief Map that provides the ID numbers of each declaration within + /// the output stream. + /// + /// The ID numbers of declarations are consecutive (in order of + /// discovery) and start at 2. 1 is reserved for the translation + /// unit, while 0 is reserved for NULL. + llvm::DenseMap<const Decl *, pch::ID> DeclIDs; + + /// \brief Offset of each declaration in the bitstream, indexed by + /// the declaration's ID. + llvm::SmallVector<uint64_t, 16> DeclOffsets; + + /// \brief Queue containing the declarations that we still need to + /// emit. + std::queue<Decl *> DeclsToEmit; + + /// \brief Map that provides the ID numbers of each type within the + /// output stream. + /// + /// The ID numbers of types are consecutive (in order of discovery) + /// and start at 1. 0 is reserved for NULL. When types are actually + /// stored in the stream, the ID number is shifted by 3 bits to + /// allow for the const/volatile/restrict qualifiers. + llvm::DenseMap<const Type *, pch::ID> TypeIDs; + + /// \brief Offset of each type in the bitstream, indexed by + /// the type's ID. + llvm::SmallVector<uint64_t, 16> TypeOffsets; + + /// \brief The type ID that will be assigned to the next new type. + unsigned NextTypeID; + + void WriteType(const Type *T); + void WriteTypesBlock(ASTContext &Context); + uint64_t WriteDeclContextLexicalBlock(ASTContext &Context, DeclContext *DC); + uint64_t WriteDeclContextVisibleBlock(ASTContext &Context, DeclContext *DC); + void WriteDeclsBlock(ASTContext &Context); + +public: + typedef llvm::SmallVector<uint64_t, 64> RecordData; + + /// \brief Create a new precompiled header writer that outputs to + /// the given bitstream. + PCHWriter(llvm::BitstreamWriter &S); + + /// \brief Write a precompiled header for the given AST context. + void WritePCH(ASTContext &Context); + + /// \brief Emit a source location. + void AddSourceLocation(SourceLocation Loc, RecordData &Record); + + /// \brief Emit an integral value. + void AddAPInt(const llvm::APInt &Value, RecordData &Record); + + /// \brief Emit a reference to an identifier + void AddIdentifierRef(const IdentifierInfo *II, RecordData &Record); + + /// \brief Emit a reference to a type. + void AddTypeRef(QualType T, RecordData &Record); + + /// \brief Emit a reference to a declaration. + void AddDeclRef(const Decl *D, RecordData &Record); + + /// \brief Emit a declaration name. + void AddDeclarationName(DeclarationName Name, RecordData &Record); +}; + +} // end namespace clang + +#endif diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index deeb7631917..7da1bf080c4 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -16,6 +16,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExternalASTSource.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" @@ -36,7 +37,8 @@ ASTContext::ASTContext(const LangOptions& LOpts, SourceManager &SM, bool FreeMem, unsigned size_reserve) : GlobalNestedNameSpecifier(0), CFConstantStringTypeDecl(0), ObjCFastEnumerationStateTypeDecl(0), SourceMgr(SM), LangOpts(LOpts), - FreeMemory(FreeMem), Target(t), Idents(idents), Selectors(sels) { + FreeMemory(FreeMem), Target(t), Idents(idents), Selectors(sels), + ExternalSource(0) { if (size_reserve > 0) Types.reserve(size_reserve); InitBuiltinTypes(); BuiltinInfo.InitializeBuiltins(idents, Target, LangOpts.NoBuiltin); @@ -91,6 +93,11 @@ ASTContext::~ASTContext() { TUDecl->Destroy(*this); } +void +ASTContext::setExternalSource(llvm::OwningPtr<ExternalASTSource> &Source) { + ExternalSource.reset(Source.take()); +} + void ASTContext::PrintStats() const { fprintf(stderr, "*** AST Context Stats:\n"); fprintf(stderr, " %d types total.\n", (int)Types.size()); @@ -195,6 +202,11 @@ void ASTContext::PrintStats() const { NumTypeName*sizeof(TypedefType)+NumTagged*sizeof(TagType)+ NumTypeOfTypes*sizeof(TypeOfType)+NumTypeOfExprTypes*sizeof(TypeOfExprType)+ NumExtQual*sizeof(ExtQualType))); + + if (ExternalSource.get()) { + fprintf(stderr, "\n"); + ExternalSource->PrintStats(); + } } @@ -3359,3 +3371,7 @@ ASTContext* ASTContext::Create(llvm::Deserializer& D) { return A; } + +ExternalASTSource::~ExternalASTSource() { } + +void ExternalASTSource::PrintStats() { } diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 763998e852e..be349428ecf 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExternalASTSource.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Type.h" #include "llvm/ADT/DenseMap.h" @@ -439,11 +440,82 @@ DeclContext *DeclContext::getNextContext() { } } +/// \brief Load the declarations within this lexical storage from an +/// external source. +void +DeclContext::LoadLexicalDeclsFromExternalStorage(ASTContext &Context) const { + ExternalASTSource *Source = Context.getExternalSource(); + assert(hasExternalLexicalStorage() && Source && "No external storage?"); + + llvm::SmallVector<unsigned, 64> Decls; + if (Source->ReadDeclsLexicallyInContext(const_cast<DeclContext *>(this), + Decls)) + return; + + // There is no longer any lexical storage in this context + ExternalLexicalStorage = false; + + if (Decls.empty()) + return; + + // Resolve all of the declaration IDs into declarations, building up + // a chain of declarations via the Decl::NextDeclInContext field. + Decl *FirstNewDecl = 0; + Decl *PrevDecl = 0; + for (unsigned I = 0, N = Decls.size(); I != N; ++I) { + Decl *D = Source->GetDecl(Decls[I]); + if (PrevDecl) + PrevDecl->NextDeclInContext = D; + else + FirstNewDecl = D; + + PrevDecl = D; + } + + // Splice the newly-read declarations into the beginning of the list + // of declarations. + PrevDecl->NextDeclInContext = FirstDecl; + FirstDecl = FirstNewDecl; + if (!LastDecl) + LastDecl = PrevDecl; +} + +void +DeclContext::LoadVisibleDeclsFromExternalStorage(ASTContext &Context) const { + DeclContext *This = const_cast<DeclContext *>(this); + ExternalASTSource *Source = Context.getExternalSource(); + assert(hasExternalVisibleStorage() && Source && "No external storage?"); + + llvm::SmallVector<VisibleDeclaration, 64> Decls; + if (Source->ReadDeclsVisibleInContext(This, Decls)) + return; + + // There is no longer any visible storage in this context + ExternalVisibleStorage = false; + + // Load the declaration IDs for all of the names visible in this + // context. + assert(!LookupPtr && "Have a lookup map before de-serialization?"); + StoredDeclsMap *Map = new StoredDeclsMap; + LookupPtr = Map; + for (unsigned I = 0, N = Decls.size(); I != N; ++I) { + (*Map)[Decls[I].Name].setFromDeclIDs(Decls[I].Declarations); + } +} + DeclContext::decl_iterator DeclContext::decls_begin(ASTContext &Context) const { + if (hasExternalLexicalStorage()) + LoadLexicalDeclsFromExternalStorage(Context); + + // FIXME: Check whether we need to load some declarations from + // external storage. return decl_iterator(FirstDecl); } DeclContext::decl_iterator DeclContext::decls_end(ASTContext &Context) const { + if (hasExternalLexicalStorage()) + LoadLexicalDeclsFromExternalStorage(Context); + return decl_iterator(); } @@ -491,6 +563,9 @@ DeclContext::lookup(ASTContext &Context, DeclarationName Name) { if (PrimaryContext != this) return PrimaryContext->lookup(Context, Name); + if (hasExternalVisibleStorage()) + LoadVisibleDeclsFromExternalStorage(Context); + /// If there is no lookup data structure, build one now by walking /// all of the linked DeclContexts (in declaration order!) and /// inserting their values. @@ -595,3 +670,40 @@ DeclContext::getUsingDirectives(ASTContext &Context) const { return udir_iterator_range(reinterpret_cast<udir_iterator>(Result.first), reinterpret_cast<udir_iterator>(Result.second)); } + +void StoredDeclsList::materializeDecls(ASTContext &Context) { + if (isNull()) + return; + + switch ((DataKind)(Data & 0x03)) { + case DK_Decl: + case DK_Decl_Vector: + break; + + case DK_DeclID: { + // Resolve this declaration ID to an actual declaration by + // querying the external AST source. + unsigned DeclID = Data >> 2; + + ExternalASTSource *Source = Context.getExternalSource(); + assert(Source && "No external AST source available!"); + + Data = reinterpret_cast<uintptr_t>(Source->GetDecl(DeclID)); + break; + } + + case DK_ID_Vector: { + // We have a vector of declaration IDs. Resolve all of them to + // actual declarations. + VectorTy &Vector = *getAsVector(); + ExternalASTSource *Source = Context.getExternalSource(); + assert(Source && "No external AST source available!"); + + for (unsigned I = 0, N = Vector.size(); I != N; ++I) + Vector[I] = reinterpret_cast<uintptr_t>(Source->GetDecl(Vector[I])); + + Data = (Data & ~0x03) | DK_Decl_Vector; + break; + } + } +} diff --git a/clang/lib/Frontend/PCHReader.cpp b/clang/lib/Frontend/PCHReader.cpp new file mode 100644 index 00000000000..d366cddf175 --- /dev/null +++ b/clang/lib/Frontend/PCHReader.cpp @@ -0,0 +1,602 @@ +//===--- PCHReader.cpp - Precompiled Headers Reader -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the PCHReader class, which reads a precompiled header. +// +//===----------------------------------------------------------------------===// +#include "clang/Frontend/PCHReader.h" +#include "clang/Frontend/PCHBitCodes.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Type.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/MemoryBuffer.h" +#include <algorithm> +#include <cstdio> + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Declaration deserialization +//===----------------------------------------------------------------------===// +namespace { + class VISIBILITY_HIDDEN PCHDeclReader { + PCHReader &Reader; + const PCHReader::RecordData &Record; + unsigned &Idx; + + public: + PCHDeclReader(PCHReader &Reader, const PCHReader::RecordData &Record, + unsigned &Idx) + : Reader(Reader), Record(Record), Idx(Idx) { } + + void VisitDecl(Decl *D); + void VisitTranslationUnitDecl(TranslationUnitDecl *TU); + void VisitNamedDecl(NamedDecl *ND); + void VisitTypeDecl(TypeDecl *TD); + void VisitTypedefDecl(TypedefDecl *TD); + void VisitValueDecl(ValueDecl *VD); + void VisitVarDecl(VarDecl *VD); + + std::pair<uint64_t, uint64_t> VisitDeclContext(DeclContext *DC); + }; +} + +void PCHDeclReader::VisitDecl(Decl *D) { + D->setDeclContext(cast_or_null<DeclContext>(Reader.GetDecl(Record[Idx++]))); + D->setLexicalDeclContext( + cast_or_null<DeclContext>(Reader.GetDecl(Record[Idx++]))); + D->setLocation(SourceLocation::getFromRawEncoding(Record[Idx++])); + D->setInvalidDecl(Record[Idx++]); + // FIXME: hasAttrs + D->setImplicit(Record[Idx++]); + D->setAccess((AccessSpecifier)Record[Idx++]); +} + +void PCHDeclReader::VisitTranslationUnitDecl(TranslationUnitDecl *TU) { + VisitDecl(TU); +} + +void PCHDeclReader::VisitNamedDecl(NamedDecl *ND) { + VisitDecl(ND); + ND->setDeclName(Reader.ReadDeclarationName(Record, Idx)); +} + +void PCHDeclReader::VisitTypeDecl(TypeDecl *TD) { + VisitNamedDecl(TD); + // FIXME: circular dependencies here? + TD->setTypeForDecl(Reader.GetType(Record[Idx++]).getTypePtr()); +} + +void PCHDeclReader::VisitTypedefDecl(TypedefDecl *TD) { + VisitTypeDecl(TD); + TD->setUnderlyingType(Reader.GetType(Record[Idx++])); +} + +void PCHDeclReader::VisitValueDecl(ValueDecl *VD) { + VisitNamedDecl(VD); + VD->setType(Reader.GetType(Record[Idx++])); +} + +void PCHDeclReader::VisitVarDecl(VarDecl *VD) { + VisitValueDecl(VD); + VD->setStorageClass((VarDecl::StorageClass)Record[Idx++]); + VD->setThreadSpecified(Record[Idx++]); + VD->setCXXDirectInitializer(Record[Idx++]); + VD->setDeclaredInCondition(Record[Idx++]); + VD->setPreviousDeclaration( + cast_or_null<VarDecl>(Reader.GetDecl(Record[Idx++]))); + VD->setTypeSpecStartLoc(SourceLocation::getFromRawEncoding(Record[Idx++])); +} + +std::pair<uint64_t, uint64_t> +PCHDeclReader::VisitDeclContext(DeclContext *DC) { + uint64_t LexicalOffset = Record[Idx++]; + uint64_t VisibleOffset = 0; + if (DC->getPrimaryContext() == DC) + VisibleOffset = Record[Idx++]; + return std::make_pair(LexicalOffset, VisibleOffset); +} + +// FIXME: use the diagnostics machinery +static bool Error(const char *Str) { + std::fprintf(stderr, "%s\n", Str); + return true; +} + +/// \brief Read the type-offsets block. +bool PCHReader::ReadTypeOffsets() { + if (Stream.EnterSubBlock(pch::TYPE_OFFSETS_BLOCK_ID)) + return Error("Malformed block record"); + + RecordData Record; + while (true) { + unsigned Code = Stream.ReadCode(); + if (Code == llvm::bitc::END_BLOCK) { + if (Stream.ReadBlockEnd()) + return Error("Error at end of TYPE_OFFSETS block"); + return false; + } + + if (Code == llvm::bitc::ENTER_SUBBLOCK) { + // No known subblocks, always skip them. + Stream.ReadSubBlockID(); + if (Stream.SkipBlock()) + return Error("Malformed block record"); + continue; + } + + if (Code == llvm::bitc::DEFINE_ABBREV) { + Stream.ReadAbbrevRecord(); + continue; + } + + // Read a record. + Record.clear(); + switch (Stream.ReadRecord(Code, Record)) { + default: // Default behavior: ignore. + break; + case pch::TYPE_OFFSET: + if (!TypeOffsets.empty()) + return Error("Duplicate TYPE_OFFSETS block"); + TypeOffsets.swap(Record); + TypeAlreadyLoaded.resize(TypeOffsets.size(), false); + break; + } + } +} + +/// \brief Read the decl-offsets block. +bool PCHReader::ReadDeclOffsets() { + if (Stream.EnterSubBlock(pch::DECL_OFFSETS_BLOCK_ID)) + return Error("Malformed block record"); + + RecordData Record; + while (true) { + unsigned Code = Stream.ReadCode(); + if (Code == llvm::bitc::END_BLOCK) { + if (Stream.ReadBlockEnd()) + return Error("Error at end of DECL_OFFSETS block"); + return false; + } + + if (Code == llvm::bitc::ENTER_SUBBLOCK) { + // No known subblocks, always skip them. + Stream.ReadSubBlockID(); + if (Stream.SkipBlock()) + return Error("Malformed block record"); + continue; + } + + if (Code == llvm::bitc::DEFINE_ABBREV) { + Stream.ReadAbbrevRecord(); + continue; + } + + // Read a record. + Record.clear(); + switch (Stream.ReadRecord(Code, Record)) { + default: // Default behavior: ignore. + break; + case pch::DECL_OFFSET: + if (!DeclOffsets.empty()) + return Error("Duplicate DECL_OFFSETS block"); + DeclOffsets.swap(Record); + DeclAlreadyLoaded.resize(DeclOffsets.size(), false); + break; + } + } +} + +bool PCHReader::ReadPCHBlock() { + if (Stream.EnterSubBlock(pch::PCH_BLOCK_ID)) + return Error("Malformed block record"); + + // Read all of the records and blocks for the PCH file. + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + if (Code == llvm::bitc::END_BLOCK) { + if (Stream.ReadBlockEnd()) + return Error("Error at end of module block"); + return false; + } + + if (Code == llvm::bitc::ENTER_SUBBLOCK) { + switch (Stream.ReadSubBlockID()) { + case pch::DECLS_BLOCK_ID: // Skip decls block (lazily loaded) + case pch::TYPES_BLOCK_ID: // Skip types block (lazily loaded) + default: // Skip unknown content. + if (Stream.SkipBlock()) + return Error("Malformed block record"); + break; + + + case pch::TYPE_OFFSETS_BLOCK_ID: + if (ReadTypeOffsets()) + return Error("Malformed type-offsets block"); + break; + + case pch::DECL_OFFSETS_BLOCK_ID: + if (ReadDeclOffsets()) + return Error("Malformed decl-offsets block"); + break; + } + } + } + + return Error("Premature end of bitstream"); +} + +PCHReader::~PCHReader() { } + +bool PCHReader::ReadPCH(const std::string &FileName) { + // Open the PCH file. + std::string ErrStr; + Buffer.reset(llvm::MemoryBuffer::getFile(FileName.c_str(), &ErrStr)); + if (!Buffer) + return Error(ErrStr.c_str()); + + // Initialize the stream + Stream.init((const unsigned char *)Buffer->getBufferStart(), + (const unsigned char *)Buffer->getBufferEnd()); + + // Sniff for the signature. + if (Stream.Read(8) != 'C' || + Stream.Read(8) != 'P' || + Stream.Read(8) != 'C' || + Stream.Read(8) != 'H') + return Error("Not a PCH file"); + + // We expect a number of well-defined blocks, though we don't necessarily + // need to understand them all. + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + + if (Code != llvm::bitc::ENTER_SUBBLOCK) + return Error("Invalid record at top-level"); + + unsigned BlockID = Stream.ReadSubBlockID(); + + // We only know the PCH subblock ID. + switch (BlockID) { + case llvm::bitc::BLOCKINFO_BLOCK_ID: + if (Stream.ReadBlockInfoBlock()) + return Error("Malformed BlockInfoBlock"); + break; + case pch::PCH_BLOCK_ID: + if (ReadPCHBlock()) + return true; + break; + default: + if (Stream.SkipBlock()) + return Error("Malformed block record"); + break; + } + } + + // Load the translation unit declaration + ReadDeclRecord(DeclOffsets[0], 0); + + return false; +} + +/// \brief Read and return the type at the given offset. +/// +/// This routine actually reads the record corresponding to the type +/// at the given offset in the bitstream. It is a helper routine for +/// GetType, which deals with reading type IDs. +QualType PCHReader::ReadTypeRecord(uint64_t Offset) { + Stream.JumpToBit(Offset); + RecordData Record; + unsigned Code = Stream.ReadCode(); + switch ((pch::TypeCode)Stream.ReadRecord(Code, Record)) { + case pch::TYPE_FIXED_WIDTH_INT: { + assert(Record.size() == 2 && "Incorrect encoding of fixed-width int type"); + return Context.getFixedWidthIntType(Record[0], Record[1]); + } + + case pch::TYPE_COMPLEX: { + assert(Record.size() == 1 && "Incorrect encoding of complex type"); + QualType ElemType = GetType(Record[0]); + return Context.getComplexType(ElemType); + } + + case pch::TYPE_POINTER: { + assert(Record.size() == 1 && "Incorrect encoding of pointer type"); + QualType PointeeType = GetType(Record[0]); + return Context.getPointerType(PointeeType); + } + + case pch::TYPE_BLOCK_POINTER: { + assert(Record.size() == 1 && "Incorrect encoding of block pointer type"); + QualType PointeeType = GetType(Record[0]); + return Context.getBlockPointerType(PointeeType); + } + + case pch::TYPE_LVALUE_REFERENCE: { + assert(Record.size() == 1 && "Incorrect encoding of lvalue reference type"); + QualType PointeeType = GetType(Record[0]); + return Context.getLValueReferenceType(PointeeType); + } + + case pch::TYPE_RVALUE_REFERENCE: { + assert(Record.size() == 1 && "Incorrect encoding of rvalue reference type"); + QualType PointeeType = GetType(Record[0]); + return Context.getRValueReferenceType(PointeeType); + } + + case pch::TYPE_MEMBER_POINTER: { + assert(Record.size() == 1 && "Incorrect encoding of member pointer type"); + QualType PointeeType = GetType(Record[0]); + QualType ClassType = GetType(Record[1]); + return Context.getMemberPointerType(PointeeType, ClassType.getTypePtr()); + } + + // FIXME: Several other kinds of types to deserialize here! + default: + assert("Unable to deserialize this type"); + break; + } + + // Suppress a GCC warning + return QualType(); +} + +/// \brief Note that we have loaded the declaration with the given +/// Index. +/// +/// This routine notes that this declaration has already been loaded, +/// so that future GetDecl calls will return this declaration rather +/// than trying to load a new declaration. +inline void PCHReader::LoadedDecl(unsigned Index, Decl *D) { + assert(!DeclAlreadyLoaded[Index] && "Decl loaded twice?"); + DeclAlreadyLoaded[Index] = true; + DeclOffsets[Index] = reinterpret_cast<uint64_t>(D); +} + +/// \brief Read the declaration at the given offset from the PCH file. +Decl *PCHReader::ReadDeclRecord(uint64_t Offset, unsigned Index) { + Decl *D = 0; + Stream.JumpToBit(Offset); + RecordData Record; + unsigned Code = Stream.ReadCode(); + unsigned Idx = 0; + PCHDeclReader Reader(*this, Record, Idx); + switch ((pch::DeclCode)Stream.ReadRecord(Code, Record)) { + case pch::DECL_TRANSLATION_UNIT: + assert(Index == 0 && "Translation unit must be at index 0"); + Reader.VisitTranslationUnitDecl(Context.getTranslationUnitDecl()); + D = Context.getTranslationUnitDecl(); + LoadedDecl(Index, D); + break; + + case pch::DECL_TYPEDEF: { + TypedefDecl *Typedef = TypedefDecl::Create(Context, 0, SourceLocation(), + 0, QualType()); + LoadedDecl(Index, Typedef); + Reader.VisitTypedefDecl(Typedef); + D = Typedef; + break; + } + + case pch::DECL_VAR: { + VarDecl *Var = VarDecl::Create(Context, 0, SourceLocation(), 0, QualType(), + VarDecl::None, SourceLocation()); + LoadedDecl(Index, Var); + Reader.VisitVarDecl(Var); + D = Var; + break; + } + + default: + assert(false && "Cannot de-serialize this kind of declaration"); + break; + } + + // If this declaration is also a declaration context, get the + // offsets for its tables of lexical and visible declarations. + if (DeclContext *DC = dyn_cast<DeclContext>(D)) { + std::pair<uint64_t, uint64_t> Offsets = Reader.VisitDeclContext(DC); + if (Offsets.first || Offsets.second) { + DC->setHasExternalLexicalStorage(Offsets.first != 0); + DC->setHasExternalVisibleStorage(Offsets.second != 0); + DeclContextOffsets[DC] = Offsets; + } + } + assert(Idx == Record.size()); + + return D; +} + +QualType PCHReader::GetType(unsigned ID) { + unsigned Quals = ID & 0x07; + unsigned Index = ID >> 3; + + if (Index < pch::NUM_PREDEF_TYPE_IDS) { + QualType T; + switch ((pch::PredefinedTypeIDs)Index) { + case pch::PREDEF_TYPE_NULL_ID: return QualType(); + case pch::PREDEF_TYPE_VOID_ID: T = Context.VoidTy; break; + case pch::PREDEF_TYPE_BOOL_ID: T = Context.BoolTy; break; + + case pch::PREDEF_TYPE_CHAR_U_ID: + case pch::PREDEF_TYPE_CHAR_S_ID: + // FIXME: Check that the signedness of CharTy is correct! + T = Context.CharTy; + break; + + case pch::PREDEF_TYPE_UCHAR_ID: T = Context.UnsignedCharTy; break; + case pch::PREDEF_TYPE_USHORT_ID: T = Context.UnsignedShortTy; break; + case pch::PREDEF_TYPE_UINT_ID: T = Context.UnsignedIntTy; break; + case pch::PREDEF_TYPE_ULONG_ID: T = Context.UnsignedLongTy; break; + case pch::PREDEF_TYPE_ULONGLONG_ID: T = Context.UnsignedLongLongTy; break; + case pch::PREDEF_TYPE_SCHAR_ID: T = Context.SignedCharTy; break; + case pch::PREDEF_TYPE_WCHAR_ID: T = Context.WCharTy; break; + case pch::PREDEF_TYPE_SHORT_ID: T = Context.ShortTy; break; + case pch::PREDEF_TYPE_INT_ID: T = Context.IntTy; break; + case pch::PREDEF_TYPE_LONG_ID: T = Context.LongTy; break; + case pch::PREDEF_TYPE_LONGLONG_ID: T = Context.LongLongTy; break; + case pch::PREDEF_TYPE_FLOAT_ID: T = Context.FloatTy; break; + case pch::PREDEF_TYPE_DOUBLE_ID: T = Context.DoubleTy; break; + case pch::PREDEF_TYPE_LONGDOUBLE_ID: T = Context.LongDoubleTy; break; + case pch::PREDEF_TYPE_OVERLOAD_ID: T = Context.OverloadTy; break; + case pch::PREDEF_TYPE_DEPENDENT_ID: T = Context.DependentTy; break; + } + + assert(!T.isNull() && "Unknown predefined type"); + return T.getQualifiedType(Quals); + } + + Index -= pch::NUM_PREDEF_TYPE_IDS; + if (!TypeAlreadyLoaded[Index]) { + // Load the type from the PCH file. + TypeOffsets[Index] = reinterpret_cast<uint64_t>( + ReadTypeRecord(TypeOffsets[Index]).getTypePtr()); + TypeAlreadyLoaded[Index] = true; + } + + return QualType(reinterpret_cast<Type *>(TypeOffsets[Index]), Quals); +} + +Decl *PCHReader::GetDecl(unsigned ID) { + if (ID == 0) + return 0; + + unsigned Index = ID - 1; + if (DeclAlreadyLoaded[Index]) + return reinterpret_cast<Decl *>(DeclOffsets[Index]); + + // Load the declaration from the PCH file. + return ReadDeclRecord(DeclOffsets[Index], Index); +} + +bool PCHReader::ReadDeclsLexicallyInContext(DeclContext *DC, + llvm::SmallVectorImpl<unsigned> &Decls) { + assert(DC->hasExternalLexicalStorage() && + "DeclContext has no lexical decls in storage"); + uint64_t Offset = DeclContextOffsets[DC].first; + assert(Offset && "DeclContext has no lexical decls in storage"); + + // Load the record containing all of the declarations lexically in + // this context. + Stream.JumpToBit(Offset); + RecordData Record; + unsigned Code = Stream.ReadCode(); + unsigned RecCode = Stream.ReadRecord(Code, Record); + assert(RecCode == pch::DECL_CONTEXT_LEXICAL && "Expected lexical block"); + + // Load all of the declaration IDs + Decls.clear(); + Decls.insert(Decls.end(), Record.begin(), Record.end()); + return false; +} + +bool PCHReader::ReadDeclsVisibleInContext(DeclContext *DC, + llvm::SmallVectorImpl<VisibleDeclaration> & Decls) { + assert(DC->hasExternalVisibleStorage() && + "DeclContext has no visible decls in storage"); + uint64_t Offset = DeclContextOffsets[DC].second; + assert(Offset && "DeclContext has no visible decls in storage"); + + // Load the record containing all of the declarations visible in + // this context. + Stream.JumpToBit(Offset); + RecordData Record; + unsigned Code = Stream.ReadCode(); + unsigned RecCode = Stream.ReadRecord(Code, Record); + assert(RecCode == pch::DECL_CONTEXT_VISIBLE && "Expected visible block"); + if (Record.size() == 0) + return false; + + Decls.clear(); + + unsigned Idx = 0; + // llvm::SmallVector<uintptr_t, 16> DeclIDs; + while (Idx < Record.size()) { + Decls.push_back(VisibleDeclaration()); + Decls.back().Name = ReadDeclarationName(Record, Idx); + + // FIXME: Don't actually read anything here! + unsigned Size = Record[Idx++]; + llvm::SmallVector<unsigned, 4> & LoadedDecls + = Decls.back().Declarations; + LoadedDecls.reserve(Size); + for (unsigned I = 0; I < Size; ++I) + LoadedDecls.push_back(Record[Idx++]); + } + + return false; +} + +void PCHReader::PrintStats() { + std::fprintf(stderr, "*** PCH Statistics:\n"); + + unsigned NumTypesLoaded = std::count(TypeAlreadyLoaded.begin(), + TypeAlreadyLoaded.end(), + true); + unsigned NumDeclsLoaded = std::count(DeclAlreadyLoaded.begin(), + DeclAlreadyLoaded.end(), + true); + std::fprintf(stderr, " %u/%u types read (%f%%)\n", + NumTypesLoaded, (unsigned)TypeAlreadyLoaded.size(), + ((float)NumTypesLoaded/(float)TypeAlreadyLoaded.size() * 100)); + std::fprintf(stderr, " %u/%u declarations read (%f%%)\n", + NumDeclsLoaded, (unsigned)DeclAlreadyLoaded.size(), + ((float)NumDeclsLoaded/(float)DeclAlreadyLoaded.size() * 100)); + std::fprintf(stderr, "\n"); +} + +const IdentifierInfo *PCHReader::GetIdentifierInfo(const RecordData &Record, + unsigned &Idx) { + // FIXME: we need unique IDs for identifiers. + std::string Str; + unsigned Length = Record[Idx++]; + Str.resize(Length); + for (unsigned I = 0; I != Length; ++I) + Str[I] = Record[Idx++]; + return &Context.Idents.get(Str); +} + +DeclarationName +PCHReader::ReadDeclarationName(const RecordData &Record, unsigned &Idx) { + DeclarationName::NameKind Kind = (DeclarationName::NameKind)Record[Idx++]; + switch (Kind) { + case DeclarationName::Identifier: + return DeclarationName(GetIdentifierInfo(Record, Idx)); + + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + assert(false && "Unable to de-serialize Objective-C selectors"); + break; + + case DeclarationName::CXXConstructorName: + return Context.DeclarationNames.getCXXConstructorName( + GetType(Record[Idx++])); + + case DeclarationName::CXXDestructorName: + return Context.DeclarationNames.getCXXDestructorName( + GetType(Record[Idx++])); + + case DeclarationName::CXXConversionFunctionName: + return Context.DeclarationNames.getCXXConversionFunctionName( + GetType(Record[Idx++])); + + case DeclarationName::CXXOperatorName: + return Context.DeclarationNames.getCXXOperatorName( + (OverloadedOperatorKind)Record[Idx++]); + + case DeclarationName::CXXUsingDirective: + return DeclarationName::getUsingDirectiveName(); + } + + // Required to silence GCC warning + return DeclarationName(); +} diff --git a/clang/lib/Frontend/PCHWriter.cpp b/clang/lib/Frontend/PCHWriter.cpp new file mode 100644 index 00000000000..39936b34f5f --- /dev/null +++ b/clang/lib/Frontend/PCHWriter.cpp @@ -0,0 +1,636 @@ +//===--- PCHWriter.h - Precompiled Headers Writer ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the PCHWriter class, which writes a precompiled header. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/PCHWriter.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclContextInternals.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/Type.h" +#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Support/Compiler.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Type serialization +//===----------------------------------------------------------------------===// +namespace { + class VISIBILITY_HIDDEN PCHTypeWriter { + PCHWriter &Writer; + PCHWriter::RecordData &Record; + + public: + /// \brief Type code that corresponds to the record generated. + pch::TypeCode Code; + + PCHTypeWriter(PCHWriter &Writer, PCHWriter::RecordData &Record) + : Writer(Writer), Record(Record) { } + + void VisitArrayType(const ArrayType *T); + void VisitFunctionType(const FunctionType *T); + void VisitTagType(const TagType *T); + +#define TYPE(Class, Base) void Visit##Class##Type(const Class##Type *T); +#define ABSTRACT_TYPE(Class, Base) +#define DEPENDENT_TYPE(Class, Base) +#include "clang/AST/TypeNodes.def" + }; +} + +void PCHTypeWriter::VisitExtQualType(const ExtQualType *T) { + Writer.AddTypeRef(QualType(T->getBaseType(), 0), Record); + Record.push_back(T->getObjCGCAttr()); // FIXME: use stable values + Record.push_back(T->getAddressSpace()); + Code = pch::TYPE_EXT_QUAL; +} + +void PCHTypeWriter::VisitBuiltinType(const BuiltinType *T) { + assert(false && "Built-in types are never serialized"); +} + +void PCHTypeWriter::VisitFixedWidthIntType(const FixedWidthIntType *T) { + Record.push_back(T->getWidth()); + Record.push_back(T->isSigned()); + Code = pch::TYPE_FIXED_WIDTH_INT; +} + +void PCHTypeWriter::VisitComplexType(const ComplexType *T) { + Writer.AddTypeRef(T->getElementType(), Record); + Code = pch::TYPE_COMPLEX; +} + +void PCHTypeWriter::VisitPointerType(const PointerType *T) { + Writer.AddTypeRef(T->getPointeeType(), Record); + Code = pch::TYPE_POINTER; +} + +void PCHTypeWriter::VisitBlockPointerType(const BlockPointerType *T) { + Writer.AddTypeRef(T->getPointeeType(), Record); + Code = pch::TYPE_BLOCK_POINTER; +} + +void PCHTypeWriter::VisitLValueReferenceType(const LValueReferenceType *T) { + Writer.AddTypeRef(T->getPointeeType(), Record); + Code = pch::TYPE_LVALUE_REFERENCE; +} + +void PCHTypeWriter::VisitRValueReferenceType(const RValueReferenceType *T) { + Writer.AddTypeRef(T->getPointeeType(), Record); + Code = pch::TYPE_RVALUE_REFERENCE; +} + +void PCHTypeWriter::VisitMemberPointerType(const MemberPointerType *T) { + Writer.AddTypeRef(T->getPointeeType(), Record); + Writer.AddTypeRef(QualType(T->getClass(), 0), Record); + Code = pch::TYPE_MEMBER_POINTER; +} + +void PCHTypeWriter::VisitArrayType(const ArrayType *T) { + Writer.AddTypeRef(T->getElementType(), Record); + Record.push_back(T->getSizeModifier()); // FIXME: stable values + Record.push_back(T->getIndexTypeQualifier()); // FIXME: stable values +} + +void PCHTypeWriter::VisitConstantArrayType(const ConstantArrayType *T) { + VisitArrayType(T); + Writer.AddAPInt(T->getSize(), Record); + Code = pch::TYPE_CONSTANT_ARRAY; +} + +void PCHTypeWriter::VisitIncompleteArrayType(const IncompleteArrayType *T) { + VisitArrayType(T); + Code = pch::TYPE_INCOMPLETE_ARRAY; +} + +void PCHTypeWriter::VisitVariableArrayType(const VariableArrayType *T) { + VisitArrayType(T); + // FIXME: Serialize array size expression. + assert(false && "Cannot serialize variable-length arrays"); + Code = pch::TYPE_VARIABLE_ARRAY; +} + +void PCHTypeWriter::VisitVectorType(const VectorType *T) { + Writer.AddTypeRef(T->getElementType(), Record); + Record.push_back(T->getNumElements()); + Code = pch::TYPE_VECTOR; +} + +void PCHTypeWriter::VisitExtVectorType(const ExtVectorType *T) { + VisitVectorType(T); + Code = pch::TYPE_EXT_VECTOR; +} + +void PCHTypeWriter::VisitFunctionType(const FunctionType *T) { + Writer.AddTypeRef(T->getResultType(), Record); +} + +void PCHTypeWriter::VisitFunctionNoProtoType(const FunctionNoProtoType *T) { + VisitFunctionType(T); + Code = pch::TYPE_FUNCTION_NO_PROTO; +} + +void PCHTypeWriter::VisitFunctionProtoType(const FunctionProtoType *T) { + VisitFunctionType(T); + Record.push_back(T->getNumArgs()); + for (unsigned I = 0, N = T->getNumArgs(); I != N; ++I) + Writer.AddTypeRef(T->getArgType(I), Record); + Record.push_back(T->isVariadic()); + Record.push_back(T->getTypeQuals()); + Code = pch::TYPE_FUNCTION_PROTO; +} + +void PCHTypeWriter::VisitTypedefType(const TypedefType *T) { + Writer.AddDeclRef(T->getDecl(), Record); + Code = pch::TYPE_TYPEDEF; +} + +void PCHTypeWriter::VisitTypeOfExprType(const TypeOfExprType *T) { + // FIXME: serialize the typeof expression + assert(false && "Cannot serialize typeof(expr)"); + Code = pch::TYPE_TYPEOF_EXPR; +} + +void PCHTypeWriter::VisitTypeOfType(const TypeOfType *T) { + Writer.AddTypeRef(T->getUnderlyingType(), Record); + Code = pch::TYPE_TYPEOF; +} + +void PCHTypeWriter::VisitTagType(const TagType *T) { + Writer.AddDeclRef(T->getDecl(), Record); + assert(!T->isBeingDefined() && + "Cannot serialize in the middle of a type definition"); +} + +void PCHTypeWriter::VisitRecordType(const RecordType *T) { + VisitTagType(T); + Code = pch::TYPE_RECORD; +} + +void PCHTypeWriter::VisitEnumType(const EnumType *T) { + VisitTagType(T); + Code = pch::TYPE_ENUM; +} + +void +PCHTypeWriter::VisitTemplateSpecializationType( + const TemplateSpecializationType *T) { + // FIXME: Serialize this type + assert(false && "Cannot serialize template specialization types"); +} + +void PCHTypeWriter::VisitQualifiedNameType(const QualifiedNameType *T) { + // FIXME: Serialize this type + assert(false && "Cannot serialize qualified name types"); +} + +void PCHTypeWriter::VisitObjCInterfaceType(const ObjCInterfaceType *T) { + Writer.AddDeclRef(T->getDecl(), Record); + Code = pch::TYPE_OBJC_INTERFACE; +} + +void +PCHTypeWriter::VisitObjCQualifiedInterfaceType( + const ObjCQualifiedInterfaceType *T) { + VisitObjCInterfaceType(T); + Record.push_back(T->getNumProtocols()); + for (unsigned I = 0, N = T->getNumProtocols(); I != N; ++I) + Writer.AddDeclRef(T->getProtocol(I), Record); + Code = pch::TYPE_OBJC_QUALIFIED_INTERFACE; +} + +void PCHTypeWriter::VisitObjCQualifiedIdType(const ObjCQualifiedIdType *T) { + Record.push_back(T->getNumProtocols()); + for (unsigned I = 0, N = T->getNumProtocols(); I != N; ++I) + Writer.AddDeclRef(T->getProtocols(I), Record); + Code = pch::TYPE_OBJC_QUALIFIED_ID; +} + +void +PCHTypeWriter::VisitObjCQualifiedClassType(const ObjCQualifiedClassType *T) { + Record.push_back(T->getNumProtocols()); + for (unsigned I = 0, N = T->getNumProtocols(); I != N; ++I) + Writer.AddDeclRef(T->getProtocols(I), Record); + Code = pch::TYPE_OBJC_QUALIFIED_CLASS; +} + +//===----------------------------------------------------------------------===// +// Declaration serialization +//===----------------------------------------------------------------------===// +namespace { + class VISIBILITY_HIDDEN PCHDeclWriter + : public DeclVisitor<PCHDeclWriter, void> { + + PCHWriter &Writer; + PCHWriter::RecordData &Record; + + public: + pch::DeclCode Code; + + PCHDeclWriter(PCHWriter &Writer, PCHWriter::RecordData &Record) + : Writer(Writer), Record(Record) { } + + void VisitDecl(Decl *D); + void VisitTranslationUnitDecl(TranslationUnitDecl *D); + void VisitNamedDecl(NamedDecl *D); + void VisitTypeDecl(TypeDecl *D); + void VisitTypedefDecl(TypedefDecl *D); + void VisitValueDecl(ValueDecl *D); + void VisitVarDecl(VarDecl *D); + + void VisitDeclContext(DeclContext *DC, uint64_t LexicalOffset, + uint64_t VisibleOffset); + }; +} + +void PCHDeclWriter::VisitDecl(Decl *D) { + Writer.AddDeclRef(cast_or_null<Decl>(D->getDeclContext()), Record); + Writer.AddDeclRef(cast_or_null<Decl>(D->getLexicalDeclContext()), Record); + Writer.AddSourceLocation(D->getLocation(), Record); + Record.push_back(D->isInvalidDecl()); + // FIXME: hasAttrs + Record.push_back(D->isImplicit()); + Record.push_back(D->getAccess()); +} + +void PCHDeclWriter::VisitTranslationUnitDecl(TranslationUnitDecl *D) { + VisitDecl(D); + Code = pch::DECL_TRANSLATION_UNIT; +} + +void PCHDeclWriter::VisitNamedDecl(NamedDecl *D) { + VisitDecl(D); + Writer.AddDeclarationName(D->getDeclName(), Record); +} + +void PCHDeclWriter::VisitTypeDecl(TypeDecl *D) { + VisitNamedDecl(D); + Writer.AddTypeRef(QualType(D->getTypeForDecl(), 0), Record); +} + +void PCHDeclWriter::VisitTypedefDecl(TypedefDecl *D) { + VisitTypeDecl(D); + Writer.AddTypeRef(D->getUnderlyingType(), Record); + Code = pch::DECL_TYPEDEF; +} + +void PCHDeclWriter::VisitValueDecl(ValueDecl *D) { + VisitNamedDecl(D); + Writer.AddTypeRef(D->getType(), Record); +} + +void PCHDeclWriter::VisitVarDecl(VarDecl *D) { + VisitValueDecl(D); + Record.push_back(D->getStorageClass()); + Record.push_back(D->isThreadSpecified()); + Record.push_back(D->hasCXXDirectInitializer()); + Record.push_back(D->isDeclaredInCondition()); + Writer.AddDeclRef(D->getPreviousDeclaration(), Record); + Writer.AddSourceLocation(D->getTypeSpecStartLoc(), Record); + // FIXME: emit initializer + Code = pch::DECL_VAR; +} + +/// \brief Emit the DeclContext part of a declaration context decl. +/// +/// \param LexicalOffset the offset at which the DECL_CONTEXT_LEXICAL +/// block for this declaration context is stored. May be 0 to indicate +/// that there are no declarations stored within this context. +/// +/// \param VisibleOffset the offset at which the DECL_CONTEXT_VISIBLE +/// block for this declaration context is stored. May be 0 to indicate +/// that there are no declarations visible from this context. Note +/// that this value will not be emitted for non-primary declaration +/// contexts. +void PCHDeclWriter::VisitDeclContext(DeclContext *DC, uint64_t LexicalOffset, + uint64_t VisibleOffset) { + Record.push_back(LexicalOffset); + if (DC->getPrimaryContext() == DC) + Record.push_back(VisibleOffset); +} + +//===----------------------------------------------------------------------===// +// PCHWriter Implementation +//===----------------------------------------------------------------------===// + +/// \brief Write the representation of a type to the PCH stream. +void PCHWriter::WriteType(const Type *T) { + pch::ID &ID = TypeIDs[T]; + if (ID == 0) // we haven't seen this type before + ID = NextTypeID++; + + // Record the offset for this type. + if (TypeOffsets.size() == ID - pch::NUM_PREDEF_TYPE_IDS) + TypeOffsets.push_back(S.GetCurrentBitNo()); + else if (TypeOffsets.size() < ID - pch::NUM_PREDEF_TYPE_IDS) { + TypeOffsets.resize(ID + 1 - pch::NUM_PREDEF_TYPE_IDS); + TypeOffsets[ID - pch::NUM_PREDEF_TYPE_IDS] = S.GetCurrentBitNo(); + } + + RecordData Record; + + // Emit the type's representation. + PCHTypeWriter W(*this, Record); + switch (T->getTypeClass()) { + // For all of the concrete, non-dependent types, call the + // appropriate visitor function. +#define TYPE(Class, Base) \ + case Type::Class: W.Visit##Class##Type(cast<Class##Type>(T)); break; +#define ABSTRACT_TYPE(Class, Base) +#define DEPENDENT_TYPE(Class, Base) +#include "clang/AST/TypeNodes.def" + + // For all of the dependent type nodes (which only occur in C++ + // templates), produce an error. +#define TYPE(Class, Base) +#define DEPENDENT_TYPE(Class, Base) case Type::Class: +#include "clang/AST/TypeNodes.def" + assert(false && "Cannot serialize dependent type nodes"); + break; + } + + // Emit the serialized record. + S.EmitRecord(W.Code, Record); +} + +/// \brief Write a block containing all of the types. +void PCHWriter::WriteTypesBlock(ASTContext &Context) { + // Enter the types block + S.EnterSubblock(pch::TYPES_BLOCK_ID, 2); + + // Emit all of the types in the ASTContext + for (std::vector<Type*>::const_iterator T = Context.getTypes().begin(), + TEnd = Context.getTypes().end(); + T != TEnd; ++T) { + // Builtin types are never serialized. + if (isa<BuiltinType>(*T)) + continue; + + WriteType(*T); + } + + // Exit the types block + S.ExitBlock(); + + // Write the type offsets block + S.EnterSubblock(pch::TYPE_OFFSETS_BLOCK_ID, 2); + S.EmitRecord(pch::TYPE_OFFSET, TypeOffsets); + S.ExitBlock(); +} + +/// \brief Write the block containing all of the declaration IDs +/// lexically declared within the given DeclContext. +/// +/// \returns the offset of the DECL_CONTEXT_LEXICAL block within the +/// bistream, or 0 if no block was written. +uint64_t PCHWriter::WriteDeclContextLexicalBlock(ASTContext &Context, + DeclContext *DC) { + if (DC->decls_begin(Context) == DC->decls_end(Context)) + return 0; + + uint64_t Offset = S.GetCurrentBitNo(); + RecordData Record; + for (DeclContext::decl_iterator D = DC->decls_begin(Context), + DEnd = DC->decls_end(Context); + D != DEnd; ++D) + AddDeclRef(*D, Record); + + S.EmitRecord(pch::DECL_CONTEXT_LEXICAL, Record); + return Offset; +} + +/// \brief Write the block containing all of the declaration IDs +/// visible from the given DeclContext. +/// +/// \returns the offset of the DECL_CONTEXT_VISIBLE block within the +/// bistream, or 0 if no block was written. +uint64_t PCHWriter::WriteDeclContextVisibleBlock(ASTContext &Context, + DeclContext *DC) { + if (DC->getPrimaryContext() != DC) + return 0; + + // Force the DeclContext to build a its name-lookup table. + DC->lookup(Context, DeclarationName()); + + // Serialize the contents of the mapping used for lookup. Note that, + // although we have two very different code paths, the serialized + // representation is the same for both cases: a declaration name, + // followed by a size, followed by references to the visible + // declarations that have that name. + uint64_t Offset = S.GetCurrentBitNo(); + RecordData Record; + StoredDeclsMap *Map = static_cast<StoredDeclsMap*>(DC->getLookupPtr()); + for (StoredDeclsMap::iterator D = Map->begin(), DEnd = Map->end(); + D != DEnd; ++D) { + AddDeclarationName(D->first, Record); + DeclContext::lookup_result Result = D->second.getLookupResult(Context); + Record.push_back(Result.second - Result.first); + for(; Result.first != Result.second; ++Result.first) + AddDeclRef(*Result.first, Record); + } + + if (Record.size() == 0) + return 0; + + S.EmitRecord(pch::DECL_CONTEXT_VISIBLE, Record); + return Offset; +} + +/// \brief Write a block containing all of the declarations. +void PCHWriter::WriteDeclsBlock(ASTContext &Context) { + // Enter the declarations block + S.EnterSubblock(pch::DECLS_BLOCK_ID, 2); + + // Emit all of the declarations. + RecordData Record; + PCHDeclWriter W(*this, Record); + while (!DeclsToEmit.empty()) { + // Pull the next declaration off the queue + Decl *D = DeclsToEmit.front(); + DeclsToEmit.pop(); + + // If this declaration is also a DeclContext, write blocks for the + // declarations that lexically stored inside its context and those + // declarations that are visible from its context. These blocks + // are written before the declaration itself so that we can put + // their offsets into the record for the declaration. + uint64_t LexicalOffset = 0; + uint64_t VisibleOffset = 0; + DeclContext *DC = dyn_cast<DeclContext>(D); + if (DC) { + LexicalOffset = WriteDeclContextLexicalBlock(Context, DC); + VisibleOffset = WriteDeclContextVisibleBlock(Context, DC); + } + + // Determine the ID for this declaration + pch::ID ID = DeclIDs[D]; + if (ID == 0) + ID = DeclIDs.size(); + + unsigned Index = ID - 1; + + // Record the offset for this declaration + if (DeclOffsets.size() == Index) + DeclOffsets.push_back(S.GetCurrentBitNo()); + else if (DeclOffsets.size() < Index) { + DeclOffsets.resize(Index+1); + DeclOffsets[Index] = S.GetCurrentBitNo(); + } + + // Build and emit a record for this declaration + Record.clear(); + W.Code = (pch::DeclCode)0; + W.Visit(D); + if (DC) W.VisitDeclContext(DC, LexicalOffset, VisibleOffset); + assert(W.Code && "Visitor did not set record code"); + S.EmitRecord(W.Code, Record); + } + + // Exit the declarations block + S.ExitBlock(); + + // Write the declaration offsets block + S.EnterSubblock(pch::DECL_OFFSETS_BLOCK_ID, 2); + S.EmitRecord(pch::DECL_OFFSET, DeclOffsets); + S.ExitBlock(); +} + +PCHWriter::PCHWriter(llvm::BitstreamWriter &S) + : S(S), NextTypeID(pch::NUM_PREDEF_TYPE_IDS) { } + +void PCHWriter::WritePCH(ASTContext &Context) { + // Emit the file header. + S.Emit((unsigned)'C', 8); + S.Emit((unsigned)'P', 8); + S.Emit((unsigned)'C', 8); + S.Emit((unsigned)'H', 8); + + // The translation unit is the first declaration we'll emit. + DeclIDs[Context.getTranslationUnitDecl()] = 1; + DeclsToEmit.push(Context.getTranslationUnitDecl()); + + // Write the remaining PCH contents. + S.EnterSubblock(pch::PCH_BLOCK_ID, 2); + WriteTypesBlock(Context); + WriteDeclsBlock(Context); + S.ExitBlock(); +} + +void PCHWriter::AddSourceLocation(SourceLocation Loc, RecordData &Record) { + Record.push_back(Loc.getRawEncoding()); +} + +void PCHWriter::AddAPInt(const llvm::APInt &Value, RecordData &Record) { + Record.push_back(Value.getBitWidth()); + unsigned N = Value.getNumWords(); + const uint64_t* Words = Value.getRawData(); + for (unsigned I = 0; I != N; ++I) + Record.push_back(Words[I]); +} + +void PCHWriter::AddIdentifierRef(const IdentifierInfo *II, RecordData &Record) { + // FIXME: Emit an identifier ID, not the actual string! + const char *Name = II->getName(); + unsigned Len = strlen(Name); + Record.push_back(Len); + Record.insert(Record.end(), Name, Name + Len); +} + +void PCHWriter::AddTypeRef(QualType T, RecordData &Record) { + if (T.isNull()) { + Record.push_back(pch::PREDEF_TYPE_NULL_ID); + return; + } + + if (const BuiltinType *BT = dyn_cast<BuiltinType>(T.getTypePtr())) { + pch::ID ID; + switch (BT->getKind()) { + case BuiltinType::Void: ID = pch::PREDEF_TYPE_VOID_ID; break; + case BuiltinType::Bool: ID = pch::PREDEF_TYPE_BOOL_ID; break; + case BuiltinType::Char_U: ID = pch::PREDEF_TYPE_CHAR_U_ID; break; + case BuiltinType::UChar: ID = pch::PREDEF_TYPE_UCHAR_ID; break; + case BuiltinType::UShort: ID = pch::PREDEF_TYPE_USHORT_ID; break; + case BuiltinType::UInt: ID = pch::PREDEF_TYPE_UINT_ID; break; + case BuiltinType::ULong: ID = pch::PREDEF_TYPE_ULONG_ID; break; + case BuiltinType::ULongLong: ID = pch::PREDEF_TYPE_ULONGLONG_ID; break; + case BuiltinType::Char_S: ID = pch::PREDEF_TYPE_CHAR_S_ID; break; + case BuiltinType::SChar: ID = pch::PREDEF_TYPE_SCHAR_ID; break; + case BuiltinType::WChar: ID = pch::PREDEF_TYPE_WCHAR_ID; break; + case BuiltinType::Short: ID = pch::PREDEF_TYPE_SHORT_ID; break; + case BuiltinType::Int: ID = pch::PREDEF_TYPE_INT_ID; break; + case BuiltinType::Long: ID = pch::PREDEF_TYPE_LONG_ID; break; + case BuiltinType::LongLong: ID = pch::PREDEF_TYPE_LONGLONG_ID; break; + case BuiltinType::Float: ID = pch::PREDEF_TYPE_FLOAT_ID; break; + case BuiltinType::Double: ID = pch::PREDEF_TYPE_DOUBLE_ID; break; + case BuiltinType::LongDouble: ID = pch::PREDEF_TYPE_LONGDOUBLE_ID; break; + case BuiltinType::Overload: ID = pch::PREDEF_TYPE_OVERLOAD_ID; break; + case BuiltinType::Dependent: ID = pch::PREDEF_TYPE_DEPENDENT_ID; break; + } + + Record.push_back((ID << 3) | T.getCVRQualifiers()); + return; + } + + pch::ID &ID = TypeIDs[T.getTypePtr()]; + if (ID == 0) // we haven't seen this type before + ID = NextTypeID++; + + // Encode the type qualifiers in the type reference. + Record.push_back((ID << 3) | T.getCVRQualifiers()); +} + +void PCHWriter::AddDeclRef(const Decl *D, RecordData &Record) { + if (D == 0) { + Record.push_back(0); + return; + } + + pch::ID &ID = DeclIDs[D]; + if (ID == 0) { + // We haven't seen this declaration before. Give it a new ID and + // enqueue it in the list of declarations to emit. + ID = DeclIDs.size(); + DeclsToEmit.push(const_cast<Decl *>(D)); + } + + Record.push_back(ID); +} + +void PCHWriter::AddDeclarationName(DeclarationName Name, RecordData &Record) { + Record.push_back(Name.getNameKind()); + switch (Name.getNameKind()) { + case DeclarationName::Identifier: + AddIdentifierRef(Name.getAsIdentifierInfo(), Record); + break; + + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + assert(false && "Serialization of Objective-C selectors unavailable"); + break; + + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + AddTypeRef(Name.getCXXNameType(), Record); + break; + + case DeclarationName::CXXOperatorName: + Record.push_back(Name.getCXXOverloadedOperator()); + break; + + case DeclarationName::CXXUsingDirective: + // No extra data to emit + break; + } +} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 474c1e490e4..b024d36acc3 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -524,6 +524,12 @@ bool Sema::MergeTypeDefDecl(TypedefDecl *New, Decl *OldD) { return false; } + // __builtin_va_list gets redeclared in the built-in definitions + // buffer when using PCH. Don't complain about such redefinitions. + if (Context.getExternalSource() && + strcmp(SourceMgr.getBufferName(New->getLocation()), "<built-in>") == 0) + return false; + Diag(New->getLocation(), diag::err_redefinition) << New->getDeclName(); Diag(Old->getLocation(), diag::note_previous_definition); return true; diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 0b11d9cf68c..cd825070054 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -878,6 +878,17 @@ Sema::LookupName(Scope *S, DeclarationName Name, LookupNameKind NameKind, // We have a single lookup result. return LookupResult::CreateLookupResult(Context, *I); } + + /// If the context has an external AST source attached, look at + /// translation unit scope. + if (Context.getExternalSource()) { + DeclContext::lookup_iterator I, E; + for (llvm::tie(I, E) + = Context.getTranslationUnitDecl()->lookup(Context, Name); + I != E; ++I) + if (isAcceptableLookupResult(*I, NameKind, IDNS)) + return LookupResult::CreateLookupResult(Context, I, E); + } } else { // Perform C++ unqualified name lookup. std::pair<bool, LookupResult> MaybeResult = diff --git a/clang/test/PCH/variables.c b/clang/test/PCH/variables.c new file mode 100644 index 00000000000..2981a6796a7 --- /dev/null +++ b/clang/test/PCH/variables.c @@ -0,0 +1,6 @@ +// RUN: clang-cc -emit-pch -o %t %S/variables.h && +// RUN: clang-cc -include-pch %t -fsyntax-only -verify %s + +int *ip2 = &x; +float *fp = &ip; // expected-warning{{incompatible pointer types}} + diff --git a/clang/test/PCH/variables.h b/clang/test/PCH/variables.h new file mode 100644 index 00000000000..442b5f64dff --- /dev/null +++ b/clang/test/PCH/variables.h @@ -0,0 +1,5 @@ +// RUN: clang-cc -emit-pch -o variables.h.pch variables.h +extern int x; +extern float y; +extern int *ip; +float z; diff --git a/clang/test/TestRunner.sh b/clang/test/TestRunner.sh index 754ab300603..66c1e1eab52 100755 --- a/clang/test/TestRunner.sh +++ b/clang/test/TestRunner.sh @@ -8,6 +8,7 @@ # # %s - Replaced with the input name of the program, or the program to # execute, as appropriate. +# %S - Replaced with the directory where the input file resides # %prcontext - prcontext.tcl script # %t - temporary file name (derived from testcase name) # @@ -15,6 +16,7 @@ FILENAME=$1 TESTNAME=$1 SUBST=$1 +FILEDIR=`dirname $TESTNAME` OUTPUT=Output/$1.out @@ -78,6 +80,7 @@ grep 'RUN:' $FILENAME | \ -e "s| clang | $CLANG |g" \ -e "s| clang-cc | $CLANGCC |g" \ -e "s|%s|$SUBST|g" \ + -e "s|%S|$FILEDIR|g" \ -e "s|%prcontext|prcontext.tcl|g" \ -e "s|%t|$TEMPOUTPUT|g" > $SCRIPT diff --git a/clang/tools/clang-cc/ASTConsumers.h b/clang/tools/clang-cc/ASTConsumers.h index 970dfecada0..e7bc962bd92 100644 --- a/clang/tools/clang-cc/ASTConsumers.h +++ b/clang/tools/clang-cc/ASTConsumers.h @@ -68,6 +68,11 @@ ASTConsumer *CreateASTSerializer(const std::string& InFile, const std::string& EmitDir, Diagnostic &Diags); +ASTConsumer *CreatePCHGenerator(Diagnostic &Diags, + const LangOptions &Features, + const std::string& InFile, + const std::string& OutFile); + ASTConsumer *CreateBlockRewriter(const std::string& InFile, const std::string& OutFile, Diagnostic &Diags, diff --git a/clang/tools/clang-cc/GeneratePCH.cpp b/clang/tools/clang-cc/GeneratePCH.cpp new file mode 100644 index 00000000000..a2333487188 --- /dev/null +++ b/clang/tools/clang-cc/GeneratePCH.cpp @@ -0,0 +1,78 @@ +//===--- GeneratePCH.cpp - AST Consumer for PCH Generation ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the CreatePCHGenerate function, which creates an +// ASTConsume that generates a PCH file. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/PCHWriter.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTConsumer.h" +#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/System/Path.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Streams.h" +#include <string> + +using namespace clang; +using namespace llvm; + +namespace { + class VISIBILITY_HIDDEN PCHGenerator : public ASTConsumer { + Diagnostic &Diags; + std::string OutFile; + + public: + explicit PCHGenerator(Diagnostic &Diags, const std::string &OutFile) + : Diags(Diags), OutFile(OutFile) { } + + virtual void HandleTranslationUnit(ASTContext &Ctx); + }; +} + +void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) { + if (Diags.hasErrorOccurred()) + return; + + // Write the PCH contents into a buffer + std::vector<unsigned char> Buffer; + BitstreamWriter Stream(Buffer); + PCHWriter Writer(Stream); + + // Emit the PCH file + Writer.WritePCH(Ctx); + + // Open up the PCH file. + std::string ErrMsg; + llvm::raw_fd_ostream Out(OutFile.c_str(), true, ErrMsg); + + if (!ErrMsg.empty()) { + llvm::errs() << "PCH error: " << ErrMsg << "\n"; + return; + } + + // Write the generated bitstream to "Out". + Out.write((char *)&Buffer.front(), Buffer.size()); + + // Make sure it hits disk now. + Out.flush(); +} + +namespace clang { + +ASTConsumer *CreatePCHGenerator(Diagnostic &Diags, + const LangOptions &Features, + const std::string& InFile, + const std::string& OutFile) { + return new PCHGenerator(Diags, OutFile); +} + +} diff --git a/clang/tools/clang-cc/clang-cc.cpp b/clang/tools/clang-cc/clang-cc.cpp index b10323ab735..f8c44d6f7dc 100644 --- a/clang/tools/clang-cc/clang-cc.cpp +++ b/clang/tools/clang-cc/clang-cc.cpp @@ -29,6 +29,7 @@ #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/InitHeaderSearch.h" #include "clang/Frontend/PathDiagnosticClients.h" +#include "clang/Frontend/PCHReader.h" #include "clang/Frontend/TextDiagnosticBuffer.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Analysis/PathDiagnostic.h" @@ -194,6 +195,7 @@ enum ProgActions { DumpRawTokens, // Dump out raw tokens. RunAnalysis, // Run one or more source code analyses. GeneratePTH, // Generate pre-tokenized header. + GeneratePCH, // Generate pre-compiled header. InheritanceView // View C++ inheritance for a specified class. }; @@ -229,6 +231,8 @@ ProgAction(llvm::cl::desc("Choose output type:"), llvm::cl::ZeroOrMore, "Print DeclContexts and their Decls"), clEnumValN(GeneratePTH, "emit-pth", "Generate pre-tokenized header file"), + clEnumValN(GeneratePCH, "emit-pch", + "Generate pre-compiled header file"), clEnumValN(TestSerialization, "test-pickling", "Run prototype serialization code"), clEnumValN(EmitAssembly, "S", @@ -975,6 +979,10 @@ static llvm::cl::opt<std::string> ImplicitIncludePTH("include-pth", llvm::cl::value_desc("file"), llvm::cl::desc("Include file before parsing")); +static llvm::cl::opt<std::string> +ImplicitIncludePCH("include-pch", llvm::cl::value_desc("file"), + llvm::cl::desc("Include precompiled header file")); + // Append a #define line to Buf for Macro. Macro should be of the form XXX, // in which case we emit "#define XXX 1" or "XXX=Y z W" in which case we emit // "#define XXX Y z W". To get a #define with no value, use "XXX=". @@ -1516,6 +1524,9 @@ static ASTConsumer *CreateASTConsumer(const std::string& InFile, // FIXME: Allow user to tailor where the file is written. return CreateASTSerializer(InFile, OutputFile, Diag); + case GeneratePCH: + return CreatePCHGenerator(Diag, LangOpts, InFile, OutputFile); + case RewriteObjC: return CreateCodeRewriterTest(InFile, OutputFile, Diag, LangOpts); @@ -1683,6 +1694,18 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, PP.getSelectorTable(), /* FreeMemory = */ !DisableFree)); + if (!ImplicitIncludePCH.empty()) { + // The user has asked us to include a precompiled header. Load + // the precompiled header into the AST context. + llvm::OwningPtr<PCHReader> Reader( + new clang::PCHReader(*ContextOwner.get())); + if (Reader->ReadPCH(ImplicitIncludePCH)) + return; + + llvm::OwningPtr<ExternalASTSource> Source(Reader.take()); + ContextOwner->setExternalSource(Source); + } + ParseAST(PP, Consumer.get(), *ContextOwner.get(), Stats); if (FixItRewrite) |