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/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);