When we load a function or method body from an AST file, we check

whether that function/method already has a body (loaded from some
other AST file), as introduced in r165137. Delay this check until
after the redeclaration chains have been wired up.

While I'm here, make the loading of method bodies lazy.

llvm-svn: 165513
This commit is contained in:
Douglas Gregor 2012-10-09 17:21:28 +00:00
parent 7eaf29c61e
commit a6017bbd52
8 changed files with 61 additions and 25 deletions

View File

@ -174,8 +174,7 @@ private:
SourceLocation DeclEndLoc; // the location of the ';' or '{'.
// The following are only used for method definitions, null otherwise.
// FIXME: space savings opportunity, consider a sub-class.
Stmt *Body;
LazyDeclStmtPtr Body;
/// SelfDecl - Decl for the implicit self parameter. This is lazily
/// constructed by createImplicitParams.
@ -242,7 +241,7 @@ private:
SelLocsKind(SelLoc_StandardNoSpace), IsOverriding(0),
MethodDeclType(T), ResultTInfo(ResultTInfo),
ParamsAndSelLocs(0), NumParams(0),
DeclEndLoc(endLoc), Body(0), SelfDecl(0), CmdDecl(0) {
DeclEndLoc(endLoc), Body(), SelfDecl(0), CmdDecl(0) {
setImplicit(isImplicitlyDeclared);
}
@ -427,10 +426,15 @@ public:
return ImplementationControl(DeclImplementation);
}
virtual Stmt *getBody() const {
return (Stmt*) Body;
}
CompoundStmt *getCompoundBody() { return (CompoundStmt*)Body; }
/// \brief Determine whether this method has a body.
virtual bool hasBody() const { return Body; }
/// \brief Retrieve the body of this method, if it has one.
virtual Stmt *getBody() const;
void setLazyBody(uint64_t Offset) { Body = Offset; }
CompoundStmt *getCompoundBody() { return (CompoundStmt*)getBody(); }
void setBody(Stmt *B) { Body = B; }
/// \brief Returns whether this specific method is a definition.

View File

@ -342,6 +342,9 @@ private:
/// declarations that have not yet been linked to their definitions.
llvm::SmallPtrSet<Decl *, 4> PendingDefinitions;
/// \brief Functions or methods that have bodies that will be attached.
llvm::SmallDenseMap<Decl *, uint64_t, 4> PendingBodies;
/// \brief Read the records that describe the contents of declcontexts.
bool ReadDeclContextStorage(ModuleFile &M,
llvm::BitstreamCursor &Cursor,

View File

@ -445,6 +445,10 @@ ObjCMethodDecl *ObjCMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
Selector(), QualType(), 0, 0);
}
Stmt *ObjCMethodDecl::getBody() const {
return Body.get(getASTContext().getExternalSource());
}
void ObjCMethodDecl::setAsRedeclaration(const ObjCMethodDecl *PrevMethod) {
assert(PrevMethod);
getASTContext().setObjCMethodRedeclaration(PrevMethod, this);

View File

@ -6480,6 +6480,26 @@ void ASTReader::finishPendingActions() {
R->Common = RTD->Common;
}
PendingDefinitions.clear();
// Load the bodies of any functions or methods we've encountered. We do
// this now (delayed) so that we can be sure that the declaration chains
// have been fully wired up.
for (llvm::SmallDenseMap<Decl *, uint64_t, 4>::iterator
PB = PendingBodies.begin(), PBEnd = PendingBodies.end();
PB != PBEnd; ++PB) {
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(PB->first)) {
// FIXME: Check for =delete/=default?
// FIXME: Complain about ODR violations here?
if (!getContext().getLangOpts().Modules || !FD->hasBody())
FD->setLazyBody(PB->second);
continue;
}
ObjCMethodDecl *MD = cast<ObjCMethodDecl>(PB->first);
if (!getContext().getLangOpts().Modules || !MD->hasBody())
MD->setLazyBody(PB->second);
}
PendingBodies.clear();
}
void ASTReader::FinishedDeserializing() {

View File

@ -47,6 +47,8 @@ namespace clang {
DeclID DeclContextIDForTemplateParmDecl;
DeclID LexicalDeclContextIDForTemplateParmDecl;
bool HasPendingBody;
uint64_t GetCurrentCursorOffset();
SourceLocation ReadSourceLocation(const RecordData &R, unsigned &I) {
@ -198,11 +200,14 @@ namespace clang {
const RecordData &Record, unsigned &Idx)
: Reader(Reader), F(F), ThisDeclID(thisDeclID),
RawLocation(RawLocation), Record(Record), Idx(Idx),
TypeIDForTypeDecl(0) { }
TypeIDForTypeDecl(0), HasPendingBody(false) { }
static void attachPreviousDecl(Decl *D, Decl *previous);
static void attachLatestDecl(Decl *D, Decl *latest);
/// \brief Determine whether this declaration has a pending body.
bool hasPendingBody() const { return HasPendingBody; }
void Visit(Decl *D);
void UpdateDecl(Decl *D, ModuleFile &ModuleFile,
@ -324,9 +329,10 @@ void ASTDeclReader::Visit(Decl *D) {
// module).
// FIXME: Also consider = default and = delete.
// FIXME: Can we diagnose ODR violations somehow?
if (Record[Idx++] &&
(!Reader.getContext().getLangOpts().Modules || !FD->hasBody()))
FD->setLazyBody(GetCurrentCursorOffset());
if (Record[Idx++]) {
Reader.PendingBodies[FD] = GetCurrentCursorOffset();
HasPendingBody = true;
}
} else if (D->isTemplateParameter()) {
// If we have a fully initialized template parameter, we can now
// set its DeclContext.
@ -634,13 +640,10 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) {
VisitNamedDecl(MD);
if (Record[Idx++]) {
// In practice, this won't be executed (since method definitions
// don't occur in header files).
// Switch case IDs for this method body.
ASTReader::SwitchCaseMapTy SwitchCaseStmtsForObjCMethod;
SaveAndRestore<ASTReader::SwitchCaseMapTy *>
SCFOM(Reader.CurrSwitchCaseStmts, &SwitchCaseStmtsForObjCMethod);
MD->setBody(Reader.ReadStmt(F));
// Load the body on-demand. Most clients won't care, because method
// definitions rarely show up in headers.
Reader.PendingBodies[MD] = GetCurrentCursorOffset();
HasPendingBody = true;
MD->setSelfDecl(ReadDeclAs<ImplicitParamDecl>(Record, Idx));
MD->setCmdDecl(ReadDeclAs<ImplicitParamDecl>(Record, Idx));
}
@ -1662,7 +1665,7 @@ inline void ASTReader::LoadedDecl(unsigned Index, Decl *D) {
/// This routine should return true for anything that might affect
/// code generation, e.g., inline function definitions, Objective-C
/// declarations with metadata, etc.
static bool isConsumerInterestedIn(Decl *D) {
static bool isConsumerInterestedIn(Decl *D, bool HasBody) {
// An ObjCMethodDecl is never considered as "interesting" because its
// implementation container always is.
@ -1674,7 +1677,7 @@ static bool isConsumerInterestedIn(Decl *D) {
return Var->isFileVarDecl() &&
Var->isThisDeclarationADefinition() == VarDecl::Definition;
if (FunctionDecl *Func = dyn_cast<FunctionDecl>(D))
return Func->doesThisDeclarationHaveABody();
return Func->doesThisDeclarationHaveABody() || HasBody;
return false;
}
@ -2162,7 +2165,7 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) {
// AST consumer might need to know about, queue it.
// We don't pass it to the consumer immediately because we may be in recursive
// loading, and some declarations may still be initializing.
if (isConsumerInterestedIn(D))
if (isConsumerInterestedIn(D, Reader.hasPendingBody()))
InterestingDecls.push_back(D);
return D;

View File

@ -60,7 +60,7 @@ typedef int T1;
typedef float T2;
int func0(int);
int func1(int);
int func1(int x) { return x; }
int func2(int);

View File

@ -65,7 +65,7 @@ typedef double T2;
int func0(int);
int func1(int);
int func1(int);
int func1(int);
int func1(int x) { return x; }
int func1(int);
static int func2(int);

View File

@ -14,3 +14,5 @@
struct S1;
struct S2;
struct S2;
int func1(int);