[NamedLazyMemberLoading] Write serialized decl member tables.

This commit is contained in:
Graydon Hoare
2017-10-28 12:45:43 -07:00
parent c78e04cd4f
commit 5eca5f5161
5 changed files with 235 additions and 16 deletions

View File

@@ -37,6 +37,7 @@ namespace swift {
bool AutolinkForceLoad = false; bool AutolinkForceLoad = false;
bool EnableNestedTypeLookupTable = false; bool EnableNestedTypeLookupTable = false;
bool EnableDeclMemberNamesTable = false;
bool SerializeAllSIL = false; bool SerializeAllSIL = false;
bool SerializeOptionsForDebugging = false; bool SerializeOptionsForDebugging = false;
bool IsSIB = false; bool IsSIB = false;

View File

@@ -835,6 +835,8 @@ static bool performCompile(CompilerInstance &Instance,
serializationOpts.OutputPath = opts.ModuleOutputPath.c_str(); serializationOpts.OutputPath = opts.ModuleOutputPath.c_str();
serializationOpts.SerializeAllSIL = true; serializationOpts.SerializeAllSIL = true;
serializationOpts.IsSIB = true; serializationOpts.IsSIB = true;
serializationOpts.EnableDeclMemberNamesTable =
Invocation.getLangOptions().NamedLazyMemberLoading;
serialize(DC, serializationOpts, SM.get()); serialize(DC, serializationOpts, SM.get());
} }
@@ -899,6 +901,8 @@ static bool performCompile(CompilerInstance &Instance,
Invocation.getClangImporterOptions().ExtraArgs; Invocation.getClangImporterOptions().ExtraArgs;
serializationOpts.EnableNestedTypeLookupTable = serializationOpts.EnableNestedTypeLookupTable =
opts.EnableSerializationNestedTypeLookupTable; opts.EnableSerializationNestedTypeLookupTable;
serializationOpts.EnableDeclMemberNamesTable =
Invocation.getLangOptions().NamedLazyMemberLoading;
if (!IRGenOpts.ForceLoadSymbolName.empty()) if (!IRGenOpts.ForceLoadSymbolName.empty())
serializationOpts.AutolinkForceLoad = true; serializationOpts.AutolinkForceLoad = true;
@@ -977,6 +981,8 @@ static bool performCompile(CompilerInstance &Instance,
serializationOpts.OutputPath = opts.ModuleOutputPath.c_str(); serializationOpts.OutputPath = opts.ModuleOutputPath.c_str();
serializationOpts.SerializeAllSIL = true; serializationOpts.SerializeAllSIL = true;
serializationOpts.IsSIB = true; serializationOpts.IsSIB = true;
serializationOpts.EnableDeclMemberNamesTable =
Invocation.getLangOptions().NamedLazyMemberLoading;
serialize(DC, serializationOpts, SM.get()); serialize(DC, serializationOpts, SM.get());
} }

View File

@@ -540,7 +540,7 @@ public:
static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&data) { static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&data) {
unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data);
return { keyLength, sizeof(serialization::BitOffset) }; return { keyLength, sizeof(uint32_t) };
} }
static internal_key_type ReadKey(const uint8_t *data, unsigned length) { static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
@@ -562,8 +562,8 @@ public:
static data_type ReadData(internal_key_type key, const uint8_t *data, static data_type ReadData(internal_key_type key, const uint8_t *data,
unsigned length) { unsigned length) {
assert(length == sizeof(serialization::BitOffset)); assert(length == sizeof(uint32_t));
return endian::readNext<serialization::BitOffset, little, unaligned>(data); return endian::readNext<uint32_t, little, unaligned>(data);
} }
}; };

View File

@@ -72,6 +72,19 @@ static constexpr bool declIDFitsIn32Bits() {
return PtrIntInfo::digits - DeclIDTraits::NumLowBitsAvailable <= Int32Info::digits; return PtrIntInfo::digits - DeclIDTraits::NumLowBitsAvailable <= Int32Info::digits;
} }
/// Used for static_assert.
static constexpr bool bitOffsetFitsIn32Bits() {
// FIXME: Considering BitOffset is a _bit_ offset, and we're storing it in 31
// bits of a PointerEmbeddedInt, the maximum offset inside a modulefile we can
// handle happens at 2**28 _bytes_, which is only 268MB. Considering
// Swift.swiftmodule is itself 25MB, it seems entirely possible users will
// exceed this limit.
using Int32Info = std::numeric_limits<uint32_t>;
using PtrIntInfo = std::numeric_limits<uintptr_t>;
using BitOffsetTraits = llvm::PointerLikeTypeTraits<BitOffset>;
return PtrIntInfo::digits - BitOffsetTraits::NumLowBitsAvailable <= Int32Info::digits;
}
namespace { namespace {
/// Used to serialize the on-disk decl hash table. /// Used to serialize the on-disk decl hash table.
class DeclTableInfo { class DeclTableInfo {
@@ -285,6 +298,109 @@ namespace {
} }
} }
}; };
class DeclMemberNamesTableInfo {
public:
using key_type = DeclBaseName;
using key_type_ref = const key_type &;
using data_type = BitOffset; // Offsets to sub-tables
using data_type_ref = const data_type &;
using hash_value_type = uint32_t;
using offset_type = unsigned;
hash_value_type ComputeHash(key_type_ref key) {
switch (key.getKind()) {
case DeclBaseName::Kind::Normal:
assert(!key.empty());
return llvm::HashString(key.getIdentifier().str());
case DeclBaseName::Kind::Subscript:
return static_cast<uint8_t>(DeclNameKind::Subscript);
case DeclBaseName::Kind::Destructor:
return static_cast<uint8_t>(DeclNameKind::Destructor);
}
}
std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out,
key_type_ref key,
data_type_ref data) {
uint32_t keyLength = sizeof(uint8_t); // For the flag of the name's kind
if (key.getKind() == DeclBaseName::Kind::Normal) {
keyLength += key.getIdentifier().str().size(); // The name's length
}
uint32_t dataLength = sizeof(data_type);
endian::Writer<little> writer(out);
writer.write<uint16_t>(keyLength);
writer.write<uint16_t>(dataLength);
return { keyLength, dataLength };
}
void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
endian::Writer<little> writer(out);
switch (key.getKind()) {
case DeclBaseName::Kind::Normal:
writer.write<uint8_t>(static_cast<uint8_t>(DeclNameKind::Normal));
writer.OS << key.getIdentifier().str();
break;
case DeclBaseName::Kind::Subscript:
writer.write<uint8_t>(static_cast<uint8_t>(DeclNameKind::Subscript));
break;
case DeclBaseName::Kind::Destructor:
writer.write<uint8_t>(static_cast<uint8_t>(DeclNameKind::Destructor));
break;
}
}
void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data,
unsigned len) {
static_assert(bitOffsetFitsIn32Bits(), "BitOffset too large");
endian::Writer<little> writer(out);
writer.write<data_type>(static_cast<uint32_t>(data));
}
};
class DeclMembersTableInfo {
public:
using key_type = DeclID;
using key_type_ref = const key_type &;
using data_type = Serializer::DeclMembersData; // Vector of DeclIDs
using data_type_ref = const data_type &;
using hash_value_type = uint32_t;
using offset_type = unsigned;
hash_value_type ComputeHash(key_type_ref key) {
return llvm::hash_value(static_cast<uint32_t>(key));
}
std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out,
key_type_ref key,
data_type_ref data) {
// This will trap if a single ValueDecl has more than 16383 members
// with the same DeclBaseName. Seems highly unlikely.
assert((data.size() < (1 << 14)) && "Too many members");
uint32_t keyLength = sizeof(uint32_t); // key DeclID
uint32_t dataLength = sizeof(uint32_t) * data.size(); // value DeclIDs
endian::Writer<little> writer(out);
writer.write<uint16_t>(keyLength);
writer.write<uint16_t>(dataLength);
return { keyLength, dataLength };
}
void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
static_assert(declIDFitsIn32Bits(), "DeclID too large");
assert(len == sizeof(uint32_t));
endian::Writer<little> writer(out);
writer.write<uint32_t>(key);
}
void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data,
unsigned len) {
static_assert(declIDFitsIn32Bits(), "DeclID too large");
endian::Writer<little> writer(out);
for (auto entry : data) {
writer.write<uint32_t>(entry);
}
}
};
} // end anonymous namespace } // end anonymous namespace
namespace llvm { namespace llvm {
@@ -1613,7 +1729,8 @@ static bool shouldSerializeMember(Decl *D) {
llvm_unreachable("Unhandled DeclKind in switch."); llvm_unreachable("Unhandled DeclKind in switch.");
} }
void Serializer::writeMembers(DeclRange members, bool isClass) { void Serializer::writeMembers(DeclID parentID,
DeclRange members, bool isClass) {
using namespace decls_block; using namespace decls_block;
unsigned abbrCode = DeclTypeAbbrCodes[MembersLayout::Code]; unsigned abbrCode = DeclTypeAbbrCodes[MembersLayout::Code];
@@ -1625,8 +1742,18 @@ void Serializer::writeMembers(DeclRange members, bool isClass) {
DeclID memberID = addDeclRef(member); DeclID memberID = addDeclRef(member);
memberIDs.push_back(memberID); memberIDs.push_back(memberID);
if (isClass) { if (auto VD = dyn_cast<ValueDecl>(member)) {
if (auto VD = dyn_cast<ValueDecl>(member)) {
// Record parent->members in subtable of DeclMemberNames.
std::unique_ptr<DeclMembersTable> &memberTable =
DeclMemberNames[VD->getBaseName()].second;
if (!memberTable) {
memberTable = llvm::make_unique<DeclMembersTable>();
}
(*memberTable)[parentID].push_back(memberID);
// Possibly add a record to ClassMembersForDynamicLookup too.
if (isClass) {
if (VD->canBeAccessedByDynamicLookup()) { if (VD->canBeAccessedByDynamicLookup()) {
auto &list = ClassMembersForDynamicLookup[VD->getBaseName()]; auto &list = ClassMembersForDynamicLookup[VD->getBaseName()];
list.push_back({getKindForTable(VD), memberID}); list.push_back({getKindForTable(VD), memberID});
@@ -2562,7 +2689,7 @@ void Serializer::writeDecl(const Decl *D) {
for (auto *genericParams : allGenericParams) for (auto *genericParams : allGenericParams)
writeGenericParams(genericParams); writeGenericParams(genericParams);
writeMembers(extension->getMembers(), isClassExtension); writeMembers(id, extension->getMembers(), isClassExtension);
writeConformances(conformances, DeclTypeAbbrCodes); writeConformances(conformances, DeclTypeAbbrCodes);
break; break;
} }
@@ -2770,7 +2897,7 @@ void Serializer::writeDecl(const Decl *D) {
writeGenericParams(theStruct->getGenericParams()); writeGenericParams(theStruct->getGenericParams());
writeMembers(theStruct->getMembers(), false); writeMembers(id, theStruct->getMembers(), false);
writeConformances(conformances, DeclTypeAbbrCodes); writeConformances(conformances, DeclTypeAbbrCodes);
break; break;
} }
@@ -2817,7 +2944,7 @@ void Serializer::writeDecl(const Decl *D) {
inheritedAndDependencyTypes); inheritedAndDependencyTypes);
writeGenericParams(theEnum->getGenericParams()); writeGenericParams(theEnum->getGenericParams());
writeMembers(theEnum->getMembers(), false); writeMembers(id, theEnum->getMembers(), false);
writeConformances(conformances, DeclTypeAbbrCodes); writeConformances(conformances, DeclTypeAbbrCodes);
break; break;
} }
@@ -2855,7 +2982,7 @@ void Serializer::writeDecl(const Decl *D) {
inheritedTypes); inheritedTypes);
writeGenericParams(theClass->getGenericParams()); writeGenericParams(theClass->getGenericParams());
writeMembers(theClass->getMembers(), true); writeMembers(id, theClass->getMembers(), true);
writeConformances(conformances, DeclTypeAbbrCodes); writeConformances(conformances, DeclTypeAbbrCodes);
break; break;
} }
@@ -2889,7 +3016,7 @@ void Serializer::writeDecl(const Decl *D) {
writeGenericParams(proto->getGenericParams()); writeGenericParams(proto->getGenericParams());
writeGenericRequirements( writeGenericRequirements(
proto->getRequirementSignature(), DeclTypeAbbrCodes); proto->getRequirementSignature(), DeclTypeAbbrCodes);
writeMembers(proto->getMembers(), true); writeMembers(id, proto->getMembers(), true);
writeDefaultWitnessTable(proto, DeclTypeAbbrCodes); writeDefaultWitnessTable(proto, DeclTypeAbbrCodes);
break; break;
} }
@@ -3954,6 +4081,51 @@ writeNestedTypeDeclsTable(const index_block::NestedTypeDeclsLayout &declList,
declList.emit(scratch, tableOffset, hashTableBlob); declList.emit(scratch, tableOffset, hashTableBlob);
} }
static void
writeDeclMemberNamesTable(const index_block::DeclMemberNamesLayout &declNames,
const Serializer::DeclMemberNamesTable &table) {
SmallVector<uint64_t, 8> scratch;
llvm::SmallString<4096> hashTableBlob;
uint32_t tableOffset;
{
llvm::OnDiskChainedHashTableGenerator<DeclMemberNamesTableInfo> generator;
// Emit the offsets of the sub-tables; the tables themselves have been
// separately emitted into DECL_MEMBER_TABLES_BLOCK by now.
for (auto &entry : table) {
// Or they _should_ have been; check for nonzero offsets.
assert(static_cast<unsigned>(entry.second.first) != 0);
generator.insert(entry.first, entry.second.first);
}
llvm::raw_svector_ostream blobStream(hashTableBlob);
// Make sure that no bucket is at offset 0
endian::Writer<little>(blobStream).write<uint32_t>(0);
tableOffset = generator.Emit(blobStream);
}
declNames.emit(scratch, tableOffset, hashTableBlob);
}
static void
writeDeclMembersTable(const decl_member_tables_block::DeclMembersLayout &mems,
const Serializer::DeclMembersTable &table) {
SmallVector<uint64_t, 8> scratch;
llvm::SmallString<4096> hashTableBlob;
uint32_t tableOffset;
{
llvm::OnDiskChainedHashTableGenerator<DeclMembersTableInfo> generator;
for (auto &entry : table)
generator.insert(entry.first, entry.second);
llvm::raw_svector_ostream blobStream(hashTableBlob);
// Make sure that no bucket is at offset 0
endian::Writer<little>(blobStream).write<uint32_t>(0);
tableOffset = generator.Emit(blobStream);
}
mems.emit(scratch, tableOffset, hashTableBlob);
}
namespace { namespace {
struct DeclCommentTableData { struct DeclCommentTableData {
@@ -4491,7 +4663,8 @@ static void collectInterestingNestedDeclarations(
} }
void Serializer::writeAST(ModuleOrSourceFile DC, void Serializer::writeAST(ModuleOrSourceFile DC,
bool enableNestedTypeLookupTable) { bool enableNestedTypeLookupTable,
bool enableDeclMemberNamesTable) {
DeclTable topLevelDecls, operatorDecls, operatorMethodDecls; DeclTable topLevelDecls, operatorDecls, operatorMethodDecls;
DeclTable precedenceGroupDecls; DeclTable precedenceGroupDecls;
ObjCMethodTable objcMethods; ObjCMethodTable objcMethods;
@@ -4625,6 +4798,25 @@ void Serializer::writeAST(ModuleOrSourceFile DC,
index_block::EntryPointLayout EntryPoint(Out); index_block::EntryPointLayout EntryPoint(Out);
EntryPoint.emit(ScratchRecord, entryPointClassID.getValue()); EntryPoint.emit(ScratchRecord, entryPointClassID.getValue());
} }
if (enableDeclMemberNamesTable) {
{
// Write sub-tables to a skippable sub-block.
BCBlockRAII restoreBlock(Out, DECL_MEMBER_TABLES_BLOCK_ID, 4);
decl_member_tables_block::DeclMembersLayout DeclMembersTable(Out);
for (auto &entry : DeclMemberNames) {
// Save BitOffset we're writing sub-table to.
static_assert(bitOffsetFitsIn32Bits(), "BitOffset too large");
assert(Out.GetCurrentBitNo() < (1ull << 32));
entry.second.first = Out.GetCurrentBitNo();
// Write sub-table.
writeDeclMembersTable(DeclMembersTable, *entry.second.second);
}
}
// Write top-level table mapping names to sub-tables.
index_block::DeclMemberNamesLayout DeclMemberNamesTable(Out);
writeDeclMemberNamesTable(DeclMemberNamesTable, DeclMemberNames);
}
} }
} }
@@ -4657,7 +4849,8 @@ void Serializer::writeToStream(raw_ostream &os, ModuleOrSourceFile DC,
S.writeHeader(options); S.writeHeader(options);
S.writeInputBlock(options); S.writeInputBlock(options);
S.writeSIL(SILMod, options.SerializeAllSIL); S.writeSIL(SILMod, options.SerializeAllSIL);
S.writeAST(DC, options.EnableNestedTypeLookupTable); S.writeAST(DC, options.EnableNestedTypeLookupTable,
options.EnableDeclMemberNamesTable);
} }
S.writeToStream(os); S.writeToStream(os);

View File

@@ -129,6 +129,17 @@ public:
// hash table of all defined Objective-C methods. // hash table of all defined Objective-C methods.
using NestedTypeDeclsTable = llvm::MapVector<Identifier, NestedTypeDeclsData>; using NestedTypeDeclsTable = llvm::MapVector<Identifier, NestedTypeDeclsData>;
using DeclMembersData = SmallVector<DeclID, 2>;
// In-memory representation of what will eventually be an on-disk
// hash table of all ValueDecl-members of a paticular DeclBaseName.
using DeclMembersTable = llvm::MapVector<DeclID, DeclMembersData>;
using DeclMemberNamesData = std::pair<serialization::BitOffset,
std::unique_ptr<DeclMembersTable>>;
// In-memory representation of what will eventually be an on-disk
// hash table mapping DeclBaseNames to DeclMembersData tables.
using DeclMemberNamesTable = llvm::MapVector<DeclBaseName, DeclMemberNamesData>;
using ExtensionTableData = using ExtensionTableData =
SmallVector<std::pair<const NominalTypeDecl *, DeclID>, 4>; SmallVector<std::pair<const NominalTypeDecl *, DeclID>, 4>;
using ExtensionTable = llvm::MapVector<Identifier, ExtensionTableData>; using ExtensionTable = llvm::MapVector<Identifier, ExtensionTableData>;
@@ -139,6 +150,11 @@ private:
/// This is used for id-style lookup. /// This is used for id-style lookup.
DeclTable ClassMembersForDynamicLookup; DeclTable ClassMembersForDynamicLookup;
/// A map from DeclBaseNames of members to Decl->members sub-tables.
///
/// This is for Named Lazy Member Loading.
DeclMemberNamesTable DeclMemberNames;
/// The queue of types and decls that need to be serialized. /// The queue of types and decls that need to be serialized.
/// ///
/// This is a queue and not simply a vector because serializing one /// This is a queue and not simply a vector because serializing one
@@ -289,10 +305,11 @@ private:
/// Writes an array of members for a decl context. /// Writes an array of members for a decl context.
/// ///
/// \param members The decls within the context /// \param parentID The DeclID of the context.
/// \param members The decls within the context.
/// \param isClass True if the context could be a class context (class, /// \param isClass True if the context could be a class context (class,
/// class extension, or protocol). /// class extension, or protocol).
void writeMembers(DeclRange members, bool isClass); void writeMembers(DeclID parentID, DeclRange members, bool isClass);
/// Write a default witness table for a protocol. /// Write a default witness table for a protocol.
/// ///
@@ -369,7 +386,9 @@ private:
void writeSIL(const SILModule *M, bool serializeAllSIL); void writeSIL(const SILModule *M, bool serializeAllSIL);
/// Top-level entry point for serializing a module. /// Top-level entry point for serializing a module.
void writeAST(ModuleOrSourceFile DC, bool enableNestedTypeLookupTable); void writeAST(ModuleOrSourceFile DC,
bool enableNestedTypeLookupTable,
bool enableDeclMemberNamesTable);
void writeToStream(raw_ostream &os); void writeToStream(raw_ostream &os);