-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[WIP] Visitor for AccessedStorage use-def chain walk #26896
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
[WIP] Visitor for AccessedStorage use-def chain walk #26896
Conversation
08af45a
to
127198b
Compare
@swift-ci Please benchmark |
@swift-ci Please test |
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.
Two quick comments. I want to go through this with a finer tooth comb again.
@@ -406,6 +402,40 @@ template <> struct DenseMapInfo<swift::AccessedStorage> { | |||
|
|||
namespace swift { | |||
|
|||
/// Abstract base class for a visitor passed to \c visitAccessUseDefChain. | |||
class AccessUseDefChainVisitor { |
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 use CRTP and make visitAccessUseDefChain a static method?
// TODO-NEXT: end_borrow [[INNER]] | ||
// TODO-NEXT: end_borrow [[OUTER]] | ||
// TODO-NEXT: destroy_value | ||
// CHECK: [[OUTER:%.*]] = begin_borrow |
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.
Does this expand the functionality of the pass in some way? If so, can you do that in a different PR? My hope is that this one can be just a simple refactoring so I can eye-ball 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.
The refactoring part is in 918282e6a80619e072be6b1d8f186f7ab38926e3, and the SemanticARCOpts bit in the next commit, if you want to review them separately.
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. I didn't notice it was in a separate commit.
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 might be better to split AccessedStorage
into an AccessedStorageKind
interface with the uint_8
enum and all the kind-related queries.
lib/SIL/MemAccessUtils.cpp
Outdated
SILValue operAddr = cast<LoadInst>(sourceAddr)->getOperand(); | ||
assert(isa<UncheckedTakeEnumDataAddrInst>(operAddr)); | ||
return AccessedStorageResult::incomplete(operAddr); | ||
class AccessUseDefChainVisitor { |
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 this needs to be a template parameter. findAccessedStorage[NonNested]
is the critical path for several expensive passes. Every time I debugged a compile time issue with exclusivity enabled, all the time was spent in getAccessedStorageFromAddress
. I wouldn't don't mind keeping it in the .cpp
and explicitly specializing for the few visitors, but that could mean including SILOptimizer
headers from within SIL
.
The template could be instantiated once for getAccessedStorageFromAddress
and a second time for a virtual visitor, but maybe it should just be fast everywhere.
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 we could make the implementation of visitAccessUseDefChain
always_inline
within MemAccessUtils.cpp, so that the visitor can be devirtualized.
lib/SIL/MemAccessUtils.cpp
Outdated
phiArg->getIncomingPhiValues(addressWorklist); | ||
continue; | ||
|
||
class FindAccessedStorageVisitor : public AccessUseDefChainVisitor { |
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 worklist in the outer function anymore. If you move the class into an anonymous namespace, then it would still be possible to read the outer function.
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.
Oops, I forgot to delete the now-unused vars from the outer scope. What do you mean by " it would still be possible to read the outer function". At top level, the class would still need to declare its own member variables for the state, unless I'm missing something.
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 my personal bias toward function scopes that fit on screen. I can read this:
namespace {
class Utility {
State state;
visitStuff1() {}
visitStuff2() {}
};
} // namespace
void myAPI() {
// has an implementation that fits on a screen
return Utility().doIt()
}
I can't read this:
void myAPI() {
class {
// blah blah for more than a screen
}
// who does this code belong too a method of the above class or some outer function?
return stuff
}
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, I thought you mean "read" in a language semantics sense. OTOH, scoping the nested class helps indicate that there are no uses of the class outside of the one function, and there's only one line of code outside of the class in this case.
Performance: -OCode size: -OPerformance: -Osize
Code size: -OsizePerformance: -OnoneCode size: -swiftlibsHow 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
|
@atrick Could you clarify what you mean? |
When I wrote the comment about
|
Hm. I split out the visitor methods because IMO it helped make clear the almost 1:1 type relationship between access kinds and the instruction types they correspond to—that was lost on me when I was first looking at this code—and it cleaned up a bunch of casts that were necessary in the SemanticARCOpts code before I broke out the methods. If we're going to CRTP this, I could make it so that the individual access methods all have default implementations that call a common |
The same logic for looking through projection paths is useful elsewhere.
By intercepting the walk that the findAccessedStorage walker does, we can find the exact borrow that appeared along a certain access, so we don't need to limit ourselves to a singly-borrowed base.
127198b
to
97d15f6
Compare
@swift-ci Please test |
Build failed |
Build failed |
@jckarter this looks really nice! Yes, visitors should have typed cases--that's the only time I like the visitor pattern. The user can just construct an AccessedStorage from the identified base to get all the benefits I was looking for. I was trying to micro-optimize by classifying AccessedStorage first but this is cleaner and probably just as fast. |
Factor a visitor out of
findAccessedStorage
to let other clients of the logic have more control over what's done with the information. Use the visitor in theisWrittenTo
analysis to extract the exactbegin_borrow
that an access went through in order to do the linear lifetime analysis onlet
loads.