mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 04:46:06 +00:00
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. llvm-svn: 69989
This commit is contained in:
parent
969bc68195
commit
c78d34699f
@ -259,6 +259,21 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// 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.
|
||||
|
@ -41,6 +41,11 @@ inline unsigned BernsteinHash(const char* x, unsigned n) {
|
||||
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 @@ public:
|
||||
|
||||
// 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 @@ public:
|
||||
|
||||
// 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)) {
|
||||
|
@ -168,7 +168,10 @@ namespace clang {
|
||||
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.
|
||||
|
@ -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 @@ private:
|
||||
/// \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 @@ public:
|
||||
|
||||
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 @@ public:
|
||||
/// 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.
|
||||
|
@ -159,6 +159,7 @@ private:
|
||||
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);
|
||||
|
@ -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 @@ public:
|
||||
/// 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) {
|
||||
|
@ -1084,6 +1084,114 @@ unsigned PCHStmtReader::VisitObjCProtocolExpr(ObjCProtocolExpr *E) {
|
||||
// PCH reader implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
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 @@ PCHReader::ReadPCHBlock(uint64_t &PreprocessorBlockOffset,
|
||||
}
|
||||
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 @@ Decl *PCHReader::GetDecl(pch::DeclID ID) {
|
||||
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::PrintStats() {
|
||||
|
||||
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 @@ IdentifierInfo* PCHReader::get(const char *NameStart, const char *NameEnd) {
|
||||
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);
|
||||
|
@ -1773,6 +1773,176 @@ void PCHWriter::WriteDeclsBlock(ASTContext &Context) {
|
||||
Stream.ExitBlock();
|
||||
}
|
||||
|
||||
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;
|
||||
@ -1880,6 +2050,8 @@ void PCHWriter::WriteIdentifierTable(Preprocessor &PP) {
|
||||
{
|
||||
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 @@ void PCHWriter::WritePCH(Sema &SemaRef) {
|
||||
WritePreprocessor(PP);
|
||||
WriteTypesBlock(Context);
|
||||
WriteDeclsBlock(Context);
|
||||
WriteMethodPool(SemaRef);
|
||||
WriteSelectorTable();
|
||||
WriteIdentifierTable(PP);
|
||||
Stream.EmitRecord(pch::TYPE_OFFSET, TypeOffsets);
|
||||
|
@ -163,8 +163,8 @@ void Sema::ActOnTranslationUnitScope(SourceLocation Loc, Scope *S) {
|
||||
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) {
|
||||
|
@ -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 @@ namespace clang {
|
||||
class Decl;
|
||||
class DeclContext;
|
||||
class DeclSpec;
|
||||
class ExternalSemaSource;
|
||||
class NamedDecl;
|
||||
class Stmt;
|
||||
class Expr;
|
||||
@ -128,6 +129,9 @@ public:
|
||||
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 @@ public:
|
||||
/// 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 @@ public:
|
||||
/// 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);
|
||||
|
@ -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 @@ bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *Method,
|
||||
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 @@ void Sema::AddInstanceMethodToGlobalPool(ObjCMethodDecl *Method) {
|
||||
// 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 @@ ObjCMethodDecl *Sema::LookupInstanceMethodInGlobalPool(Selector Sel,
|
||||
}
|
||||
|
||||
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 @@ void Sema::AddFactoryMethodToGlobalPool(ObjCMethodDecl *Method) {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -497,7 +497,7 @@ Sema::ExprResult Sema::ActOnInstanceMessage(ExprTy *receiver, Selector Sel,
|
||||
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 @@ Sema::ExprResult Sema::ActOnInstanceMessage(ExprTy *receiver, Selector Sel,
|
||||
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));
|
||||
|
37
clang/test/PCH/method_pool.h
Normal file
37
clang/test/PCH/method_pool.h
Normal file
@ -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
|
21
clang/test/PCH/method_pool.m
Normal file
21
clang/test/PCH/method_pool.m
Normal file
@ -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}} */
|
Loading…
x
Reference in New Issue
Block a user