From: Douglas Gregor Date: Sat, 18 Apr 2009 00:07:54 +0000 (+0000) Subject: Lazy deserialization of function bodies for PCH files. For the Carbon X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=250fc9c859fdeed3f200ae911a7e7ea338f38436;p=clang Lazy deserialization of function bodies for PCH files. For the Carbon "Hello, World!", this takes us from deserializing 6469 statements/expressions down to deserializing 1 statement/expression. It only translated into a 1% improvement on the Carbon-prefixed 403.gcc, but (a) it's the right thing to do, and (b) we expect this to matter more once we lazily deserialize identifiers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69407 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 083f6c6363..a5c2801b48 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -565,7 +565,7 @@ private: /// FunctionDecl object to save an allocation like FunctionType does. ParmVarDecl **ParamInfo; - Stmt *Body; + LazyStmtPtr Body; /// PreviousDeclaration - A link to the previous declaration of this /// same function, NULL if this is the first declaration. For @@ -641,6 +641,7 @@ public: bool isThisDeclarationADefinition() const { return Body; } void setBody(CompoundStmt *B) { Body = (Stmt*) B; } + void setLazyBody(uint64_t Offset) { Body = Offset; } /// Whether this function is virtual, either by explicit marking, or by /// overriding a virtual function. Only valid on C++ member functions. diff --git a/include/clang/AST/ExternalASTSource.h b/include/clang/AST/ExternalASTSource.h index d5499d7b05..267b4838a4 100644 --- a/include/clang/AST/ExternalASTSource.h +++ b/include/clang/AST/ExternalASTSource.h @@ -55,6 +55,13 @@ public: /// building a new declaration. virtual Decl *GetDecl(unsigned ID) = 0; + /// \brief Resolve the offset of a statement into a statement. + /// + /// This operation will read a new statement from the external + /// source each time it is called, and is meant to be used via a + /// LazyOffsetPtr. + virtual Stmt *GetStmt(uint64_t Offset) = 0; + /// \brief Read all of the declarations lexically stored in a /// declaration context. /// @@ -95,6 +102,72 @@ public: virtual void PrintStats(); }; +/// \brief A lazy pointer to an AST node (of base type T) that resides +/// within an external AST source. +/// +/// The AST node is identified within the external AST source by a +/// 63-bit offset, and can be retrieved via an operation on the +/// external AST source itself. +template +struct LazyOffsetPtr { + /// \brief Either a pointer to an AST node or the offset within the + /// external AST source where the AST node can be found. + /// + /// If the low bit is clear, a pointer to the AST node. If the low + /// bit is set, the upper 63 bits are the offset. + mutable uint64_t Ptr; + +public: + LazyOffsetPtr() : Ptr(0) { } + + explicit LazyOffsetPtr(T *Ptr) : Ptr(reinterpret_cast(Ptr)) { } + explicit LazyOffsetPtr(uint64_t Offset) : Ptr((Offset << 1) | 0x01) { + assert((Offset << 1 >> 1) == Offset && "Offsets must require < 63 bits"); + if (Offset == 0) + Ptr = 0; + } + + LazyOffsetPtr &operator=(T *Ptr) { + this->Ptr = reinterpret_cast(Ptr); + return *this; + } + + LazyOffsetPtr &operator=(uint64_t Offset) { + assert((Offset << 1 >> 1) == Offset && "Offsets must require < 63 bits"); + if (Offset == 0) + Ptr = 0; + else + Ptr = (Offset << 1) | 0x01; + + return *this; + } + + /// \brief Whether this pointer is non-NULL. + /// + /// This operation does not require the AST node to be deserialized. + operator bool() const { return Ptr != 0; } + + /// \brief Whether this pointer is currently stored as an offset. + bool isOffset() const { return Ptr & 0x01; } + + /// \brief Retrieve the pointer to the AST node that this lazy pointer + /// + /// \param Source the external AST source. + /// + /// \returns a pointer to the AST node. + T* get(ExternalASTSource *Source) const { + if (isOffset()) { + assert(Source && + "Cannot deserialize a lazy pointer without an AST source"); + Ptr = reinterpret_cast((Source->*Get)(Ptr >> 1)); + } + return reinterpret_cast(Ptr); + } +}; + +/// \brief A lazy pointer to a statement. +typedef LazyOffsetPtr LazyStmtPtr; + } // end namespace clang #endif // LLVM_CLANG_AST_EXTERNAL_AST_SOURCE_H diff --git a/include/clang/Frontend/PCHReader.h b/include/clang/Frontend/PCHReader.h index c4f90a3c79..34cf7bc1aa 100644 --- a/include/clang/Frontend/PCHReader.h +++ b/include/clang/Frontend/PCHReader.h @@ -190,6 +190,13 @@ public: /// building a new declaration. virtual Decl *GetDecl(pch::DeclID ID); + /// \brief Resolve the offset of a statement into a statement. + /// + /// This operation will read a new statement from the external + /// source each time it is called, and is meant to be used via a + /// LazyOffsetPtr. + virtual Stmt *GetStmt(uint64_t Offset); + /// \brief Read all of the declarations lexically stored in a /// declaration context. /// diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 5d49d706d7..8bda32398f 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -308,8 +308,8 @@ const Expr *VarDecl::getDefinition(const VarDecl *&Def) const { //===----------------------------------------------------------------------===// void FunctionDecl::Destroy(ASTContext& C) { - if (Body) - Body->Destroy(C); + if (Body && Body.isOffset()) + Body.get(C.getExternalSource())->Destroy(C); for (param_iterator I=param_begin(), E=param_end(); I!=E; ++I) (*I)->Destroy(C); @@ -325,7 +325,7 @@ CompoundStmt *FunctionDecl::getBody(ASTContext &Context, for (const FunctionDecl *FD = this; FD != 0; FD = FD->PreviousDeclaration) { if (FD->Body) { Definition = FD; - return cast(FD->Body); + return cast(FD->Body.get(Context.getExternalSource())); } } @@ -334,8 +334,9 @@ CompoundStmt *FunctionDecl::getBody(ASTContext &Context, CompoundStmt *FunctionDecl::getBodyIfAvailable() const { for (const FunctionDecl *FD = this; FD != 0; FD = FD->PreviousDeclaration) { - if (FD->Body) - return cast(FD->Body); + if (FD->Body && !FD->Body.isOffset()) { + return cast(FD->Body.get(0)); + } } return 0; diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp index 5ea1f7c6c2..dc8d3fce34 100644 --- a/lib/Frontend/PCHReader.cpp +++ b/lib/Frontend/PCHReader.cpp @@ -140,7 +140,7 @@ void PCHDeclReader::VisitEnumConstantDecl(EnumConstantDecl *ECD) { void PCHDeclReader::VisitFunctionDecl(FunctionDecl *FD) { VisitValueDecl(FD); if (Record[Idx++]) - FD->setBody(cast(Reader.ReadStmt())); + FD->setLazyBody(Reader.getStream().GetCurrentBitNo()); FD->setPreviousDeclaration( cast_or_null(Reader.GetDecl(Record[Idx++]))); FD->setStorageClass((FunctionDecl::StorageClass)Record[Idx++]); @@ -1880,6 +1880,15 @@ Decl *PCHReader::GetDecl(pch::DeclID ID) { return ReadDeclRecord(DeclOffsets[Index], Index); } +Stmt *PCHReader::GetStmt(uint64_t Offset) { + // Keep track of where we are in the stream, then jump back there + // after reading this declaration. + SavedStreamPosition SavedPosition(Stream); + + Stream.JumpToBit(Offset); + return ReadStmt(); +} + bool PCHReader::ReadDeclsLexicallyInContext(DeclContext *DC, llvm::SmallVectorImpl &Decls) { assert(DC->hasExternalLexicalStorage() &&