Skip to content

Commit 5eddbc7

Browse files
committed
[AST] Tail-allocate GenericEnvironment's archetypes-to-interface types mapping
Store the archetype-to-interface-type mapping (which is used to map *out* of a generic environment) is a tail-allocated array of (archetype, generic type parameter) pairs. This array is built up lazily, as we compute the context types for generic parameters. Searching in this array is linear while it is being constructed. Once it is complete, it is sorted so that future searches are logarithmic. Aside from the space savings of not having a DenseMap lying around, this means we no longer need to register a destructor of a GenericEnvironment with the ASTContext, which saves us tear-down time.
1 parent c09f092 commit 5eddbc7

File tree

3 files changed

+185
-27
lines changed

3 files changed

+185
-27
lines changed

include/swift/AST/GenericEnvironment.h

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/GenericSignature.h"
2323
#include "llvm/ADT/ArrayRef.h"
2424
#include "llvm/Support/TrailingObjects.h"
25+
#include <utility>
2526

2627
namespace swift {
2728

@@ -34,17 +35,36 @@ class SILType;
3435
/// Describes the mapping between archetypes and interface types for the
3536
/// generic parameters of a DeclContext.
3637
class alignas(1 << DeclAlignInBits) GenericEnvironment final
37-
: private llvm::TrailingObjects<GenericEnvironment, Type> {
38+
: private llvm::TrailingObjects<GenericEnvironment, Type,
39+
std::pair<ArchetypeType *,
40+
GenericTypeParamType *>> {
3841
GenericSignature *Signature;
3942
ArchetypeBuilder *Builder;
40-
TypeSubstitutionMap ArchetypeToInterfaceMap;
43+
44+
// The number of generic type parameter -> context type mappings we have
45+
// recorded so far. This saturates at the number of generic type parameters,
46+
// at which point the archetype-to-interface trailing array is sorted.
47+
unsigned NumMappingsRecorded : 16;
48+
49+
// The number of archetype-to-interface type mappings. This is always <=
50+
// \c NumMappingsRecorded.
51+
unsigned NumArchetypeToInterfaceMappings : 16;
4152

4253
friend TrailingObjects;
4354

55+
/// An entry in the array mapping from archetypes to their corresponding
56+
/// generic type parameters.
57+
typedef std::pair<ArchetypeType *, GenericTypeParamType *>
58+
ArchetypeToInterfaceMapping;
59+
4460
size_t numTrailingObjects(OverloadToken<Type>) const {
4561
return Signature->getGenericParams().size();
4662
}
4763

64+
size_t numTrailingObjects(OverloadToken<ArchetypeToInterfaceMapping>) const {
65+
return Signature->getGenericParams().size();
66+
}
67+
4868
/// Retrieve the array containing the context types associated with the
4969
/// generic parameters, stored in parallel with the generic parameters of the
5070
/// generic signature.
@@ -61,6 +81,30 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final
6181
Signature->getGenericParams().size());
6282
}
6383

84+
/// Retrieve the active set of archetype-to-interface mappings.
85+
ArrayRef<ArchetypeToInterfaceMapping>
86+
getActiveArchetypeToInterfaceMappings() const {
87+
return { getTrailingObjects<ArchetypeToInterfaceMapping>(),
88+
NumArchetypeToInterfaceMappings };
89+
}
90+
91+
/// Retrieve the active set of archetype-to-interface mappings.
92+
MutableArrayRef<ArchetypeToInterfaceMapping>
93+
getActiveArchetypeToInterfaceMappings() {
94+
return { getTrailingObjects<ArchetypeToInterfaceMapping>(),
95+
NumArchetypeToInterfaceMappings };
96+
}
97+
98+
/// Retrieve the buffer for the archetype-to-interface mappings.
99+
///
100+
/// Only the first \c NumArchetypeToInterfaceMappings elements in the buffer
101+
/// are valid.
102+
MutableArrayRef<ArchetypeToInterfaceMapping>
103+
getArchetypeToInterfaceMappingsBuffer() {
104+
return { getTrailingObjects<ArchetypeToInterfaceMapping>(),
105+
Signature->getGenericParams().size() };
106+
}
107+
64108
GenericEnvironment(GenericSignature *signature,
65109
ArchetypeBuilder *builder,
66110
TypeSubstitutionMap interfaceToArchetypeMap);
@@ -83,7 +127,20 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final
83127
Type operator()(SubstitutableType *type) const;
84128
};
85129
friend class QueryInterfaceTypeSubstitutions;
86-
130+
131+
/// Query function suitable for use as a \c TypeSubstitutionFn that queries
132+
/// the mapping of archetypes back to interface types.
133+
class QueryArchetypeToInterfaceSubstitutions {
134+
const GenericEnvironment *self;
135+
136+
public:
137+
QueryArchetypeToInterfaceSubstitutions(const GenericEnvironment *self)
138+
: self(self) { }
139+
140+
Type operator()(SubstitutableType *type) const;
141+
};
142+
friend class QueryArchetypeToInterfaceSubstitutions;
143+
87144
public:
88145
GenericSignature *getGenericSignature() const {
89146
return Signature;

lib/AST/ASTContext.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3561,14 +3561,16 @@ GenericSignature *GenericSignature::get(ArrayRef<GenericTypeParamType *> params,
35613561
GenericEnvironment *
35623562
GenericEnvironment::get(GenericSignature *signature,
35633563
TypeSubstitutionMap interfaceToArchetypeMap) {
3564+
unsigned numGenericParams = signature->getGenericParams().size();
35643565
assert(!interfaceToArchetypeMap.empty());
3565-
assert(interfaceToArchetypeMap.size() == signature->getGenericParams().size()
3566+
assert(interfaceToArchetypeMap.size() == numGenericParams
35663567
&& "incorrect number of parameters");
35673568

35683569
ASTContext &ctx = signature->getASTContext();
35693570

35703571
// Allocate and construct the new environment.
3571-
size_t bytes = totalSizeToAlloc<Type>(signature->getGenericParams().size());
3572+
size_t bytes = totalSizeToAlloc<Type, ArchetypeToInterfaceMapping>(
3573+
numGenericParams, numGenericParams);
35723574
void *mem = ctx.Allocate(bytes, alignof(GenericEnvironment));
35733575
return new (mem) GenericEnvironment(signature, nullptr,
35743576
interfaceToArchetypeMap);
@@ -3579,8 +3581,11 @@ GenericEnvironment *GenericEnvironment::getIncomplete(
35793581
ArchetypeBuilder *builder) {
35803582
TypeSubstitutionMap empty;
35813583
auto &ctx = signature->getASTContext();
3584+
35823585
// Allocate and construct the new environment.
3583-
size_t bytes = totalSizeToAlloc<Type>(signature->getGenericParams().size());
3586+
unsigned numGenericParams = signature->getGenericParams().size();
3587+
size_t bytes = totalSizeToAlloc<Type, ArchetypeToInterfaceMapping>(
3588+
numGenericParams, numGenericParams);
35843589
void *mem = ctx.Allocate(bytes, alignof(GenericEnvironment));
35853590
return new (mem) GenericEnvironment(signature, builder, empty);
35863591
}

lib/AST/GenericEnvironment.cpp

Lines changed: 117 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ GenericEnvironment::GenericEnvironment(
2626
TypeSubstitutionMap interfaceToArchetypeMap)
2727
: Signature(signature), Builder(builder)
2828
{
29+
NumMappingsRecorded = 0;
30+
NumArchetypeToInterfaceMappings = 0;
31+
2932
// Clear out the memory that holds the context types.
3033
std::uninitialized_fill(getContextTypes().begin(), getContextTypes().end(), Type());
3134

@@ -35,9 +38,6 @@ GenericEnvironment::GenericEnvironment(
3538
// mapTypeOutOfContext() produces a human-readable type.
3639
for (auto entry : interfaceToArchetypeMap)
3740
addMapping(entry.first->castTo<GenericTypeParamType>(), entry.second);
38-
39-
// Make sure this generic environment gets destroyed.
40-
signature->getASTContext().addDestructorCleanup(*this);
4141
}
4242

4343
void GenericEnvironment::addMapping(GenericParamKey key,
@@ -54,22 +54,52 @@ void GenericEnvironment::addMapping(GenericParamKey key,
5454

5555
// If we mapped the generic parameter to an archetype, add it to the
5656
// reverse mapping.
57-
auto *archetype = contextType->getAs<ArchetypeType>();
58-
if (!archetype) return;
59-
60-
// Check whether we've already recorded an interface type for this archetype.
61-
// If not, record one and we're done.
62-
auto genericParam = genericParams[index];
63-
auto result = ArchetypeToInterfaceMap.insert({archetype, genericParam});
64-
if (result.second) return;
65-
66-
// Multiple generic parameters map to the same archetype. If the
67-
// existing entry comes from a later generic parameter, replace it with
68-
// the earlier generic parameter. This gives us a deterministic reverse
69-
// mapping.
70-
auto otherGP = result.first->second->castTo<GenericTypeParamType>();
71-
if (GenericParamKey(genericParam) < GenericParamKey(otherGP))
72-
result.first->second = genericParam;
57+
if (auto *archetype = contextType->getAs<ArchetypeType>()) {
58+
auto genericParam = genericParams[index];
59+
60+
// Check whether we've already recorded a generic parameter for this
61+
// archetype. Note that we always perform a linear search, because we
62+
// won't have sorted the list yet.
63+
bool found = false;
64+
for (auto &mapping : getActiveArchetypeToInterfaceMappings()) {
65+
if (mapping.first != archetype) continue;
66+
67+
// Multiple generic parameters map to the same archetype. If the
68+
// existing entry comes from a later generic parameter, replace it with
69+
// the earlier generic parameter. This gives us a deterministic reverse
70+
// mapping.
71+
auto otherGP = mapping.second->castTo<GenericTypeParamType>();
72+
if (GenericParamKey(genericParam) < GenericParamKey(otherGP))
73+
mapping.second = genericParam;
74+
found = true;
75+
break;
76+
}
77+
78+
// If we haven't recorded a generic parameter for this archetype, do so now.
79+
if (!found) {
80+
void *ptr = getArchetypeToInterfaceMappingsBuffer().data()
81+
+ NumArchetypeToInterfaceMappings;
82+
new (ptr) ArchetypeToInterfaceMapping(archetype, genericParam);
83+
++NumArchetypeToInterfaceMappings;
84+
}
85+
}
86+
87+
// Note that we've recorded this mapping.
88+
++NumMappingsRecorded;
89+
90+
// If we've recorded all of the mappings, go ahead and sort the array of
91+
// archetype-to-interface-type mappings.
92+
if (NumMappingsRecorded == genericParams.size()) {
93+
llvm::array_pod_sort(getActiveArchetypeToInterfaceMappings().begin(),
94+
getActiveArchetypeToInterfaceMappings().end(),
95+
[](const ArchetypeToInterfaceMapping *lhs,
96+
const ArchetypeToInterfaceMapping *rhs) -> int {
97+
std::less<ArchetypeType *> compare;
98+
if (compare(lhs->first, rhs->first)) return -1;
99+
if (compare(rhs->first, lhs->first)) return 1;
100+
return 0;
101+
});
102+
}
73103
}
74104

75105
Optional<Type> GenericEnvironment::getMappingIfPresent(
@@ -88,11 +118,13 @@ Optional<Type> GenericEnvironment::getMappingIfPresent(
88118

89119
bool GenericEnvironment::containsPrimaryArchetype(
90120
ArchetypeType *archetype) const {
91-
return ArchetypeToInterfaceMap.count(archetype) > 0;
121+
return static_cast<bool>(
122+
QueryArchetypeToInterfaceSubstitutions(this)(archetype));
92123
}
93124

94125
Type GenericEnvironment::mapTypeOutOfContext(ModuleDecl *M, Type type) const {
95-
type = type.subst(M, ArchetypeToInterfaceMap, SubstFlags::AllowLoweredTypes);
126+
type = type.subst(M, QueryArchetypeToInterfaceSubstitutions(this),
127+
SubstFlags::AllowLoweredTypes);
96128
assert(!type->hasArchetype() && "not fully substituted");
97129
return type;
98130
}
@@ -132,6 +164,70 @@ Type GenericEnvironment::QueryInterfaceTypeSubstitutions::operator()(
132164
return Type();
133165
}
134166

167+
Type GenericEnvironment::QueryArchetypeToInterfaceSubstitutions::operator()(
168+
SubstitutableType *type) const {
169+
auto archetype = type->getAs<ArchetypeType>();
170+
if (!archetype) return Type();
171+
172+
// If not all generic parameters have had their context types recorded,
173+
// perform a linear search.
174+
auto genericParams = self->Signature->getGenericParams();
175+
unsigned numGenericParams = genericParams.size();
176+
if (self->NumMappingsRecorded < numGenericParams) {
177+
// Search through all of the active archetype-to-interface mappings.
178+
for (auto &mapping : self->getActiveArchetypeToInterfaceMappings())
179+
if (mapping.first == archetype) return mapping.second;
180+
181+
// We don't know if the archetype is from a different context or if we
182+
// simply haven't recorded it yet. Spin through all of the generic
183+
// parameters looking for one that provides this mapping.
184+
for (auto gp : genericParams) {
185+
// Map the generic parameter into our context. If we get back an
186+
// archetype that matches, we're done.
187+
auto gpArchetype = self->mapTypeIntoContext(gp)->getAs<ArchetypeType>();
188+
if (gpArchetype == archetype) return gp;
189+
}
190+
191+
// We have checked all of the generic parameters and not found anything;
192+
// there is no substitution.
193+
return Type();
194+
}
195+
196+
// All generic parameters have ad their context types recorded, which means
197+
// that the archetypes-to-interface-types array is sorted by address. Use a
198+
// binary search.
199+
struct MappingComparison {
200+
bool operator()(const ArchetypeToInterfaceMapping &lhs,
201+
const ArchetypeType *rhs) const {
202+
std::less<const ArchetypeType *> compare;
203+
204+
return compare(lhs.first, rhs);
205+
}
206+
207+
bool operator()(const ArchetypeType *lhs,
208+
const ArchetypeToInterfaceMapping &rhs) const {
209+
std::less<const ArchetypeType *> compare;
210+
211+
return compare(lhs, rhs.first);
212+
}
213+
214+
bool operator()(const ArchetypeToInterfaceMapping &lhs,
215+
const ArchetypeToInterfaceMapping &rhs) const {
216+
std::less<const ArchetypeType *> compare;
217+
218+
return compare(lhs.first, rhs.first);
219+
}
220+
} mappingComparison;
221+
222+
auto mappings = self->getActiveArchetypeToInterfaceMappings();
223+
auto known = std::lower_bound(mappings.begin(), mappings.end(), archetype,
224+
mappingComparison);
225+
if (known != mappings.end() && known->first == archetype)
226+
return known->second;
227+
228+
return Type();
229+
}
230+
135231
Type GenericEnvironment::mapTypeIntoContext(ModuleDecl *M, Type type) const {
136232
type = type.subst(M, QueryInterfaceTypeSubstitutions(this),
137233
SubstFlags::AllowLoweredTypes);

0 commit comments

Comments
 (0)