-
Notifications
You must be signed in to change notification settings - Fork 10.5k
GlobalOpt: optimize static properties with resilient types. #21614
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
@swift-ci test |
@swift-ci benchmark |
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.
How important is this optimization? Can it wait until after opaque values?
} | ||
|
||
// Search for a matching lowering in the linked list of lowerings. | ||
while (true) { |
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 looks O(n), can we do this better?
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.
We could, but currently it's not worth the effort, as n = 1.
In case we add many more resilience expansions in the future we can think of a more complicated lookup mechanism.
bool isLoadableOrOpaque(SILType Ty) { | ||
if (!F) { | ||
// We are inserting into the static initializer of a SILGlobalVariable. | ||
// All types used there are loadable by definition. |
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 the emitter for the initializer has a bug? I think you still want to use ResilienceExpansion::Maximal 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.
Unfortunately this does not work. The conservative assumption would trigger a false assert.
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.
Oh wait, you are saying ResilienceExpansion::Maximal. Yeah, that's a good idea.
@@ -1139,7 +1139,7 @@ namespace { | |||
/// Build the appropriate TypeLowering subclass for the given type, | |||
/// which is assumed to already have been lowered. | |||
class LowerType | |||
: public TypeClassifierBase<LowerType, const TypeLowering *> | |||
: public TypeClassifierBase<LowerType, TypeLowering *> |
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.
Last time I tried to plumb through something like this I had to change a lot more code. In particular it looks like class LowerType
has a number of recursive calls to TC.getTypeLowering(), and we're not passing the expansion down through them.
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.
Look at #5524, where I was trying to do the same thing but with a generic signature.
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.
Hm, as I see it, the Expansion is passed down recursively (with classifyType).
Except in visitTupleType - maybe this is a bug?
Anyway, in case it's not passed down in some cases, it just makes the analysis more conservative.
Build comment file:Performance: -O
Performance: -Osize
Performance: -Onone
Code size: -swiftlibs
How to read the dataThe tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.If you see any unexpected regressions, you should consider fixing the Noise: Sometimes the performance results (not code size!) contain false Hardware Overview
|
Now that I think about it wouldn’t it make more sense to change SILGen to use the new SILFunction-specific entry point? Then you won’t need your new peephole at all. This was going to be my approach when I wanted to implement resilience-aware type lowering in SIL. |
I experimented with that a bit, but it didn't work out. The reason is that the initializer of the resilient struct still has to return the struct indirectly. Only after inlining this initializer into the globalinit function the peephole can do its work. |
@eeckstein ah, I see. In any case we should eventually fix SILGen to do the right thing here. Also once we get opaque values, SILGen won't be initializing structs in memory in-place with struct_element_addr anymore; the whole So I'm fine with landing this as-is, if you fix the >80 char lines and also fix the TupleType case. |
public struct ResilientStruct { | ||
var rawValue: Int | ||
|
||
public static let staticVal = ResilientStruct(rawValue: 27) |
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 add some more test case too though? Global variables, as well as maybe statics with tuple type, or something involving enums.
This lets the SIL optimizer reason about types (e.g. if a type is loadable) in specific functions. For example, a type might be loadable in a resilient function, but not in an inlinable function.
…ilience. Allow creating instructions with types which are only loadable in resilient functions (but not in all functions).
For example: struct ResilientStruct { var singleField: Int public static let x = ResilientStruct(singleField: 27) } The generated (resilient) getter for x should just return a 27 (wrapped in a struct) and not lazily initialize a global variable. rdar://problem/46712028
f8f39ca
to
aa5a4e1
Compare
I will fix the tuple case and add more tests in a follow-up PR. This GlobalOpt peephole is pretty limited and I cannot create test cases for more complex types with it. I'll use the new isLoadable API in other optimizations where I can create more test cases for it. |
@swift-ci smoke test |
@eeckstein Ok, thanks. I'm going to revisit this code soon since I want to implement this optimization at the SILGen level as well. |
For example:
The generated (resilient) getter for x should just return a 27 (wrapped in a struct) and not lazily initialize a global variable.
The main SIL infrastructure change for this is to store type lowerings for multiple resilience expansions.
This lets the SIL optimizer reason about types (e.g. if a type is loadable) in specific functions.
For example, a type might be loadable in a resilient function, but not in an inlinable function.
rdar://problem/46712028