PCH support for the global method pool (= instance and factory method
pools, combined). The methods in the global method pool are lazily
loaded from an on-disk hash table when Sema looks into its version of
the hash tables.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69989 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h
index d457760..989fdab 100644
--- a/include/clang/AST/DeclObjC.h
+++ b/include/clang/AST/DeclObjC.h
@@ -259,6 +259,21 @@
}
};
+/// ObjCMethodList - a linked list of methods with different signatures.
+struct ObjCMethodList {
+ ObjCMethodDecl *Method;
+ ObjCMethodList *Next;
+
+ ObjCMethodList() {
+ Method = 0;
+ Next = 0;
+ }
+ ObjCMethodList(ObjCMethodDecl *M, ObjCMethodList *C) {
+ Method = M;
+ Next = C;
+ }
+};
+
/// ObjCContainerDecl - Represents a container for method declarations.
/// Current sub-classes are ObjCInterfaceDecl, ObjCCategoryDecl, and
/// ObjCProtocolDecl.
diff --git a/include/clang/Basic/OnDiskHashTable.h b/include/clang/Basic/OnDiskHashTable.h
index 3caeb9f..f54d670 100644
--- a/include/clang/Basic/OnDiskHashTable.h
+++ b/include/clang/Basic/OnDiskHashTable.h
@@ -41,6 +41,11 @@
return R + (R >> 5);
}
+inline unsigned BernsteinHashPartial(const char* x, unsigned n, unsigned R) {
+ for (unsigned i = 0 ; i < n ; ++i, ++x) R = R * 33 + *x;
+ return R + (R >> 5);
+}
+
namespace io {
typedef uint32_t Offset;
@@ -199,7 +204,8 @@
// Store the offset for the data of this bucket.
B.off = out.tell();
-
+ assert(B.off && "Cannot write a bucket at offset 0. Please add padding.");
+
// Write out the number of items in the bucket.
Emit16(out, B.length);
@@ -318,7 +324,7 @@
// Read the key.
const internal_key_type& X =
- Info::ReadKey((const unsigned char* const) Items, L.first);
+ InfoPtr->ReadKey((const unsigned char* const) Items, L.first);
// If the key doesn't match just skip reading the value.
if (!Info::EqualKey(X, iKey)) {
diff --git a/include/clang/Frontend/PCHBitCodes.h b/include/clang/Frontend/PCHBitCodes.h
index 31a30b8..b84d2ed 100644
--- a/include/clang/Frontend/PCHBitCodes.h
+++ b/include/clang/Frontend/PCHBitCodes.h
@@ -168,7 +168,10 @@
LOCALLY_SCOPED_EXTERNAL_DECLS = 11,
/// \brief Record code for the Objective-C Selector Table.
- SELECTOR_TABLE = 12
+ SELECTOR_TABLE = 12,
+
+ /// \brief Record code for the Objective-C method pool,
+ METHOD_POOL = 13
};
/// \brief Record types used within a source manager block.
diff --git a/include/clang/Frontend/PCHReader.h b/include/clang/Frontend/PCHReader.h
index 05e3ac6..34cf3e1 100644
--- a/include/clang/Frontend/PCHReader.h
+++ b/include/clang/Frontend/PCHReader.h
@@ -16,6 +16,7 @@
#include "clang/Frontend/PCHBitCodes.h"
#include "clang/AST/DeclarationName.h"
#include "clang/Sema/ExternalSemaSource.h"
+#include "clang/AST/DeclObjC.h"
#include "clang/AST/Type.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/IdentifierTable.h"
@@ -148,6 +149,13 @@
/// \brief SelectorData, indexed by the selector ID minus one.
llvm::SmallVector<Selector, 16> SelectorData;
+ /// \brief A pointer to an on-disk hash table of opaque type
+ /// PCHMethodPoolLookupTable.
+ ///
+ /// This hash table provides the instance and factory methods
+ /// associated with every selector known in the PCH file.
+ void *MethodPoolLookupTable;
+
/// \brief The set of external definitions stored in the the PCH
/// file.
llvm::SmallVector<uint64_t, 16> ExternalDefinitions;
@@ -223,7 +231,8 @@
explicit PCHReader(Preprocessor &PP, ASTContext &Context)
: SemaObj(0), PP(PP), Context(Context), Consumer(0),
- IdentifierTableData(0), NumStatementsRead(0), NumMacrosRead(0),
+ IdentifierTableData(0), IdentifierLookupTable(0),
+ MethodPoolLookupTable(0), NumStatementsRead(0), NumMacrosRead(0),
NumLexicalDeclContextsRead(0), NumVisibleDeclContextsRead(0) { }
~PCHReader() {}
@@ -305,6 +314,14 @@
/// the macro.
virtual IdentifierInfo* get(const char *NameStart, const char *NameEnd);
+ /// \brief Load the contents of the global method pool for a given
+ /// selector.
+ ///
+ /// \returns a pair of Objective-C methods lists containing the
+ /// instance and factory methods, respectively, with this selector.
+ virtual std::pair<ObjCMethodList, ObjCMethodList>
+ ReadMethodPool(Selector Sel);
+
void SetIdentifierInfo(unsigned ID, const IdentifierInfo *II);
/// \brief Report a diagnostic.
diff --git a/include/clang/Frontend/PCHWriter.h b/include/clang/Frontend/PCHWriter.h
index d1fe1d4..15c2e10 100644
--- a/include/clang/Frontend/PCHWriter.h
+++ b/include/clang/Frontend/PCHWriter.h
@@ -159,6 +159,7 @@
uint64_t WriteDeclContextLexicalBlock(ASTContext &Context, DeclContext *DC);
uint64_t WriteDeclContextVisibleBlock(ASTContext &Context, DeclContext *DC);
void WriteDeclsBlock(ASTContext &Context);
+ void WriteMethodPool(Sema &SemaRef);
void WriteIdentifierTable(Preprocessor &PP);
void WriteSelectorTable();
void WriteAttributeRecord(const Attr *Attr);
diff --git a/include/clang/Sema/ExternalSemaSource.h b/include/clang/Sema/ExternalSemaSource.h
index 1c216e4..0f0d375 100644
--- a/include/clang/Sema/ExternalSemaSource.h
+++ b/include/clang/Sema/ExternalSemaSource.h
@@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_SEMA_EXTERNAL_SEMA_SOURCE_H
#define LLVM_CLANG_SEMA_EXTERNAL_SEMA_SOURCE_H
+#include "clang/AST/DeclObjC.h"
#include "clang/AST/ExternalASTSource.h"
namespace clang {
@@ -32,6 +33,16 @@
/// being used to perform semantic analysis on the abstract syntax
/// tree.
virtual void InitializeSema(Sema &S) {}
+
+ /// \brief Load the contents of the global method pool for a given
+ /// selector.
+ ///
+ /// \returns a pair of Objective-C methods lists containing the
+ /// instance and factory methods, respectively, with this selector.
+ virtual std::pair<ObjCMethodList, ObjCMethodList>
+ ReadMethodPool(Selector Sel) {
+ return std::pair<ObjCMethodList, ObjCMethodList>();
+ }
// isa/cast/dyn_cast support
static bool classof(const ExternalASTSource *Source) {
diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp
index 62d0ba4..5a1e882 100644
--- a/lib/Frontend/PCHReader.cpp
+++ b/lib/Frontend/PCHReader.cpp
@@ -1085,6 +1085,114 @@
//===----------------------------------------------------------------------===//
namespace {
+class VISIBILITY_HIDDEN PCHMethodPoolLookupTrait {
+ PCHReader &Reader;
+
+public:
+ typedef std::pair<ObjCMethodList, ObjCMethodList> data_type;
+
+ typedef Selector external_key_type;
+ typedef external_key_type internal_key_type;
+
+ explicit PCHMethodPoolLookupTrait(PCHReader &Reader) : Reader(Reader) { }
+
+ static bool EqualKey(const internal_key_type& a,
+ const internal_key_type& b) {
+ return a == b;
+ }
+
+ static unsigned ComputeHash(Selector Sel) {
+ unsigned N = Sel.getNumArgs();
+ if (N == 0)
+ ++N;
+ unsigned R = 5381;
+ for (unsigned I = 0; I != N; ++I)
+ if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(I))
+ R = clang::BernsteinHashPartial(II->getName(), II->getLength(), R);
+ return R;
+ }
+
+ // This hopefully will just get inlined and removed by the optimizer.
+ static const internal_key_type&
+ GetInternalKey(const external_key_type& x) { return x; }
+
+ static std::pair<unsigned, unsigned>
+ ReadKeyDataLength(const unsigned char*& d) {
+ using namespace clang::io;
+ unsigned KeyLen = ReadUnalignedLE16(d);
+ unsigned DataLen = ReadUnalignedLE16(d);
+ return std::make_pair(KeyLen, DataLen);
+ }
+
+ internal_key_type ReadKey(const unsigned char* d, unsigned n) {
+ using namespace clang::io;
+ SelectorTable &SelTable = Reader.getContext().Selectors;
+ unsigned N = ReadUnalignedLE16(d);
+ IdentifierInfo *FirstII
+ = Reader.DecodeIdentifierInfo(ReadUnalignedLE32(d));
+ if (N == 0)
+ return SelTable.getNullarySelector(FirstII);
+ else if (N == 1)
+ return SelTable.getUnarySelector(FirstII);
+
+ llvm::SmallVector<IdentifierInfo *, 16> Args;
+ Args.push_back(FirstII);
+ for (unsigned I = 1; I != N; ++I)
+ Args.push_back(Reader.DecodeIdentifierInfo(ReadUnalignedLE32(d)));
+
+ return SelTable.getSelector(N, &Args[0]);
+ }
+
+ data_type ReadData(Selector, const unsigned char* d, unsigned DataLen) {
+ using namespace clang::io;
+ unsigned NumInstanceMethods = ReadUnalignedLE16(d);
+ unsigned NumFactoryMethods = ReadUnalignedLE16(d);
+
+ data_type Result;
+
+ // Load instance methods
+ ObjCMethodList *Prev = 0;
+ for (unsigned I = 0; I != NumInstanceMethods; ++I) {
+ ObjCMethodDecl *Method
+ = cast<ObjCMethodDecl>(Reader.GetDecl(ReadUnalignedLE32(d)));
+ if (!Result.first.Method) {
+ // This is the first method, which is the easy case.
+ Result.first.Method = Method;
+ Prev = &Result.first;
+ continue;
+ }
+
+ Prev->Next = new ObjCMethodList(Method, 0);
+ Prev = Prev->Next;
+ }
+
+ // Load factory methods
+ Prev = 0;
+ for (unsigned I = 0; I != NumFactoryMethods; ++I) {
+ ObjCMethodDecl *Method
+ = cast<ObjCMethodDecl>(Reader.GetDecl(ReadUnalignedLE32(d)));
+ if (!Result.second.Method) {
+ // This is the first method, which is the easy case.
+ Result.second.Method = Method;
+ Prev = &Result.second;
+ continue;
+ }
+
+ Prev->Next = new ObjCMethodList(Method, 0);
+ Prev = Prev->Next;
+ }
+
+ return Result;
+ }
+};
+
+} // end anonymous namespace
+
+/// \brief The on-disk hash table used for the global method pool.
+typedef OnDiskChainedHashTable<PCHMethodPoolLookupTrait>
+ PCHMethodPoolLookupTable;
+
+namespace {
class VISIBILITY_HIDDEN PCHIdentifierLookupTrait {
PCHReader &Reader;
@@ -1844,6 +1952,14 @@
}
LocallyScopedExternalDecls.swap(Record);
break;
+
+ case pch::METHOD_POOL:
+ MethodPoolLookupTable
+ = PCHMethodPoolLookupTable::Create(
+ (const unsigned char *)BlobStart + Record[0],
+ (const unsigned char *)BlobStart,
+ PCHMethodPoolLookupTrait(*this));
+ break;
}
}
Error("Premature end of bitstream");
@@ -2539,6 +2655,7 @@
return 0;
unsigned Index = ID - 1;
+ assert(Index < DeclAlreadyLoaded.size() && "Declaration ID out of range");
if (DeclAlreadyLoaded[Index])
return reinterpret_cast<Decl *>(DeclOffsets[Index]);
@@ -2679,7 +2796,8 @@
void PCHReader::InitializeSema(Sema &S) {
SemaObj = &S;
-
+ S.ExternalSource = this;
+
// Makes sure any declarations that were deserialized "too early"
// still get added to the identifier's declaration chains.
for (unsigned I = 0, N = PreloadedDecls.size(); I != N; ++I) {
@@ -2719,6 +2837,21 @@
return *Pos;
}
+std::pair<ObjCMethodList, ObjCMethodList>
+PCHReader::ReadMethodPool(Selector Sel) {
+ if (!MethodPoolLookupTable)
+ return std::pair<ObjCMethodList, ObjCMethodList>();
+
+ // Try to find this selector within our on-disk hash table.
+ PCHMethodPoolLookupTable *PoolTable
+ = (PCHMethodPoolLookupTable*)MethodPoolLookupTable;
+ PCHMethodPoolLookupTable::iterator Pos = PoolTable->find(Sel);
+ if (Pos == PoolTable->end())
+ return std::pair<ObjCMethodList, ObjCMethodList>();;
+
+ return *Pos;
+}
+
void PCHReader::SetIdentifierInfo(unsigned ID, const IdentifierInfo *II) {
assert(ID && "Non-zero identifier ID required");
IdentifierData[ID - 1] = reinterpret_cast<uint64_t>(II);
diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp
index de4b999..581ca05 100644
--- a/lib/Frontend/PCHWriter.cpp
+++ b/lib/Frontend/PCHWriter.cpp
@@ -1774,6 +1774,176 @@
}
namespace {
+// Trait used for the on-disk hash table used in the method pool.
+class VISIBILITY_HIDDEN PCHMethodPoolTrait {
+ PCHWriter &Writer;
+
+public:
+ typedef Selector key_type;
+ typedef key_type key_type_ref;
+
+ typedef std::pair<ObjCMethodList, ObjCMethodList> data_type;
+ typedef const data_type& data_type_ref;
+
+ explicit PCHMethodPoolTrait(PCHWriter &Writer) : Writer(Writer) { }
+
+ static unsigned ComputeHash(Selector Sel) {
+ unsigned N = Sel.getNumArgs();
+ if (N == 0)
+ ++N;
+ unsigned R = 5381;
+ for (unsigned I = 0; I != N; ++I)
+ if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(I))
+ R = clang::BernsteinHashPartial(II->getName(), II->getLength(), R);
+ return R;
+ }
+
+ std::pair<unsigned,unsigned>
+ EmitKeyDataLength(llvm::raw_ostream& Out, Selector Sel,
+ data_type_ref Methods) {
+ unsigned KeyLen = 2 + (Sel.getNumArgs()? Sel.getNumArgs() * 4 : 4);
+ clang::io::Emit16(Out, KeyLen);
+ unsigned DataLen = 2 + 2; // 2 bytes for each of the method counts
+ for (const ObjCMethodList *Method = &Methods.first; Method;
+ Method = Method->Next)
+ if (Method->Method)
+ DataLen += 4;
+ for (const ObjCMethodList *Method = &Methods.second; Method;
+ Method = Method->Next)
+ if (Method->Method)
+ DataLen += 4;
+ clang::io::Emit16(Out, DataLen);
+ return std::make_pair(KeyLen, DataLen);
+ }
+
+ void EmitKey(llvm::raw_ostream& Out, Selector Sel, unsigned) {
+ // FIXME: Keep track of the location of the key data (the
+ // selector), so we can fold the selector table's storage into
+ // this hash table.
+ unsigned N = Sel.getNumArgs();
+ clang::io::Emit16(Out, N);
+ if (N == 0)
+ N = 1;
+ for (unsigned I = 0; I != N; ++I)
+ clang::io::Emit32(Out,
+ Writer.getIdentifierRef(Sel.getIdentifierInfoForSlot(I)));
+ }
+
+ void EmitData(llvm::raw_ostream& Out, key_type_ref,
+ data_type_ref Methods, unsigned) {
+ unsigned NumInstanceMethods = 0;
+ for (const ObjCMethodList *Method = &Methods.first; Method;
+ Method = Method->Next)
+ if (Method->Method)
+ ++NumInstanceMethods;
+
+ unsigned NumFactoryMethods = 0;
+ for (const ObjCMethodList *Method = &Methods.second; Method;
+ Method = Method->Next)
+ if (Method->Method)
+ ++NumFactoryMethods;
+
+ clang::io::Emit16(Out, NumInstanceMethods);
+ clang::io::Emit16(Out, NumFactoryMethods);
+ for (const ObjCMethodList *Method = &Methods.first; Method;
+ Method = Method->Next)
+ if (Method->Method)
+ clang::io::Emit32(Out, Writer.getDeclID(Method->Method));
+ clang::io::Emit16(Out, NumFactoryMethods);
+ for (const ObjCMethodList *Method = &Methods.second; Method;
+ Method = Method->Next)
+ if (Method->Method)
+ clang::io::Emit32(Out, Writer.getDeclID(Method->Method));
+ }
+};
+} // end anonymous namespace
+
+/// \brief Write the method pool into the PCH file.
+///
+/// The method pool contains both instance and factory methods, stored
+/// in an on-disk hash table indexed by the selector.
+void PCHWriter::WriteMethodPool(Sema &SemaRef) {
+ using namespace llvm;
+
+ // Create and write out the blob that contains the instance and
+ // factor method pools.
+ bool Empty = true;
+ {
+ OnDiskChainedHashTableGenerator<PCHMethodPoolTrait> Generator;
+
+ // Create the on-disk hash table representation. Start by
+ // iterating through the instance method pool.
+ PCHMethodPoolTrait::key_type Key;
+ for (llvm::DenseMap<Selector, ObjCMethodList>::iterator
+ Instance = SemaRef.InstanceMethodPool.begin(),
+ InstanceEnd = SemaRef.InstanceMethodPool.end();
+ Instance != InstanceEnd; ++Instance) {
+ // Check whether there is a factory method with the same
+ // selector.
+ llvm::DenseMap<Selector, ObjCMethodList>::iterator Factory
+ = SemaRef.FactoryMethodPool.find(Instance->first);
+
+ if (Factory == SemaRef.FactoryMethodPool.end())
+ Generator.insert(Instance->first,
+ std::make_pair(Instance->second,
+ ObjCMethodList()));
+ else
+ Generator.insert(Instance->first,
+ std::make_pair(Instance->second, Factory->second));
+
+ Empty = false;
+ }
+
+ // Now iterate through the factory method pool, to pick up any
+ // selectors that weren't already in the instance method pool.
+ for (llvm::DenseMap<Selector, ObjCMethodList>::iterator
+ Factory = SemaRef.FactoryMethodPool.begin(),
+ FactoryEnd = SemaRef.FactoryMethodPool.end();
+ Factory != FactoryEnd; ++Factory) {
+ // Check whether there is an instance method with the same
+ // selector. If so, there is no work to do here.
+ llvm::DenseMap<Selector, ObjCMethodList>::iterator Instance
+ = SemaRef.InstanceMethodPool.find(Factory->first);
+
+ if (Instance == SemaRef.InstanceMethodPool.end())
+ Generator.insert(Factory->first,
+ std::make_pair(ObjCMethodList(), Factory->second));
+
+ Empty = false;
+ }
+
+ if (Empty)
+ return;
+
+ // Create the on-disk hash table in a buffer.
+ llvm::SmallVector<char, 4096> MethodPool;
+ uint32_t BucketOffset;
+ {
+ PCHMethodPoolTrait Trait(*this);
+ llvm::raw_svector_ostream Out(MethodPool);
+ // Make sure that no bucket is at offset 0
+ clang::io::Emit16(Out, 0);
+ BucketOffset = Generator.Emit(Out, Trait);
+ }
+
+ // Create a blob abbreviation
+ BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
+ Abbrev->Add(BitCodeAbbrevOp(pch::METHOD_POOL));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
+ unsigned MethodPoolAbbrev = Stream.EmitAbbrev(Abbrev);
+
+ // Write the identifier table
+ RecordData Record;
+ Record.push_back(pch::METHOD_POOL);
+ Record.push_back(BucketOffset);
+ Stream.EmitRecordWithBlob(MethodPoolAbbrev, Record,
+ &MethodPool.front(),
+ MethodPool.size());
+ }
+}
+
+namespace {
class VISIBILITY_HIDDEN PCHIdentifierTableTrait {
PCHWriter &Writer;
Preprocessor &PP;
@@ -1880,6 +2050,8 @@
{
PCHIdentifierTableTrait Trait(*this, PP);
llvm::raw_svector_ostream Out(IdentifierTable);
+ // Make sure that no bucket is at offset 0
+ clang::io::Emit16(Out, 0);
BucketOffset = Generator.Emit(Out, Trait);
}
@@ -2113,6 +2285,7 @@
WritePreprocessor(PP);
WriteTypesBlock(Context);
WriteDeclsBlock(Context);
+ WriteMethodPool(SemaRef);
WriteSelectorTable();
WriteIdentifierTable(PP);
Stream.EmitRecord(pch::TYPE_OFFSET, TypeOffsets);
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 3702c89..367c9f5 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -163,8 +163,8 @@
Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
bool CompleteTranslationUnit)
: LangOpts(pp.getLangOptions()), PP(pp), Context(ctxt), Consumer(consumer),
- Diags(PP.getDiagnostics()),
- SourceMgr(PP.getSourceManager()), CurContext(0), PreDeclaratorDC(0),
+ Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()),
+ ExternalSource(0), CurContext(0), PreDeclaratorDC(0),
CurBlock(0), PackContext(0), IdResolver(pp.getLangOptions()),
GlobalNewDeleteDeclared(false),
CompleteTranslationUnit(CompleteTranslationUnit) {
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index b3cb5e2..7426965 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -19,13 +19,13 @@
#include "CXXFieldCollector.h"
#include "SemaOverload.h"
#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclObjC.h"
#include "clang/Parse/Action.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/OwningPtr.h"
-#include "clang/AST/DeclObjC.h"
#include <string>
#include <vector>
@@ -40,6 +40,7 @@
class Decl;
class DeclContext;
class DeclSpec;
+ class ExternalSemaSource;
class NamedDecl;
class Stmt;
class Expr;
@@ -128,6 +129,9 @@
Diagnostic &Diags;
SourceManager &SourceMgr;
+ /// \brief Source of additional semantic information.
+ ExternalSemaSource *ExternalSource;
+
/// CurContext - This is the current declaration context of parsing.
DeclContext *CurContext;
@@ -243,27 +247,17 @@
/// unit.
bool CompleteTranslationUnit;
- /// ObjCMethodList - a linked list of methods with different signatures.
- struct ObjCMethodList {
- ObjCMethodDecl *Method;
- ObjCMethodList *Next;
-
- ObjCMethodList() {
- Method = 0;
- Next = 0;
- }
- ObjCMethodList(ObjCMethodDecl *M, ObjCMethodList *C) {
- Method = M;
- Next = C;
- }
- };
+ typedef llvm::DenseMap<Selector, ObjCMethodList> MethodPool;
+
/// Instance/Factory Method Pools - allows efficient lookup when typechecking
/// messages to "id". We need to maintain a list, since selectors can have
/// differing signatures across classes. In Cocoa, this happens to be
/// extremely uncommon (only 1% of selectors are "overloaded").
- llvm::DenseMap<Selector, ObjCMethodList> InstanceMethodPool;
- llvm::DenseMap<Selector, ObjCMethodList> FactoryMethodPool;
+ MethodPool InstanceMethodPool;
+ MethodPool FactoryMethodPool;
+ MethodPool::iterator ReadMethodPool(Selector Sel, bool isInstance);
+
/// Private Helper predicate to check for 'self'.
bool isSelfExpr(Expr *RExpr);
public:
@@ -1126,6 +1120,10 @@
/// LookupInstanceMethodInGlobalPool - Returns the method and warns if
/// there are multiple signatures.
ObjCMethodDecl *LookupInstanceMethodInGlobalPool(Selector Sel, SourceRange R);
+
+ /// LookupFactoryMethodInGlobalPool - Returns the method and warns if
+ /// there are multiple signatures.
+ ObjCMethodDecl *LookupFactoryMethodInGlobalPool(Selector Sel, SourceRange R);
/// AddFactoryMethodToGlobalPool - Same as above, but for factory methods.
void AddFactoryMethodToGlobalPool(ObjCMethodDecl *Method);
diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp
index f4014d1..05e471b 100644
--- a/lib/Sema/SemaDeclObjC.cpp
+++ b/lib/Sema/SemaDeclObjC.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "Sema.h"
+#include "clang/Sema/ExternalSemaSource.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
@@ -1090,8 +1091,47 @@
return true;
}
+/// \brief Read the contents of the instance and factory method pools
+/// for a given selector from external storage.
+///
+/// This routine should only be called once, when neither the instance
+/// nor the factory method pool has an entry for this selector.
+Sema::MethodPool::iterator Sema::ReadMethodPool(Selector Sel,
+ bool isInstance) {
+ assert(ExternalSource && "We need an external AST source");
+ assert(InstanceMethodPool.find(Sel) == InstanceMethodPool.end() &&
+ "Selector data already loaded into the instance method pool");
+ assert(FactoryMethodPool.find(Sel) == FactoryMethodPool.end() &&
+ "Selector data already loaded into the factory method pool");
+
+ // Read the method list from the external source.
+ std::pair<ObjCMethodList, ObjCMethodList> Methods
+ = ExternalSource->ReadMethodPool(Sel);
+
+ if (isInstance) {
+ if (Methods.second.Method)
+ FactoryMethodPool[Sel] = Methods.second;
+ return InstanceMethodPool.insert(std::make_pair(Sel, Methods.first)).first;
+ }
+
+ if (Methods.first.Method)
+ InstanceMethodPool[Sel] = Methods.first;
+
+ return FactoryMethodPool.insert(std::make_pair(Sel, Methods.second)).first;
+}
+
void Sema::AddInstanceMethodToGlobalPool(ObjCMethodDecl *Method) {
- ObjCMethodList &Entry = InstanceMethodPool[Method->getSelector()];
+ llvm::DenseMap<Selector, ObjCMethodList>::iterator Pos
+ = InstanceMethodPool.find(Method->getSelector());
+ if (Pos == InstanceMethodPool.end()) {
+ if (ExternalSource && !FactoryMethodPool.count(Method->getSelector()))
+ Pos = ReadMethodPool(Method->getSelector(), /*isInstance=*/true);
+ else
+ Pos = InstanceMethodPool.insert(std::make_pair(Method->getSelector(),
+ ObjCMethodList())).first;
+ }
+
+ ObjCMethodList &Entry = Pos->second;
if (Entry.Method == 0) {
// Haven't seen a method with this selector name yet - add it.
Entry.Method = Method;
@@ -1113,7 +1153,16 @@
// FIXME: Finish implementing -Wno-strict-selector-match.
ObjCMethodDecl *Sema::LookupInstanceMethodInGlobalPool(Selector Sel,
SourceRange R) {
- ObjCMethodList &MethList = InstanceMethodPool[Sel];
+ llvm::DenseMap<Selector, ObjCMethodList>::iterator Pos
+ = InstanceMethodPool.find(Sel);
+ if (Pos == InstanceMethodPool.end() && !FactoryMethodPool.count(Sel)) {
+ if (ExternalSource)
+ Pos = ReadMethodPool(Sel, /*isInstance=*/true);
+ else
+ return 0;
+ }
+
+ ObjCMethodList &MethList = Pos->second;
bool issueWarning = false;
if (MethList.Method && MethList.Next) {
@@ -1134,7 +1183,17 @@
}
void Sema::AddFactoryMethodToGlobalPool(ObjCMethodDecl *Method) {
- ObjCMethodList &FirstMethod = FactoryMethodPool[Method->getSelector()];
+ llvm::DenseMap<Selector, ObjCMethodList>::iterator Pos
+ = FactoryMethodPool.find(Method->getSelector());
+ if (Pos == FactoryMethodPool.end()) {
+ if (ExternalSource && !InstanceMethodPool.count(Method->getSelector()))
+ Pos = ReadMethodPool(Method->getSelector(), /*isInstance=*/false);
+ else
+ Pos = FactoryMethodPool.insert(std::make_pair(Method->getSelector(),
+ ObjCMethodList())).first;
+ }
+
+ ObjCMethodList &FirstMethod = Pos->second;
if (!FirstMethod.Method) {
// Haven't seen a method with this selector name yet - add it.
FirstMethod.Method = Method;
@@ -1156,6 +1215,37 @@
}
}
+ObjCMethodDecl *Sema::LookupFactoryMethodInGlobalPool(Selector Sel,
+ SourceRange R) {
+ llvm::DenseMap<Selector, ObjCMethodList>::iterator Pos
+ = FactoryMethodPool.find(Sel);
+ if (Pos == FactoryMethodPool.end()) {
+ if (ExternalSource && !InstanceMethodPool.count(Sel))
+ Pos = ReadMethodPool(Sel, /*isInstance=*/false);
+ else
+ return 0;
+ }
+
+ ObjCMethodList &MethList = Pos->second;
+ bool issueWarning = false;
+
+ if (MethList.Method && MethList.Next) {
+ for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next)
+ // This checks if the methods differ by size & alignment.
+ if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, true))
+ issueWarning = true;
+ }
+ if (issueWarning && (MethList.Method && MethList.Next)) {
+ Diag(R.getBegin(), diag::warn_multiple_method_decl) << Sel << R;
+ Diag(MethList.Method->getLocStart(), diag::note_using_decl)
+ << MethList.Method->getSourceRange();
+ for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next)
+ Diag(Next->Method->getLocStart(), diag::note_also_found_decl)
+ << Next->Method->getSourceRange();
+ }
+ return MethList.Method;
+}
+
/// ProcessPropertyDecl - Make sure that any user-defined setter/getter methods
/// have the property type and issue diagnostics if they don't.
/// Also synthesize a getter/setter method if none exist (and update the
diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp
index d48ba44..f2cd001 100644
--- a/lib/Sema/SemaExprObjC.cpp
+++ b/lib/Sema/SemaExprObjC.cpp
@@ -497,7 +497,7 @@
ObjCMethodDecl *Method = LookupInstanceMethodInGlobalPool(
Sel, SourceRange(lbrac,rbrac));
if (!Method)
- Method = FactoryMethodPool[Sel].Method;
+ Method = LookupFactoryMethodInGlobalPool(Sel, SourceRange(lbrac, rbrac));
if (CheckMessageArgumentTypes(ArgExprs, NumArgs, Sel, Method, false,
lbrac, rbrac, returnType))
return true;
@@ -523,7 +523,7 @@
if (!Method) {
// If not messaging 'self', look for any factory method named 'Sel'.
if (!isSelfExpr(RExpr)) {
- Method = FactoryMethodPool[Sel].Method;
+ Method = LookupFactoryMethodInGlobalPool(Sel, SourceRange(lbrac,rbrac));
if (!Method) {
Method = LookupInstanceMethodInGlobalPool(
Sel, SourceRange(lbrac,rbrac));
diff --git a/test/PCH/method_pool.h b/test/PCH/method_pool.h
new file mode 100644
index 0000000..f7af904
--- /dev/null
+++ b/test/PCH/method_pool.h
@@ -0,0 +1,37 @@
+/* For use with the method_pool.m test */
+
+/* Whitespace below is significant */
+
+
+
+
+
+
+
+
+
+
+
+@interface TestMethodPool1
++ alloc;
+- (double)instMethod:(int)foo;
+@end
+
+@interface TestMethodPool2
+- (char)instMethod:(int)foo;
+@end
+
+@implementation TestMethodPool1
++ alloc {
+}
+
+- (double)instMethod:(int)foo {
+ return foo;
+}
+@end
+
+@implementation TestMethodPool2
+- (char)instMethod:(int)foo {
+ return foo;
+}
+@end
diff --git a/test/PCH/method_pool.m b/test/PCH/method_pool.m
new file mode 100644
index 0000000..8dd7834
--- /dev/null
+++ b/test/PCH/method_pool.m
@@ -0,0 +1,21 @@
+// Test this without pch.
+// RUN: clang-cc -include %S/method_pool.h -fsyntax-only -verify %s &&
+
+// Test with pch.
+// RUN: clang-cc -x=objective-c -emit-pch -o %t %S/method_pool.h &&
+// RUN: clang-cc -include-pch %t -fsyntax-only -verify %s
+
+int message_id(id x) {
+ return [x instMethod:17]; // expected-warning{{multiple methods}}
+}
+
+
+
+
+
+/* Whitespace below is significant */
+/* expected-note{{using}} */
+
+
+
+/* expected-note{{also}} */