-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[sil-inst-opt] Change InstModCallbacks to specify a setUseValue callback instead of RAUW callbacks. #35256
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
[sil-inst-opt] Change InstModCallbacks to specify a setUseValue callback instead of RAUW callbacks. #35256
Conversation
…ack instead of RAUW callbacks. This allows for me to do a couple of things improving quality/correctness/ease of use: 1. I reimplemented InstMod's RAUW and RAUW/erase helpers on top of setUseValue/deleteInst. Beyond allowing the caller to specify less things, we gain an orthogonality preventing bugs like overriding erase/RAUW but not overriding erase or having the erase used in erase/RAUW act differently than the erase for deleteInst. 2. There were a bunch of places using InstModCallback that also were setting uses without having the ability for InstModCallbacks perform it (since it only supported RAUW). This is an anti-pattern and could cause subtle bugs to be introduced by appropriate state in the caller not being updated.
… to wereAnyCallbacksInvoked. This makes it clear that we are not talking about a madeChange in a general sense and are instead just tracking if /any/ of our callbacks were invoked. This is still useful enough for our users and will prevent confusion.
@swift-ci smoke test |
[this](SingleValueInstruction *i, SILValue value) { | ||
eraseAndRAUWSingleValueInstruction(i, value); | ||
[this](Operand *use, SILValue newValue) { | ||
use->set(newValue); |
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 hunk may be slightly confusing. What is happening is that rather than passing an empty argument for createdNewInst, I am just using an overload of InstModCallback's constructor that omits it (and thus assumes we want the default... which we do).
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.
A single RAUW could replace thousands of operands. We design IR's very carefully so operand setting can be as efficient as possible. We definitely don't want to do a hash lookup each time. There only needs to be a single worklist insertion per RAUW operation.
@@ -434,7 +434,7 @@ void OwnershipRAUWUtility::eliminateReborrowsOfRecursiveBorrows( | |||
|
|||
// Then set our borrowing operand to take our innerBorrow instead of value | |||
// (whose lifetime we just ended). | |||
borrowingOperand->set(innerBorrow); | |||
callbacks.setUseValue(*borrowingOperand, innerBorrow); |
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 one of the files where I already knew there were issues with us not having full coverage of IR changes via InstModCallbacks. I went through and fixed it in all of the appropriate places.
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.
Ok, but who needs to know that borrowingOperand changed as opposed to innerBorrow?
@@ -752,7 +752,7 @@ swift::replaceAllUsesAndErase(SingleValueInstruction *svi, SILValue newValue, | |||
callbacks.deleteInst(user); | |||
continue; | |||
} | |||
use->set(newValue); | |||
callbacks.setUseValue(use, newValue); |
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 one of the files where I already knew there were issues with us not having full coverage of IR changes via InstModCallbacks. I went through and fixed it in all of the appropriate places to use callbacks.setUseValue();
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 obvious fix here would just have been to call the rauw callback, which we'll still need to do anyway.
@swift-ci smoke test linux platform |
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 goes against our efforts at IR design where we've gone to great lengths to make operand setting (and iteration) as fast as possible. There are two reasons I've never added a setUse hook in any IR I've worked on
-
The nature of SSA means that any optimization is at least quadratic
and usually cubic in the number of operand updates. -
All SSA algorithms I'm aware of only need to know when an SSA value
has new uses, not when each individual operand changes.
It's not clear why you need the setUseValue hook. AFAICT we only need a notifyHasNewUsers.
It's awesome that these hooks aren't indirect calls when they're not used. But that doesn't change the fact that this new hook should never be used and isn't needed. People will reach for it without realizing how disastrous it is rather than designing their code to use notifyHasNewUsers instead.
[this](SingleValueInstruction *i, SILValue value) { | ||
eraseAndRAUWSingleValueInstruction(i, value); | ||
[this](Operand *use, SILValue newValue) { | ||
use->set(newValue); |
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.
A single RAUW could replace thousands of operands. We design IR's very carefully so operand setting can be as efficient as possible. We definitely don't want to do a hash lookup each time. There only needs to be a single worklist insertion per RAUW operation.
@@ -752,7 +752,7 @@ swift::replaceAllUsesAndErase(SingleValueInstruction *svi, SILValue newValue, | |||
callbacks.deleteInst(user); | |||
continue; | |||
} | |||
use->set(newValue); | |||
callbacks.setUseValue(use, newValue); |
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 obvious fix here would just have been to call the rauw callback, which we'll still need to do anyway.
worklist.replaceValueUsesWith(oldValue, newValue); | ||
[this](Operand *use, SILValue newValue) { | ||
use->set(newValue); | ||
worklist.add(use->getUser()); |
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 should not be doing a hash lookup on every operand update!
@@ -434,7 +434,7 @@ void OwnershipRAUWUtility::eliminateReborrowsOfRecursiveBorrows( | |||
|
|||
// Then set our borrowing operand to take our innerBorrow instead of value | |||
// (whose lifetime we just ended). | |||
borrowingOperand->set(innerBorrow); | |||
callbacks.setUseValue(*borrowingOperand, innerBorrow); |
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.
Ok, but who needs to know that borrowingOperand changed as opposed to innerBorrow?
@gottesmm post-commit review |
To be a little more helpful, you could have a |
NOTE: This PR contains two changes. The first commit (4600aa7) is the meat and I included the commit message below. The other commit is just follow up changes to #35253 the predecessor PR of this PR.
[sil-inst-opt] Change InstModCallbacks to specify a setUseValue callback instead of RAUW callbacks.
This allows for me to do a couple of things improving quality/correctness/ease of use:
I reimplemented InstMod's RAUW and RAUW/erase helpers on top of
setUseValue/deleteInst. Beyond allowing the caller to specify less things, we
gain an orthogonality preventing bugs like overriding erase/RAUW but not
overriding erase or having the erase used in erase/RAUW act differently than
the erase for deleteInst.
There were a bunch of places using InstModCallback that also were setting
uses without having the ability for InstModCallbacks perform it (since it
only supported RAUW). This is an anti-pattern and could cause subtle bugs to
be introduced by appropriate state in the caller not being updated.