Skip to content

Commit a0e7777

Browse files
authored
Merge pull request #6014 from DougGregor/generic-env-tail-alloc
2 parents e41a29a + 5eddbc7 commit a0e7777

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)