[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 EnableNestedTypeLookupTable = false;
bool EnableDeclMemberNamesTable = false;
bool SerializeAllSIL = false;
bool SerializeOptionsForDebugging = false;
bool IsSIB = false;

View File

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

View File

@@ -540,7 +540,7 @@ public:
static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&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) {
@@ -562,8 +562,8 @@ public:
static data_type ReadData(internal_key_type key, const uint8_t *data,
unsigned length) {
assert(length == sizeof(serialization::BitOffset));
return endian::readNext<serialization::BitOffset, little, unaligned>(data);
assert(length == sizeof(uint32_t));
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;
}
/// 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 {
/// Used to serialize the on-disk decl hash table.
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
namespace llvm {
@@ -1613,7 +1729,8 @@ static bool shouldSerializeMember(Decl *D) {
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;
unsigned abbrCode = DeclTypeAbbrCodes[MembersLayout::Code];
@@ -1625,8 +1742,18 @@ void Serializer::writeMembers(DeclRange members, bool isClass) {
DeclID memberID = addDeclRef(member);
memberIDs.push_back(memberID);
if (isClass) {
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()) {
auto &list = ClassMembersForDynamicLookup[VD->getBaseName()];
list.push_back({getKindForTable(VD), memberID});
@@ -2562,7 +2689,7 @@ void Serializer::writeDecl(const Decl *D) {
for (auto *genericParams : allGenericParams)
writeGenericParams(genericParams);
writeMembers(extension->getMembers(), isClassExtension);
writeMembers(id, extension->getMembers(), isClassExtension);
writeConformances(conformances, DeclTypeAbbrCodes);
break;
}
@@ -2770,7 +2897,7 @@ void Serializer::writeDecl(const Decl *D) {
writeGenericParams(theStruct->getGenericParams());
writeMembers(theStruct->getMembers(), false);
writeMembers(id, theStruct->getMembers(), false);
writeConformances(conformances, DeclTypeAbbrCodes);
break;
}
@@ -2817,7 +2944,7 @@ void Serializer::writeDecl(const Decl *D) {
inheritedAndDependencyTypes);
writeGenericParams(theEnum->getGenericParams());
writeMembers(theEnum->getMembers(), false);
writeMembers(id, theEnum->getMembers(), false);
writeConformances(conformances, DeclTypeAbbrCodes);
break;
}
@@ -2855,7 +2982,7 @@ void Serializer::writeDecl(const Decl *D) {
inheritedTypes);
writeGenericParams(theClass->getGenericParams());
writeMembers(theClass->getMembers(), true);
writeMembers(id, theClass->getMembers(), true);
writeConformances(conformances, DeclTypeAbbrCodes);
break;
}
@@ -2889,7 +3016,7 @@ void Serializer::writeDecl(const Decl *D) {
writeGenericParams(proto->getGenericParams());
writeGenericRequirements(
proto->getRequirementSignature(), DeclTypeAbbrCodes);
writeMembers(proto->getMembers(), true);
writeMembers(id, proto->getMembers(), true);
writeDefaultWitnessTable(proto, DeclTypeAbbrCodes);
break;
}
@@ -3954,6 +4081,51 @@ writeNestedTypeDeclsTable(const index_block::NestedTypeDeclsLayout &declList,
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 {
struct DeclCommentTableData {
@@ -4491,7 +4663,8 @@ static void collectInterestingNestedDeclarations(
}
void Serializer::writeAST(ModuleOrSourceFile DC,
bool enableNestedTypeLookupTable) {
bool enableNestedTypeLookupTable,
bool enableDeclMemberNamesTable) {
DeclTable topLevelDecls, operatorDecls, operatorMethodDecls;
DeclTable precedenceGroupDecls;
ObjCMethodTable objcMethods;
@@ -4625,6 +4798,25 @@ void Serializer::writeAST(ModuleOrSourceFile DC,
index_block::EntryPointLayout EntryPoint(Out);
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.writeInputBlock(options);
S.writeSIL(SILMod, options.SerializeAllSIL);
S.writeAST(DC, options.EnableNestedTypeLookupTable);
S.writeAST(DC, options.EnableNestedTypeLookupTable,
options.EnableDeclMemberNamesTable);
}
S.writeToStream(os);

View File

@@ -129,6 +129,17 @@ public:
// hash table of all defined Objective-C methods.
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 =
SmallVector<std::pair<const NominalTypeDecl *, DeclID>, 4>;
using ExtensionTable = llvm::MapVector<Identifier, ExtensionTableData>;
@@ -139,6 +150,11 @@ private:
/// This is used for id-style lookup.
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.
///
/// 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.
///
/// \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,
/// 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.
///
@@ -369,7 +386,9 @@ private:
void writeSIL(const SILModule *M, bool serializeAllSIL);
/// 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);