-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add a pass to specialize opaque type archetypes. #24224
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
Add a pass to specialize opaque type archetypes. #24224
Conversation
This does not check for resilience. Resilience is currently not respected by type replacement: https://github.com/apple/swift/blob/master/lib/AST/Type.cpp#L2459 |
@swift-ci Please test |
Ah, so basically all opaque types are eliminated by this pass today? |
Yes until we fix https://github.com/apple/swift/blob/master/lib/AST/Type.cpp#L2459 |
I don't know the right answer yet how to do resilience properly: Maybe don't specialize if the |
@swift-ci Please test |
Build failed |
Build failed |
I think we should just say the opaque type is resilient if the module it’s in is resilient. Making it depend on the resilience of the underlying nominal seems like an unnecessary complication. Also the underlying nominal could be something like Array which is fragile, but you might be returning a ‘some Sequence’ still expecting it to be resilient. Finally inlinable functions would be an exception because obviously if you can see the body of the function the opaque type is not resilient. What do you think @jckarter? |
We should also add a test to validation-test/Evolution which changes an opaque type resiliently, once this is implemented. |
I meant we would call ‘’opaquedecl->getNamingDecl()’’ which returns either a abstractstoragedecl or a func decl. From there we would figure out if that decl is resilient or not in our context: here is where I am more fuzzy and would need to look at how our APIs work. But we could apply the rules you mentioned if the decl is in a different resilient module it is resilient except if the funcdecl is inline able. Or maybe I can get from the naming decl to the sil function and use SIL apis. I will look into this. |
@swift-ci Please test linux |
Build failed |
@swift-ci Please test |
Build failed |
Build failed |
@swift-ci Please test |
Build failed |
Build failed |
@swift-ci Please test linux |
@swift-ci Please smoke test |
1 similar comment
@swift-ci Please smoke test |
@slavapestov I had hoped to get some feedback from you on how best to deal with resilience. It looks like Arnold has the basic criteria implemented—the underlying type is resilient if the module is resilient, or if the body of the |
For instance, if you had:
you wouldn't necessarily want to expose |
ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypes(Type origType) const; | ||
|
||
ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypes( | ||
Type origType, ModuleDecl *modulePerformingSubstitution) const; |
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.
Can you move this method, SubstitutionMap::substOpaqueTypesWithUnderlyingTypes() and ReplaceOpaqueTypesWithUnderlyingTypes to your SIL pass since that's the only place where they're used now?
include/swift/SIL/TypeSubstCloner.h
Outdated
@@ -109,7 +109,11 @@ class TypeSubstCloner : public SILClonerWithScopes<ImplClass> { | |||
|
|||
assert(Subs.empty() || | |||
SubstCalleeSILType == | |||
Callee->getType().substGenericArgs(AI.getModule(), Subs)); |
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 assertion is a bit silly now, how about just removing it instead of teaching TypeSubstCloner about opaque types
lib/AST/Type.cpp
Outdated
auto namingDecl = opaqueRoot->getDecl()->getNamingDecl(); | ||
auto module = namingDecl->getModuleContext(); | ||
if (module->isResilient() && module != modulePerformingSubstitution && | ||
!namingDecl->getAttrs().hasAttribute<InlinableAttr>()) { |
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 us getResilienceExpansion() instead of directly checking for an inlinable attribute; you're missing a number of cases this way
|
||
// Apply the opaque types substitution. | ||
ReplaceOpaqueTypesWithUnderlyingTypes replacer(currentModule); | ||
Sty = Ty.subst(Original.getModule(), SubsMap) |
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.
since SubsMap is just the forwarding substitutions I don't think you need the first call to subst() at all, it will do nothing
return SuperTy::remapConformance(type, conf) | ||
.subst(type, replacer, replacer, | ||
SubstFlags::SubstituteOpaqueArchetypes | | ||
SubstFlags::AllowLoweredTypes); |
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.
AllowLoweredTypes isn't needed here
return SuperTy::remapASTType(ty) | ||
.subst(replacer, replacer, | ||
SubstFlags::SubstituteOpaqueArchetypes | | ||
SubstFlags::AllowLoweredTypes) |
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.
AllowLoweredTypes isn't needed here
ReplaceOpaqueTypesWithUnderlyingTypes replacer(currentModule); | ||
return ty.subst(replacer, replacer, | ||
SubstFlags::SubstituteOpaqueArchetypes | | ||
SubstFlags::AllowLoweredTypes) |
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 pretty heavy weight. How about walking the type and seeing if it contains an opaque type from the current module?
if (foundOpaqueArchetype) | ||
break; | ||
auto *allocStack = dyn_cast<AllocStackInst>(&inst); | ||
if (!allocStack || !opaqueArchetypeWouldChange( |
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 don't understand why AllocStackInsts are special. You should specialize a function even if it doesn't contain any AllocStackInsts
|
||
if (foundOpaqueArchetype) { | ||
SubstitutionMap subsMap = getFunction()->getForwardingSubstitutionMap(); | ||
OpaqueSpecializerCloner s(subsMap, *getFunction()); |
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.
the subsMap should not be used at all, I think you can try removing it. It's just burning CPU cycles to substitute using forwarding substitutions. Maybe the opaque cloner should directly inherit from SILCloner and not TypeSubstCloner?
@jckarter @aschwaighofer Yeah, I think you should look at SILFunction::getResilienceExpansion(). If a function's resilience expansion is Minimal, you should not specialize any opaque types except those from other inlinable functions. |
lib/AST/Type.cpp
Outdated
@@ -2491,6 +2501,15 @@ ReplaceOpaqueTypesWithUnderlyingTypes::operator()(CanType maybeOpaqueType, | |||
|
|||
auto archetype = archetypeAndRoot->first; | |||
auto opaqueRoot = archetypeAndRoot->second; | |||
|
|||
// Don't replace opaque types from resilient modules. |
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 bit of code is copy and pasted twice, please turn it into a method on ReplaceOpaqueTypesWithUnderlyingTypes. Also it should take the resilience expansion into account:
- If the opaque type is from an inlinable function, replace opaque type with the underlying type.
- If expansion is Maximal and we're in the same module, replace the opaque type with the underlying type.
- If expansion is Minimal and we didn't hit the first case, do nothing.
lib/SIL/SILFunctionType.cpp
Outdated
@@ -2465,7 +2476,7 @@ SILFunctionType::substGenericArgs(SILModule &silModule, | |||
LookupConformanceFn conformances) { | |||
if (!isPolymorphic()) return CanSILFunctionType(this); | |||
SILTypeSubstituter substituter(silModule, subs, conformances, | |||
getGenericSignature()); | |||
getGenericSignature(), false); |
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.
Add a comment here, /*shouldSubstituteOpaqueTypes*/ false
return shouldPerformSubstitution(opaque, context); | ||
} | ||
|
||
bool ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution( |
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.
@slavapestov Can you check whether you agree with these rules:
- if the function with the opaque result type is inline-able allow substitution
- if the context function in which we perform substitution has maximal resilience expansion and the context function and the opaque type's function are in the same module allow substitution
- if the opaque types's module is not resilient allow substitution
- otherwise disallow substitution
@swift-ci Please test |
Build failed |
Build failed |
Clones functions containing opaque type archetypes replacing the opaque type by a concrete type. rdar://46140751
We might have to replace opaque archetypes to satisfy equality of types
…aque result types
* Opaque types are abi compatible with their substituted types * Insert casts in more places * Respect no optimization attribute
cfc34b1
to
0e313c7
Compare
@swift-ci Please test |
Build failed |
Build failed |
@@ -0,0 +1,466 @@ | |||
#define DEBUG_TYPE "opaque-archetype-specializer" |
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 needs a copyright notice, etc
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.
|
||
static Type substOpaqueTypesWithUnderlyingTypes( | ||
Type ty, SILFunction *context) { | ||
ReplaceOpaqueTypesWithUnderlyingTypes replacer(context); |
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 move ReplaceOpaqueTypesWithUnderlyingTypes into this file
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.
It is now also used by lib/SIL/SILFunctionType.cpp because substituted and unsubstituted are ABI compatible.
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.
Ah, ok
if (!getBuilder().hasOwnership()) { | ||
switch (Inst->getOwnershipQualifier()) { | ||
case StoreOwnershipQualifier::Assign: { | ||
auto *li = getBuilder().createLoad(getOpLocation(Inst->getLoc()), dst, |
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 do you lower assigns to load/store here instead of letting them be?
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.
Same code as SILCloner.
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 does it need to appear here?
auto cast = getBuilder().createUncheckedAddrCast(apply.getLoc(), | ||
opd.get(), argType); | ||
opd.set(cast); | ||
} else if (argType.is<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.
What if its an optional of a function type, or a tuple type, etc? You need to recursively transform types instead
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.
Good point! I will go through memory for now in those cases.
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 broke the windows build! https://dev.azure.com/compnerd/windows-swift/_build/results?buildId=1963 |
I will fix. |
Clones functions containing opaque type archetypes replacing the opaque
type by a concrete type.
rdar://46140751