-
Notifications
You must be signed in to change notification settings - Fork 10.5k
ExistentialSpecializer Pass (without SILCombine/ConcreteType Propagation) #17366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@slavapestov Breaking the large PR from #13991 to this one as Part-1. Part-2 will be the changes to SILCombiner. |
aec0db1
to
23c6dd3
Compare
@slavapestov @atrick This is the transformation pass for existential specializer. Added a new test case and cleaned up the code as suggested by Slava. |
23c6dd3
to
a8a1fab
Compare
} // namespace | ||
|
||
/// Find concrete type from init_existential refs/addrs. | ||
static bool findConcreteTypeFromIE(SILValue Arg, CanType &ConcreteType) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to findConcreteTypeFromInitExistential
?
auto ArgType = Arg->getType(); | ||
auto SwiftArgType = ArgType.getASTType(); | ||
if (!ArgType || !SwiftArgType || !(ArgType.isExistentialType()) || | ||
ArgType.isAnyObject() || SwiftArgType->isAny()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please figure out why this doesn't work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will cause code blowup due to specialization -- I will add a comment. We do not want to do this for now.
auto Arg = Args[Idx]; | ||
auto ArgType = Arg->getType(); | ||
auto SwiftArgType = ArgType.getASTType(); | ||
if (!ArgType || !SwiftArgType || !(ArgType.isExistentialType()) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These will never be null
|
||
/// Find concrete type. | ||
CanType ConcreteType; | ||
int ApplyArgIdx = PAI ? Idx - Num + minPartialAppliedArgs : Idx; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe don't bother with PartialApplyInst
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
return false; | ||
|
||
/// External function definitions. | ||
if ((!Callee->isDefinition()) || Callee->isExternalDeclaration()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isExternalDeclaration is checking the same thing as isDefinition
SILType LoweredType = NewF->getLoweredType(); | ||
SILFunctionConventions Conv(SubstCalleeType, M); | ||
SILType ResultType = Conv.getSILResultType(); | ||
auto FunctionTy = LoweredType.castTo<SILFunctionType>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary cast
NormalBlock->createPHIArgument(ResultType, ValueOwnershipKind::Owned); | ||
SILBasicBlock *ErrorBlock = Thunk->createBasicBlock(); | ||
SILType Error = | ||
SILType::getPrimitiveObjectType(FunctionTy->getErrorResult().getType()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getSILType()
@@ -238,6 +238,9 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) { | |||
// Promote stack allocations to values. | |||
P.addMem2Reg(); | |||
|
|||
// Run the existential pecializer Pass. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo "specializer"
|
||
/// Return the concrete type from init_existential returned from | ||
/// findInitExistential. | ||
if (auto *IER = dyn_cast<InitExistentialRefInst>(InitExistential)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use findConcreteTypeFromInitExistential()
here?
auto Arg = Args[Idx]; | ||
auto ArgType = Arg->getType(); | ||
auto SwiftArgType = ArgType.getASTType(); | ||
if (!ArgType || !SwiftArgType || !(ArgType.isExistentialType()) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary parentheses around ArgType.isExistentialType() check
/// Then we splice the body of F to NewF after the first basic block. | ||
auto It = NewF->begin(); | ||
It++; | ||
NewF->getBlocks().splice(It, F->getBlocks()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of splice: use a TypeSubstCloner with a Builder and SubstitutionMap and then call the API visitSILFunction(original function F);
|
||
unsigned int OrigDepth = 0; | ||
if (F->getLoweredFunctionType()->isPolymorphic()) { | ||
OrigDepth = OrigCalleeGenericSig->getGenericParams().back()->getDepth(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this OrigDepth = OrigCalleeGenericSig->getGenericParams().back()->getDepth()+1;
and then remove the check for polymorphic on line 457 to F->getLoweredFunctionType()->isPolymorphic() and then the 458 is now < not <=
|
||
unsigned int OrigDepth = 0; | ||
if (F->getLoweredFunctionType()->isPolymorphic()) { | ||
OrigDepth = OrigCalleeGenericSig->getGenericParams().back()->getDepth(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do +1 here
GenericTypeParamType *GP = nullptr; | ||
if ((GP = dyn_cast<GenericTypeParamType>(type))) { | ||
if (F->getLoweredFunctionType()->isPolymorphic() && | ||
GP->getDepth() <= OrigDepth) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the isPolymorphic check and use < instead of <=
if (F->getLoweredFunctionType()->isPolymorphic() && | ||
GP->getDepth() <= OrigDepth) { | ||
return QuerySubstitutionMap{OrigSubMap}( | ||
cast<SubstitutableType>(GP->getCanonicalType())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type(GP).subst(OrigSubMap)
CalleeGenericSig, | ||
[&](SubstitutableType *type) -> Type { | ||
GenericTypeParamType *GP = nullptr; | ||
if ((GP = dyn_cast<GenericTypeParamType>(type))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (auto *GP = dyn_cast<GenericTypeParamType>(type))
a8a1fab
to
cc60047
Compare
@slavapestov Could you also review this one? All your comments are taken care of, except generic methods. |
cc60047
to
71d8c70
Compare
@slavapestov can you check this now? |
2642368
to
700d019
Compare
700d019
to
9592802
Compare
@slavapestov should be ready now? |
} | ||
|
||
/// Determine attributes of the existential addr arguments such as | ||
/// destroy_use, immutable_access. We should probably also disable InOut |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does "probably" mean here? Either they should be disabled if they don't work, or they should be enabled
|
||
auto ExistentialRepr = | ||
Arg->getType().getPreferredExistentialRepresentation(F->getModule()); | ||
if (!(ExistentialRepr == ExistentialRepresentation::Opaque || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would find if (ExistentialRepr != ExistentialRepresentation::Opaque && ExistentialRepr != ExistentialRepresentation::Class
more readable
bool ExistentialSpecializer::canSpecializeCalleeFunction(ApplySite &Apply) { | ||
|
||
/// Do not handle partial applies. | ||
if (auto *PAI = dyn_cast<PartialApplyInst>(Apply.getInstruction())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use isa
instead since you don't care about the value
if (!Callee->isDefinition()) | ||
return false; | ||
|
||
/// Ignore functions with indirect results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Next PR will lift this!
if (Callee->getConventions().hasIndirectSILResults()) | ||
return false; | ||
|
||
/// Ignore error returning functions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Next PR will life this.
/// Now add the yields. | ||
llvm::SmallVector<SILYieldInfo, 8> InterfaceYields; | ||
for (SILYieldInfo InterfaceYield : FTy->getYields()) { | ||
InterfaceYields.push_back(InterfaceYield); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you need to map this type out of context too?
auto SwiftType = ArgDesc.Arg->getType().getASTType(); | ||
auto OpenedType = | ||
SwiftType->openAnyExistentialType(Opened)->getCanonicalType(); | ||
auto OpenedSILType = NewF->getModule().Types.getLoweredType(OpenedType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just F.getLoweredType(OpenedType)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not work!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no API in F that allows you to take a Type. Its an empty one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is F.getLoweredType does not take an argument.
break; | ||
} | ||
}; | ||
Generic2OpenedTypeMap.insert( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assert that the insert succeeded
} | ||
} | ||
|
||
/// Strategy to specialize existentail arguments: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: "existential"
[&](SubstitutableType *type) -> Type { | ||
return NewFGenericEnv->mapTypeIntoContext(type); | ||
}, | ||
LookUpConformanceInSignature(*NewFGenericSig)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please just use LookUpConformanceInModule
instead. LookUpConformanceInSignature
is going away
9592802
to
b809ac7
Compare
@slavapestov please have a look. |
} else { | ||
auto Ty = param.getType(); | ||
InterfaceParams.push_back( | ||
SILParameterInfo(!Ty->hasTypeParameter() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if hasTypeParameter() returns false, mapTypeOutOfContext() does nothing. Perhaps remove this whole ?: operation and just use 'Ty'?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, mapTypeOutOfContext() in GenericEnvironment puts an assertion like this:
"assert(!hasTypeParameter() && "already have an interface type");"
So, I can not find an easy way to go around it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the type has type parameters it's already been "mapped out of context". You don't have to do anything here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if you just fix the last mapTypeOutOfContext() nitpick and use SmallVector instead of SmallDenseMap<int, ...>
(or explain why you can't) I will merge this.
/// Arguments that are not rewritten. | ||
auto Ty = params[ArgDesc.Index].getType(); | ||
auto MappedTy = ArgDesc.Arg->getType(); | ||
if (Ty->hasTypeParameter()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need the 'if'. It should either be fine to map it in unconditionally, or if that always fails, just don't map it in at all.
param.getConvention())); | ||
} else { | ||
auto Ty = param.getType(); | ||
InterfaceParams.push_back( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can replace this whole thing with InterfaceParams.push_back(param). You don't need to do anything with Ty
.
llvm::SmallVector<SILResultInfo, 8> InterfaceResults; | ||
for (auto &result : FTy->getResults()) { | ||
auto Ty = result.getType(); | ||
InterfaceResults.push_back(result.getWithType( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can replace this whole thing with InterfaceResults.push_back(result). You don't need to do anything with Ty
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or why bother with InterfaceResults at all? You can remove that SmallVector altogether and use FTy->getResults() when you build the new function type instead. The only thing that's changing here is the parameter types, everything else comes directly from the old function type and doesn't need any remapping.
if (FTy->hasErrorResult()) { | ||
auto errorResult = FTy->getErrorResult(); | ||
auto Ty = errorResult.getType(); | ||
InterfaceErrorResult = SILResultInfo( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just InterfaceErrorResult = errorResult
.
llvm::SmallVector<SILYieldInfo, 8> InterfaceYields; | ||
for (SILYieldInfo yield : FTy->getYields()) { | ||
auto Ty = yield.getType(); | ||
InterfaceYields.push_back(yield.getWithType( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can replace this whole thing with InterfaceYields.push_back(yield). You don't need to do anything with Ty
.
@rajbarik What do you think of using a SmallVector instead of |
An ExistentialTransformArgumentDescriptor is only built for an existential argument, not other arguments. So,SmallVector is not the right option? Am I missing anything? |
b809ac7
to
23a6ef7
Compare
/// Now add the errors. | ||
Optional<SILResultInfo> InterfaceErrorResult; | ||
if (FTy->hasErrorResult()) { | ||
InterfaceErrorResult = FTy->getErrorResult(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a follow-up PR, please remove this whole block. Instead, just pass FTy->getErrorResult() to SILFunctionType::get() below.
@swift-ci Please test |
@swift-ci Please test source compatibility |
@rajbarik I think this is good to merge now! Thank you for all your hard work. I'm going to merge it if the tests pass. Next, can you make a single commit which enables it by default, and we'll run the test suite and benchmarks to see if there are any regressions? |
@swift-ci Please test |
@swift-ci Please test source compatibility |
To enable this transformation by default, please change the following line in ExistentialSpecializer.cpp
to
|
@rajbarik Please make a PR that does changes the default flag value in SILOptions. Then we'll run PR testing on it. |
23a6ef7
to
cabaf2f
Compare
@swift-ci Please test |
@swift-ci Please test source compatibility |
@swift-ci Please test |
1 similar comment
@swift-ci Please test |
cabaf2f
to
89d31be
Compare
@swift-ci Please test |
Build failed |
@swift-ci Please test |
ExistentialSpecializer transforms functions with existential arguments to generic parameter types.
That is, it transforms from
to
With subsequent passes such as Inlining, Generic Specialization and SILCombine, the concrete type of P can be determined, which most likely will devirtualize a.bar() method call.
This is part of the large PR #13991
test cases will be added after the final code for SILCombine/ConcreteType Propagation lands.