Skip to content

Commit 118a725

Browse files
committed
Generalize and cleanup code for existential specialization.
Generalizes the ConcreteExistentialInfo abstraction so it can be used both by the ExistentialSpecializer and SILCombine, allowing redundant code in ExistentialSpecializer.cpp to be deleted. Splits OpenedArchetypeInfo from ConcreteExistentialInfo. Adds a ConcreteOpenedArchetypeInfo convenience wrapper around them both, for use wherever we were originally using ConcreteExistentialInfo. Splits getAddressOfStackInit into getStackInitInst, This is cleaner and allows both the ExistentialSpecializer and SILCombine to handle more interesting cases in the future, like unconditional_checked_cast. Creates utilities, initializeSubstitutionMap, and initializeConcreteTypeDef to simplify an generalize ConcreteExistentialInfo. While rewriting ExistentialSpecializer to use the new abstraction, I fixed a latent bug in which is was using a SIL argument index as a function type parameter index (this would have broken up if/when we decide to enable calls with indirect results).
1 parent 9afa479 commit 118a725

File tree

6 files changed

+492
-502
lines changed

6 files changed

+492
-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)