Skip to content

Commit b315246

Browse files
author
Raj Barik
committed
ProtocolDevirtualizer Part 3: Peephole Optimization to Propagate Concrete Types
1 parent 2f814dd commit b315246

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,9 @@ class SILCombiner :
295295
WitnessMethodInst *WMI);
296296
SILInstruction *propagateConcreteTypeOfInitExistential(FullApplySite AI);
297297

298+
/// Propagate concrete types to all apply arguments.
299+
SILInstruction *propagateConcreteTypeOfInitExistentialToAllApplyArgs(FullApplySite AI);
300+
298301
/// Perform one SILCombine iteration.
299302
bool doOneIteration(SILFunction &F, unsigned Iteration);
300303

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,140 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI) {
11581158
return propagateConcreteTypeOfInitExistential(AI, PD, PropagateIntoOperand);
11591159
}
11601160

1161+
/// A peephole optimizer that propagates concrete types of all arguments to Apply.
1162+
SILInstruction *
1163+
SILCombiner::propagateConcreteTypeOfInitExistentialToAllApplyArgs(FullApplySite AI) {
1164+
/// Check if legal to perform propagation.
1165+
if (!AI.hasSubstitutions())
1166+
return nullptr;
1167+
auto *Callee = AI.getReferencedFunction();
1168+
if (!Callee)
1169+
return nullptr;
1170+
1171+
auto FnTy = AI.getCallee()->getType().castTo<SILFunctionType>();
1172+
if(!FnTy->isPolymorphic())
1173+
return nullptr;
1174+
1175+
/// Find a mapping from Archetype to the Protocol declaration.
1176+
ArrayRef<Substitution> CallerSubs = Callee->getForwardingSubstitutions();
1177+
llvm::DenseMap<Type, ProtocolDecl *> GenericType2ProtocolMap;
1178+
for(auto &sub : CallerSubs) {
1179+
auto ReplacementType = sub.getReplacement();
1180+
auto Conformances = sub.getConformances();
1181+
ArchetypeType * ArcheType;
1182+
if (ReplacementType->hasArchetype() &&
1183+
(ArcheType = ReplacementType->getAs<ArchetypeType>()) &&
1184+
(Conformances.size() == 1)) {
1185+
auto ProtoDecl = Conformances[0].getRequirement();
1186+
GenericType2ProtocolMap[ArcheType->getInterfaceType()] = ProtoDecl;
1187+
}
1188+
}
1189+
1190+
auto Args = Callee->begin()->getFunctionArguments();
1191+
SmallVector<SILValue, 8> NewApplyArgs;
1192+
llvm::DenseMap<SubstitutableType *, Type> GenericType2ConcreteTypeMap;
1193+
llvm::DenseMap<Type, Optional<ProtocolConformanceRef>> ConcreteType2ConformanceMap;
1194+
bool MatchPattern = false;
1195+
1196+
/// We are looking for the following pattern (can be extended in the future).
1197+
/// %0 = alloc_ref $SomeClass
1198+
/// %1 = init_existential_ref %0 : $SomeClass : $SomeClass, $SomeProtocol
1199+
/// %2 = function_ref @something_to_devirtualize : $@convention(thin) (@guaranteed SomeProtocol) -> Int
1200+
/// %3 = apply %2(%1) : $@convention(thin) (@guaranteed SomeProtocol) -> Int
1201+
for (unsigned i = 0, e = Args.size(); i != e; ++i) {
1202+
auto ArgType = Args[i]->getType();
1203+
auto ArgASTType = ArgType.getSwiftRValueType();
1204+
auto OrigApplyArg = AI.getArgument(i);
1205+
ArchetypeType *ArcheType;
1206+
OpenExistentialRefInst *Open;
1207+
InitExistentialRefInst *IE;
1208+
/// Check all conditions for pattern matching.
1209+
if (ArgASTType->hasArchetype() && (ArcheType = ArgASTType->getAs<ArchetypeType>()) &&
1210+
(ArcheType->getInterfaceType()->is<GenericTypeParamType>()) &&
1211+
GenericType2ProtocolMap[ArcheType->getInterfaceType()] &&
1212+
(Open = dyn_cast<OpenExistentialRefInst>(OrigApplyArg)) &&
1213+
(IE = dyn_cast<InitExistentialRefInst>(Open->getOperand()))
1214+
) {
1215+
auto Protocol = GenericType2ProtocolMap[ArcheType->getInterfaceType()];
1216+
auto Conformances = IE->getConformances();
1217+
auto ConcreteType = IE->getFormalConcreteType();
1218+
auto NewApplyArg = IE->getOperand();
1219+
auto ExistentialType = IE->getType().getSwiftRValueType();
1220+
auto OpenedArchetype = Open->getType().castTo<ArchetypeType>();;
1221+
auto ExistentialSig = AI.getModule().getASTContext()
1222+
.getExistentialSignature(ExistentialType,
1223+
AI.getModule().getSwiftModule());
1224+
1225+
Substitution ConcreteSub(ConcreteType, Conformances);
1226+
auto SubMap = ExistentialSig->getSubstitutionMap({&ConcreteSub, 1});
1227+
auto Conformance = SubMap.lookupConformance(
1228+
CanType(ExistentialSig->getGenericParams()[0]), Protocol);
1229+
/// Store the concrete type and the conformance.
1230+
GenericType2ConcreteTypeMap[OpenedArchetype] = ConcreteType;
1231+
ConcreteType2ConformanceMap[ConcreteType] = Conformance;
1232+
NewApplyArgs.push_back(NewApplyArg);
1233+
MatchPattern = true;
1234+
} else {
1235+
NewApplyArgs.push_back(OrigApplyArg);
1236+
}
1237+
}
1238+
/// Bail if we did not find a pattern.
1239+
if (!MatchPattern) {
1240+
return nullptr;
1241+
}
1242+
1243+
/// Create the substition map for Apply. Boilerplate code.
1244+
SmallVector<Substitution, 8> Substitutions;
1245+
SILType NewSubstCalleeType;
1246+
auto FnSubsMap =
1247+
FnTy->getGenericSignature()->getSubstitutionMap(AI.getSubstitutions());
1248+
auto FinalSubsMap = FnSubsMap.subst(
1249+
[&](SubstitutableType *type) -> Type {
1250+
if (GenericType2ConcreteTypeMap[type])
1251+
return GenericType2ConcreteTypeMap[type];
1252+
return type;
1253+
},
1254+
[&](CanType origTy, Type substTy,
1255+
ProtocolType *proto) -> Optional<ProtocolConformanceRef> {
1256+
if (ConcreteType2ConformanceMap[substTy]) {
1257+
auto Conformance = ConcreteType2ConformanceMap[substTy];
1258+
assert(proto->getDecl() == Conformance->getRequirement());
1259+
return Conformance;
1260+
}
1261+
return ProtocolConformanceRef(proto->getDecl());
1262+
});
1263+
FnTy->getGenericSignature()->getSubstitutions(FinalSubsMap, Substitutions);
1264+
CanSILFunctionType SFT = FnTy->substGenericArgs(
1265+
AI.getModule(),
1266+
Substitutions);
1267+
NewSubstCalleeType = SILType::getPrimitiveObjectType(SFT);
1268+
1269+
/// Create a new ApplySite
1270+
FullApplySite NewAI;
1271+
1272+
/// Setup the builder.
1273+
Builder.setCurrentDebugScope(AI.getDebugScope());
1274+
Builder.addOpenedArchetypeOperands(AI.getInstruction());
1275+
1276+
/// Create the new Apply Instruction with concrete types
1277+
if (auto *TAI = dyn_cast<TryApplyInst>(AI))
1278+
NewAI = Builder.createTryApply(AI.getLoc(), AI.getCallee(), Substitutions,
1279+
NewApplyArgs, TAI->getNormalBB(), TAI->getErrorBB());
1280+
else
1281+
NewAI = Builder.createApply(AI.getLoc(), AI.getCallee(), Substitutions,
1282+
NewApplyArgs, cast<ApplyInst>(AI)->isNonThrowing());
1283+
1284+
/// Rewrite the uses of Apply based on the new one.
1285+
if (auto apply = dyn_cast<ApplyInst>(NewAI))
1286+
replaceInstUsesWith(*cast<ApplyInst>(AI.getInstruction()), apply);
1287+
1288+
/// Delete the Apply Site.
1289+
eraseInstFromFunction(*AI.getInstruction());
1290+
1291+
return NewAI.getInstruction();
1292+
}
1293+
1294+
11611295
/// \brief Check that all users of the apply are retain/release ignoring one
11621296
/// user.
11631297
static bool
@@ -1387,6 +1521,8 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) {
13871521
if (isa<FunctionRefInst>(AI->getCallee())) {
13881522
if (propagateConcreteTypeOfInitExistential(AI)) {
13891523
return nullptr;
1524+
} else if (propagateConcreteTypeOfInitExistentialToAllApplyArgs(AI)) {
1525+
return nullptr;
13901526
}
13911527
}
13921528

0 commit comments

Comments
 (0)