Skip to content

Commit ab56fa3

Browse files
committed
[Serialization] Support (de-)serialization of SubstitutionMaps.
Allow substitution maps to be serialized directly (via an ID), writing out the replacement types and conformances as appropriate. This is a more efficient form of serialization than the current SubstitutionList approach, because it maintains uniqueness of substitution maps within a module file, and is a step toward eliminating SubstitutionList entirely.
1 parent 547abf1 commit ab56fa3

File tree

8 files changed

+253
-17
lines changed

8 files changed

+253
-17
lines changed

include/swift/AST/SubstitutionMap.h

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/SubstitutionList.h"
2323
#include "swift/AST/Type.h"
2424
#include "llvm/ADT/ArrayRef.h"
25+
#include "llvm/ADT/DenseMapInfo.h"
2526
#include "llvm/ADT/FoldingSet.h"
2627
#include "llvm/ADT/Optional.h"
2728
#include "llvm/Support/TrailingObjects.h"
@@ -154,25 +155,10 @@ class SubstitutionMap {
154155
/// signature nor any replacement types/conformances.
155156
Storage *storage = nullptr;
156157

157-
/// Retrieve the array of replacement types, which line up with the
158-
/// generic parameters.
159-
///
160-
/// Note that the types may be null, for cases where the generic parameter
161-
/// is concrete but hasn't been queried yet.
162-
ArrayRef<Type> getReplacementTypes() const {
163-
return storage ? storage->getReplacementTypes() : ArrayRef<Type>();
164-
}
165-
166158
MutableArrayRef<Type> getReplacementTypes() {
167159
return storage ? storage->getReplacementTypes() : MutableArrayRef<Type>();
168160
}
169161

170-
/// Retrieve the array of protocol conformances, which line up with the
171-
/// requirements of the generic signature.
172-
ArrayRef<ProtocolConformanceRef> getConformances() const {
173-
return storage ? storage->getConformances()
174-
: ArrayRef<ProtocolConformanceRef>();
175-
}
176162
MutableArrayRef<ProtocolConformanceRef> getConformances() {
177163
return storage ? storage->getConformances()
178164
: MutableArrayRef<ProtocolConformanceRef>();
@@ -185,10 +171,21 @@ class SubstitutionMap {
185171
ArrayRef<ProtocolConformanceRef> conformances)
186172
: storage(Storage::get(genericSig, replacementTypes, conformances)) { }
187173

174+
explicit SubstitutionMap(Storage *storage) : storage(storage) { }
175+
188176
public:
189177
/// Build an empty substitution map.
190178
SubstitutionMap() { }
191179

180+
/// Build an interface type substitution map for the given generic
181+
/// signature and a vector of Substitutions that correspond to the
182+
/// requirements of this generic signature.
183+
static SubstitutionMap get(GenericSignature *genericSig,
184+
ArrayRef<Type> replacementTypes,
185+
ArrayRef<ProtocolConformanceRef> conformances) {
186+
return SubstitutionMap(genericSig, replacementTypes, conformances);
187+
}
188+
192189
/// Build an interface type substitution map for the given generic
193190
/// signature and a vector of Substitutions that correspond to the
194191
/// requirements of this generic signature.
@@ -207,13 +204,32 @@ class SubstitutionMap {
207204
return storage ? storage->getGenericSignature() : nullptr;
208205
}
209206

207+
/// Retrieve the array of protocol conformances, which line up with the
208+
/// requirements of the generic signature.
209+
ArrayRef<ProtocolConformanceRef> getConformances() const {
210+
return storage ? storage->getConformances()
211+
: ArrayRef<ProtocolConformanceRef>();
212+
}
213+
210214
/// Look up a conformance for the given type to the given protocol.
211215
Optional<ProtocolConformanceRef>
212216
lookupConformance(CanType type, ProtocolDecl *proto) const;
213217

214218
/// Whether the substitution map is empty.
215219
bool empty() const { return getGenericSignature() == nullptr; }
216220

221+
/// Whether the substitution map is non-empty.
222+
explicit operator bool() const { return !empty(); }
223+
224+
/// Retrieve the array of replacement types, which line up with the
225+
/// generic parameters.
226+
///
227+
/// Note that the types may be null, for cases where the generic parameter
228+
/// is concrete but hasn't been queried yet.
229+
ArrayRef<Type> getReplacementTypes() const {
230+
return storage ? storage->getReplacementTypes() : ArrayRef<Type>();
231+
}
232+
217233
/// Query whether any replacement types in the map contain archetypes.
218234
bool hasArchetypes() const;
219235

@@ -292,6 +308,22 @@ class SubstitutionMap {
292308
/// Profile the substitution map, for use with LLVM's FoldingSet.
293309
void profile(llvm::FoldingSetNodeID &id) const;
294310

311+
const void *getOpaqueValue() const { return storage; }
312+
313+
static SubstitutionMap getFromOpaqueValue(const void *ptr) {
314+
return SubstitutionMap(const_cast<Storage *>((const Storage *)ptr));
315+
}
316+
317+
static SubstitutionMap getEmptyKey() {
318+
return SubstitutionMap(
319+
(Storage *)llvm::DenseMapInfo<void*>::getEmptyKey());
320+
}
321+
322+
static SubstitutionMap getTombstoneKey() {
323+
return SubstitutionMap(
324+
(Storage *)llvm::DenseMapInfo<void*>::getTombstoneKey());
325+
}
326+
295327
private:
296328
friend class GenericSignature;
297329
friend class GenericEnvironment;
@@ -306,4 +338,37 @@ class SubstitutionMap {
306338

307339
} // end namespace swift
308340

341+
namespace llvm {
342+
template <>
343+
struct PointerLikeTypeTraits<swift::SubstitutionMap> {
344+
static void *getAsVoidPointer(swift::SubstitutionMap map) {
345+
return const_cast<void *>(map.getOpaqueValue());
346+
}
347+
static swift::SubstitutionMap getFromVoidPointer(const void *ptr) {
348+
return swift::SubstitutionMap::getFromOpaqueValue(ptr);
349+
}
350+
351+
/// Note: Assuming storage is at leaste 4-byte aligned.
352+
enum { NumLowBitsAvailable = 2 };
353+
};
354+
355+
// Substitution maps hash just like pointers.
356+
template<> struct DenseMapInfo<swift::SubstitutionMap> {
357+
static swift::SubstitutionMap getEmptyKey() {
358+
return swift::SubstitutionMap::getEmptyKey();
359+
}
360+
static swift::SubstitutionMap getTombstoneKey() {
361+
return swift::SubstitutionMap::getTombstoneKey();
362+
}
363+
static unsigned getHashValue(swift::SubstitutionMap map) {
364+
return DenseMapInfo<void*>::getHashValue(map.getOpaqueValue());
365+
}
366+
static bool isEqual(swift::SubstitutionMap lhs,
367+
swift::SubstitutionMap rhs) {
368+
return lhs.getOpaqueValue() == rhs.getOpaqueValue();
369+
}
370+
};
371+
372+
}
373+
309374
#endif

include/swift/Serialization/DeclTypeRecordNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ TRAILING_INFO(GENERIC_REQUIREMENT)
166166
TRAILING_INFO(LAYOUT_REQUIREMENT)
167167
OTHER(GENERIC_SIGNATURE, 244)
168168
OTHER(SIL_GENERIC_ENVIRONMENT, 245)
169+
OTHER(SUBSTITUTION_MAP, 246)
169170

170171
OTHER(LOCAL_DISCRIMINATOR, 248)
171172
OTHER(PRIVATE_DISCRIMINATOR, 249)

include/swift/Serialization/ModuleFile.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@ class ModuleFile
310310
/// Generic environments referenced by this module.
311311
std::vector<Serialized<GenericEnvironment *>> GenericEnvironments;
312312

313+
/// Substitution maps referenced by this module.
314+
std::vector<Serialized<SubstitutionMap>> SubstitutionMaps;
315+
313316
/// Represents an identifier that may or may not have been deserialized yet.
314317
///
315318
/// If \c Offset is non-zero, the identifier has not been loaded yet.
@@ -844,6 +847,10 @@ class ModuleFile
844847
GenericEnvironment *getGenericEnvironment(
845848
serialization::GenericEnvironmentID ID);
846849

850+
/// Returns the substitution map for the given ID, deserializing it if
851+
/// needed.
852+
SubstitutionMap getSubstitutionMap(serialization::SubstitutionMapID id);
853+
847854
/// Reads a substitution record from \c DeclTypeCursor.
848855
///
849856
/// If the record at the cursor is not a substitution, returns None.

include/swift/Serialization/ModuleFormat.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ using GenericSignatureIDField = DeclIDField;
8888
using GenericEnvironmentID = DeclID;
8989
using GenericEnvironmentIDField = DeclIDField;
9090

91+
// SubstitutionMapID must be the same as DeclID because it is stored in the
92+
// same way.
93+
using SubstitutionMapID = DeclID;
94+
using SubstitutionMapIDField = DeclIDField;
95+
9196
// ModuleID must be the same as IdentifierID because it is stored the same way.
9297
using ModuleID = IdentifierID;
9398
using ModuleIDField = IdentifierIDField;
@@ -1224,6 +1229,14 @@ namespace decls_block {
12241229
BCArray<TypeIDField> // generic parameter types
12251230
>;
12261231

1232+
using SubstitutionMapLayout = BCRecordLayout<
1233+
SUBSTITUTION_MAP,
1234+
GenericSignatureIDField, // generic signature
1235+
BCVBR<5>, // # of conformances
1236+
BCArray<TypeIDField> // replacement types
1237+
// Conformances trail the record.
1238+
>;
1239+
12271240
using SILGenericEnvironmentLayout = BCRecordLayout<
12281241
SIL_GENERIC_ENVIRONMENT,
12291242
BCArray<TypeIDField> // (generic parameter name, sugared interface
@@ -1599,7 +1612,8 @@ namespace index_block {
15991612
DECL_MEMBER_NAMES,
16001613

16011614
GENERIC_SIGNATURE_OFFSETS,
1602-
LastRecordKind = GENERIC_SIGNATURE_OFFSETS,
1615+
SUBSTITUTION_MAP_OFFSETS,
1616+
LastRecordKind = SUBSTITUTION_MAP_OFFSETS,
16031617
};
16041618

16051619
constexpr const unsigned RecordIDFieldWidth = 5;

lib/Serialization/Deserialization.cpp

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1097,7 +1097,77 @@ GenericEnvironment *ModuleFile::getGenericEnvironment(
10971097
serialization::GenericEnvironmentID ID) {
10981098
return getGenericSignatureOrEnvironment(ID, /*wantEnvironment=*/true)
10991099
.get<GenericEnvironment *>();
1100-
;
1100+
}
1101+
1102+
SubstitutionMap ModuleFile::getSubstitutionMap(
1103+
serialization::SubstitutionMapID id) {
1104+
using namespace decls_block;
1105+
1106+
// Zero is a sentinel for having an empty substitution map.
1107+
if (id == 0) return SubstitutionMap();
1108+
1109+
assert(id <= SubstitutionMaps.size() && "invalid SubstitutionMap ID");
1110+
auto &substitutionsOrOffset = SubstitutionMaps[id-1];
1111+
1112+
// If we've already deserialized this substitution map, return it.
1113+
if (substitutionsOrOffset.isComplete()) {
1114+
return substitutionsOrOffset.get();
1115+
}
1116+
1117+
// Read the substitution map.
1118+
BCOffsetRAII restoreOffset(DeclTypeCursor);
1119+
DeclTypeCursor.JumpToBit(substitutionsOrOffset);
1120+
DeserializingEntityRAII deserializingEntity(*this);
1121+
1122+
// Read the substitution map.
1123+
auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd);
1124+
if (entry.Kind != llvm::BitstreamEntry::Record) {
1125+
error();
1126+
return SubstitutionMap();
1127+
}
1128+
1129+
StringRef blobData;
1130+
SmallVector<uint64_t, 8> scratch;
1131+
unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, &blobData);
1132+
if (recordID != SUBSTITUTION_MAP) {
1133+
error();
1134+
return SubstitutionMap();
1135+
}
1136+
1137+
GenericSignatureID genericSigID;
1138+
uint64_t numConformances;
1139+
ArrayRef<uint64_t> replacementTypeIDs;
1140+
SubstitutionMapLayout::readRecord(scratch, genericSigID, numConformances,
1141+
replacementTypeIDs);
1142+
1143+
// Generic signature.
1144+
auto genericSig = getGenericSignature(genericSigID);
1145+
if (!genericSig) {
1146+
error();
1147+
return SubstitutionMap();
1148+
}
1149+
1150+
// Load the replacement types.
1151+
SmallVector<Type, 4> replacementTypes;
1152+
replacementTypes.reserve(replacementTypeIDs.size());
1153+
for (auto typeID : replacementTypeIDs) {
1154+
replacementTypes.push_back(getType(typeID));
1155+
}
1156+
1157+
// Read the conformances.
1158+
SmallVector<ProtocolConformanceRef, 4> conformances;
1159+
conformances.reserve(numConformances);
1160+
for (unsigned i : range(numConformances)) {
1161+
(void)i;
1162+
conformances.push_back(readConformance(DeclTypeCursor));
1163+
}
1164+
1165+
// Form the substitution map and record it.
1166+
auto substitutions =
1167+
SubstitutionMap::get(genericSig, ArrayRef<Type>(replacementTypes),
1168+
ArrayRef<ProtocolConformanceRef>(conformances));
1169+
substitutionsOrOffset = substitutions;
1170+
return substitutions;
11011171
}
11021172

11031173
bool ModuleFile::readDefaultWitnessTable(ProtocolDecl *proto) {

lib/Serialization/ModuleFile.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,10 @@ bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) {
852852
assert(blobData.empty());
853853
GenericEnvironments.assign(scratch.begin(), scratch.end());
854854
break;
855+
case index_block::SUBSTITUTION_MAP_OFFSETS:
856+
assert(blobData.empty());
857+
SubstitutionMaps.assign(scratch.begin(), scratch.end());
858+
break;
855859
case index_block::NORMAL_CONFORMANCE_OFFSETS:
856860
assert(blobData.empty());
857861
NormalConformances.assign(scratch.begin(), scratch.end());

0 commit comments

Comments
 (0)