Skip to content

Commit 4aa7eba

Browse files
authored
Merge pull request #21316 from atrick/existential-specialize-rewrite
Generalize and cleanup code for existential specialization.
2 parents 192954c + c7dcd21 commit 4aa7eba

File tree

6 files changed

+500
-502
lines changed

6 files changed

+500
-502
lines changed

include/swift/SILOptimizer/Utils/Existential.h

Lines changed: 71 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -21,33 +21,35 @@
2121

2222
namespace swift {
2323

24-
/// Find InitExistential from global_addr and copy_addr.
25-
SILValue findInitExistentialFromGlobalAddrAndCopyAddr(GlobalAddrInst *GAI,
26-
CopyAddrInst *CAI);
27-
28-
/// Find InitExistential from global_addr and an apply argument.
29-
SILValue findInitExistentialFromGlobalAddrAndApply(GlobalAddrInst *GAI,
30-
ApplySite Apply, int ArgIdx);
31-
32-
/// Returns the address of an object with which the stack location \p ASI is
33-
/// initialized. This is either a init_existential_addr or the destination of a
34-
/// copy_addr. Returns a null value if the address does not dominate the
35-
/// alloc_stack user \p ASIUser.
36-
/// If the value is copied from another stack location, \p isCopied is set to
37-
/// true.
38-
SILValue getAddressOfStackInit(SILValue allocStackAddr, SILInstruction *ASIUser,
39-
bool &isCopied);
40-
41-
/// Find the init_existential, which could be used to determine a concrete
42-
/// type of the value used by \p openedUse.
43-
/// If the value is copied from another stack location, \p isCopied is set to
44-
/// true.
24+
/// Record information about an opened archetype.
4525
///
46-
/// FIXME: replace all uses of this with ConcreteExistentialInfo.
47-
SILInstruction *findInitExistential(Operand &openedUse,
48-
ArchetypeType *&OpenedArchetype,
49-
SILValue &OpenedArchetypeDef,
50-
bool &isCopied);
26+
/// This is used to determine whether a generic call argument originates from
27+
/// an opened existential. For example:
28+
/// %o = open_existential_ref %e : $P & Q to $@opened("PQ") P & Q
29+
/// %r = apply %f<@opened("PQ") P & Q>(%o)
30+
/// : $@convention(method) <τ_0_0 where τ_0_0 : P, τ_0_0 : Q>
31+
/// (@guaranteed τ_0_0) -> @owned τ_0_0
32+
///
33+
/// When successfull, ConcreteExistentialInfo can be used to determine the
34+
/// concrete type of the opened existential.
35+
struct OpenedArchetypeInfo {
36+
ArchetypeType *OpenedArchetype = nullptr;
37+
// The opened value.
38+
SingleValueInstruction *OpenedArchetypeValue;
39+
// The existential value.
40+
SILValue ExistentialValue;
41+
// True if the openedValue is copied from another stack location
42+
bool isOpenedValueCopied = false;
43+
44+
// Construct a valid instance if the given use originates from a recognizable
45+
// OpenedArchetype instruction.
46+
OpenedArchetypeInfo(Operand &use);
47+
48+
bool isValid() const {
49+
assert(!OpenedArchetype || (OpenedArchetypeValue && ExistentialValue));
50+
return OpenedArchetype;
51+
}
52+
};
5153

5254
/// Record conformance and concrete type info derived from an init_existential
5355
/// value that is reopened before it's use. This is useful for finding the
@@ -60,20 +62,16 @@ SILInstruction *findInitExistential(Operand &openedUse,
6062
/// : $@convention(method) <τ_0_0 where τ_0_0 : P, τ_0_0 : Q>
6163
/// (@guaranteed τ_0_0) -> @owned τ_0_0
6264
struct ConcreteExistentialInfo {
63-
// The opened type passed as self. `$@opened("PQ")` above.
64-
// This is also the replacement type of the method's Self type.
65-
ArchetypeType *OpenedArchetype = nullptr;
66-
// The definition of the OpenedArchetype.
67-
SILValue OpenedArchetypeDef;
68-
// True if the openedValue is copied from another stack location
69-
bool isCopied;
70-
// The init_existential instruction that produces the opened existential.
71-
SILInstruction *InitExistential = nullptr;
7265
// The existential type of the self argument before it is opened,
7366
// produced by an init_existential.
7467
CanType ExistentialType;
7568
// The concrete type of self from the init_existential. `$C` above.
7669
CanType ConcreteType;
70+
// The concrete value used to initialize the opened existential.
71+
// `%c` in the above comment.
72+
SILValue ConcreteValue;
73+
// True if the ConcreteValue is copied from another stack location
74+
bool isConcreteValueCopied = false;
7775
// When ConcreteType is itself an opened existential, record the type
7876
// definition. May be nullptr for a valid AppliedConcreteType.
7977
SingleValueInstruction *ConcreteTypeDef = nullptr;
@@ -82,31 +80,20 @@ struct ConcreteExistentialInfo {
8280
// and includes the full list of existential conformances.
8381
// signature: <P & Q>, replacement: $C : conformances: [$P, $Q]
8482
SubstitutionMap ExistentialSubs;
85-
// The value of concrete type used to initialize the existential. `%c` above.
86-
SILValue ConcreteValue;
8783

88-
// Search for a recognized pattern in which the given value is an opened
89-
// existential that was previously initialized to a concrete type.
90-
// Constructs a valid ConcreteExistentialInfo object if successfull.
91-
ConcreteExistentialInfo(Operand &openedUse);
84+
// Search for a recognized pattern in which the given existential value is
85+
// initialized to a concrete type. Constructs a valid ConcreteExistentialInfo
86+
// object if successfull.
87+
ConcreteExistentialInfo(SILValue existential, SILInstruction *user);
9288

9389
// This constructor initializes a ConcreteExistentialInfo based on already
94-
// known ConcreteType and ProtocolDecl pair. It determines the
95-
// OpenedArchetypeDef for the ArgOperand that will be used by unchecked_cast
96-
// instructions to cast OpenedArchetypeDef to ConcreteType.
97-
ConcreteExistentialInfo(Operand &ArgOperand, CanType ConcreteType,
98-
ProtocolDecl *Protocol);
90+
// known ConcreteType and ProtocolDecl pair.
91+
ConcreteExistentialInfo(SILValue existential, SILInstruction *user,
92+
CanType ConcreteType, ProtocolDecl *Protocol);
9993

10094
/// For scenerios where ConcreteExistentialInfo is created using a known
101-
/// ConcreteType and ProtocolDecl, both of InitExistential
102-
/// and ConcreteValue can be null. So there is no need for explicit check for
103-
/// not null for them instead we assert on (!InitExistential ||
104-
/// ConcreteValue).
105-
bool isValid() const {
106-
assert(!InitExistential || ConcreteValue);
107-
return OpenedArchetype && OpenedArchetypeDef && ConcreteType &&
108-
!ExistentialSubs.empty();
109-
}
95+
/// ConcreteType and ProtocolDecl, the ConcreteValue can be null.
96+
bool isValid() const { return ConcreteType && !ExistentialSubs.empty(); }
11097

11198
// Do a conformance lookup on ConcreteType with the given requirement, P. If P
11299
// is satisfiable based on the existential's conformance, return the new
@@ -116,6 +103,35 @@ struct ConcreteExistentialInfo {
116103
CanType selfTy = P->getSelfInterfaceType()->getCanonicalType();
117104
return ExistentialSubs.lookupConformance(selfTy, P);
118105
}
106+
107+
private:
108+
void initializeSubstitutionMap(
109+
ArrayRef<ProtocolConformanceRef> ExistentialConformances, SILModule *M);
110+
111+
void initializeConcreteTypeDef(SILInstruction *typeConversionInst);
112+
};
113+
114+
// Convenience for tracking both the OpenedArchetypeInfo and
115+
// ConcreteExistentialInfo from the same SILValue.
116+
struct ConcreteOpenedExistentialInfo {
117+
OpenedArchetypeInfo OAI;
118+
// If CEI has a value, it must be valid.
119+
Optional<ConcreteExistentialInfo> CEI;
120+
121+
ConcreteOpenedExistentialInfo(Operand &use);
122+
123+
// Provide a whole module type-inferred ConcreteType to fall back on if the
124+
// concrete type cannot be determined from data flow.
125+
ConcreteOpenedExistentialInfo(Operand &use, CanType concreteType,
126+
ProtocolDecl *protocol);
127+
128+
bool isValid() const {
129+
if (!CEI)
130+
return false;
131+
132+
assert(CEI->isValid());
133+
return true;
134+
}
119135
};
120136

121137
} // end namespace swift

lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp

Lines changed: 30 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ class ExistentialSpecializer : public SILFunctionTransform {
3838
/// Determine if the current function is a target for existential
3939
/// specialization of args.
4040
bool canSpecializeExistentialArgsInFunction(
41-
ApplySite &Apply,
41+
FullApplySite &Apply,
4242
llvm::SmallDenseMap<int, ExistentialTransformArgumentDescriptor>
4343
&ExistentialArgDescriptor);
4444

4545
/// Can Callee be specialized?
46-
bool canSpecializeCalleeFunction(ApplySite &Apply);
46+
bool canSpecializeCalleeFunction(FullApplySite &Apply);
4747

4848
/// Specialize existential args in function F.
4949
void specializeExistentialArgsInAppliesWithinFunction(SILFunction &F);
@@ -70,82 +70,6 @@ class ExistentialSpecializer : public SILFunctionTransform {
7070
};
7171
} // namespace
7272

73-
/// Find concrete type from init_existential_refs/addrs.
74-
static bool findConcreteTypeFromInitExistential(SILValue Arg,
75-
CanType &ConcreteType) {
76-
if (auto *IER = dyn_cast<InitExistentialRefInst>(Arg)) {
77-
ConcreteType = IER->getFormalConcreteType();
78-
return true;
79-
} else if (auto *IE = dyn_cast<InitExistentialAddrInst>(Arg)) {
80-
ConcreteType = IE->getFormalConcreteType();
81-
return true;
82-
}
83-
return false;
84-
}
85-
86-
/// Find the concrete type of the existential argument. Wrapper
87-
/// for findInitExistential in Local.h/cpp. In future, this code
88-
/// can move to Local.h/cpp.
89-
static bool findConcreteType(ApplySite AI, int ArgIdx, CanType &ConcreteType) {
90-
bool isCopied = false;
91-
auto Arg = AI.getArgument(ArgIdx);
92-
93-
/// Ignore any unconditional cast instructions. Is it Safe? Do we need to
94-
/// also add UnconditionalCheckedCastAddrInst? TODO.
95-
if (auto *Instance = dyn_cast<UnconditionalCheckedCastInst>(Arg)) {
96-
Arg = Instance->getOperand();
97-
}
98-
99-
/// Return init_existential if the Arg is global_addr.
100-
if (auto *GAI = dyn_cast<GlobalAddrInst>(Arg)) {
101-
SILValue InitExistential =
102-
findInitExistentialFromGlobalAddrAndApply(GAI, AI, ArgIdx);
103-
/// If the Arg is already init_existential, return the concrete type.
104-
if (InitExistential &&
105-
findConcreteTypeFromInitExistential(InitExistential, ConcreteType)) {
106-
return true;
107-
}
108-
}
109-
110-
/// Handle AllocStack instruction separately.
111-
if (auto *Instance = dyn_cast<AllocStackInst>(Arg)) {
112-
if (SILValue Src =
113-
getAddressOfStackInit(Instance, AI.getInstruction(), isCopied)) {
114-
Arg = Src;
115-
}
116-
}
117-
118-
/// If the Arg is already init_existential after getAddressofStackInit
119-
/// call, return the concrete type.
120-
if (findConcreteTypeFromInitExistential(Arg, ConcreteType)) {
121-
return true;
122-
}
123-
124-
/// Call findInitExistential and determine the init_existential.
125-
ArchetypeType *OpenedArchetype = nullptr;
126-
SILValue OpenedArchetypeDef;
127-
auto FAS = FullApplySite::isa(AI.getInstruction());
128-
if (!FAS)
129-
return false;
130-
SILInstruction *InitExistential =
131-
findInitExistential(FAS.getArgumentOperands()[ArgIdx], OpenedArchetype,
132-
OpenedArchetypeDef, isCopied);
133-
if (!InitExistential) {
134-
LLVM_DEBUG(llvm::dbgs() << "ExistentialSpecializer Pass: Bail! Due to "
135-
"findInitExistential\n";);
136-
return false;
137-
}
138-
139-
/// Return the concrete type from init_existential returned from
140-
/// findInitExistential.
141-
if (auto *SingleVal = InitExistential->castToSingleValueInstruction()) {
142-
if (findConcreteTypeFromInitExistential(SingleVal, ConcreteType)) {
143-
return true;
144-
}
145-
}
146-
return false;
147-
}
148-
14973
/// Check if the argument Arg is used in a destroy_use instruction.
15074
static void
15175
findIfCalleeUsesArgInDestroyUse(SILValue Arg,
@@ -162,17 +86,21 @@ findIfCalleeUsesArgInDestroyUse(SILValue Arg,
16286
/// Check if any apply argument meets the criteria for existential
16387
/// specialization.
16488
bool ExistentialSpecializer::canSpecializeExistentialArgsInFunction(
165-
ApplySite &Apply,
89+
FullApplySite &Apply,
16690
llvm::SmallDenseMap<int, ExistentialTransformArgumentDescriptor>
16791
&ExistentialArgDescriptor) {
16892
auto *F = Apply.getReferencedFunction();
169-
auto Args = F->begin()->getFunctionArguments();
93+
auto CalleeArgs = F->begin()->getFunctionArguments();
17094
bool returnFlag = false;
17195

172-
/// Analyze the argument for protocol conformance.
173-
for (unsigned Idx = 0, Num = Args.size(); Idx < Num; ++Idx) {
174-
auto Arg = Args[Idx];
175-
auto ArgType = Arg->getType();
96+
/// Analyze the argument for protocol conformance. Iterator over the callee's
97+
/// function arguments. The same SIL argument index is used for both caller
98+
/// and callee side arguments.
99+
auto origCalleeConv = Apply.getOrigCalleeConv();
100+
assert(Apply.getCalleeArgIndexOfFirstAppliedArg() == 0);
101+
for (unsigned Idx = 0, Num = CalleeArgs.size(); Idx < Num; ++Idx) {
102+
auto CalleeArg = CalleeArgs[Idx];
103+
auto ArgType = CalleeArg->getType();
176104
auto SwiftArgType = ArgType.getASTType();
177105

178106
/// Checking for AnyObject and Any is added to ensure that we do not blow up
@@ -184,18 +112,21 @@ bool ExistentialSpecializer::canSpecializeExistentialArgsInFunction(
184112
continue;
185113

186114
auto ExistentialRepr =
187-
Arg->getType().getPreferredExistentialRepresentation(F->getModule());
115+
CalleeArg->getType().getPreferredExistentialRepresentation(
116+
F->getModule());
188117
if (ExistentialRepr != ExistentialRepresentation::Opaque &&
189118
ExistentialRepr != ExistentialRepresentation::Class)
190119
continue;
191120

192121
/// Find the concrete type.
193-
CanType ConcreteType;
194-
if (!findConcreteType(Apply, Idx, ConcreteType)) {
122+
Operand &ArgOper = Apply.getArgumentRef(Idx);
123+
CanType ConcreteType =
124+
ConcreteExistentialInfo(ArgOper.get(), ArgOper.getUser()).ConcreteType;
125+
if (!ConcreteType) {
195126
LLVM_DEBUG(
196127
llvm::dbgs()
197-
<< "ExistentialSpecializer Pass: Bail! Due to findConcreteType "
198-
"for callee:"
128+
<< "ExistentialSpecializer Pass: Bail! cannot find ConcreteType "
129+
"for call argument to:"
199130
<< F->getName() << " in caller:"
200131
<< Apply.getInstruction()->getParent()->getParent()->getName()
201132
<< "\n";);
@@ -205,15 +136,15 @@ bool ExistentialSpecializer::canSpecializeExistentialArgsInFunction(
205136
/// Determine attributes of the existential addr arguments such as
206137
/// destroy_use, immutable_access.
207138
ExistentialTransformArgumentDescriptor ETAD;
208-
ETAD.AccessType =
209-
Apply.getOrigCalleeType()->getParameters()[Idx].isIndirectMutating() ||
210-
Apply.getOrigCalleeType()->getParameters()[Idx].isConsumed()
211-
? OpenedExistentialAccess::Mutable
212-
: OpenedExistentialAccess::Immutable;
139+
auto paramInfo = origCalleeConv.getParamInfoForSILArg(Idx);
140+
ETAD.AccessType = (paramInfo.isIndirectMutating() || paramInfo.isConsumed())
141+
? OpenedExistentialAccess::Mutable
142+
: OpenedExistentialAccess::Immutable;
213143
ETAD.DestroyAddrUse = false;
214-
if ((Args[Idx]->getType().getPreferredExistentialRepresentation(
215-
F->getModule())) != ExistentialRepresentation::Class)
216-
findIfCalleeUsesArgInDestroyUse(Arg, ETAD);
144+
if ((CalleeArgs[Idx]->getType().getPreferredExistentialRepresentation(
145+
F->getModule()))
146+
!= ExistentialRepresentation::Class)
147+
findIfCalleeUsesArgInDestroyUse(CalleeArg, ETAD);
217148

218149
/// Save the attributes
219150
ExistentialArgDescriptor[Idx] = ETAD;
@@ -226,12 +157,7 @@ bool ExistentialSpecializer::canSpecializeExistentialArgsInFunction(
226157
}
227158

228159
/// Determine if this callee function can be specialized or not.
229-
bool ExistentialSpecializer::canSpecializeCalleeFunction(ApplySite &Apply) {
230-
231-
/// Do not handle partial applies.
232-
if (isa<PartialApplyInst>(Apply.getInstruction())) {
233-
return false;
234-
}
160+
bool ExistentialSpecializer::canSpecializeCalleeFunction(FullApplySite &Apply) {
235161

236162
/// Determine the caller of the apply.
237163
auto *Callee = Apply.getReferencedFunction();
@@ -285,7 +211,7 @@ void ExistentialSpecializer::specializeExistentialArgsInAppliesWithinFunction(
285211
auto *I = &*It;
286212

287213
/// Is it an apply site?
288-
ApplySite Apply = ApplySite::isa(I);
214+
FullApplySite Apply = FullApplySite::isa(I);
289215
if (!Apply)
290216
continue;
291217

0 commit comments

Comments
 (0)