-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
JIT: Relax reg-optionality validity checks #81614
Conversation
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch, @kunalspathak Issue Detailsnull
|
e97e020
to
8df13a7
Compare
/azp run Fuzzlyn |
Azure Pipelines successfully started running 1 pipeline(s). |
/azp run Fuzzlyn |
Azure Pipelines successfully started running 1 pipeline(s). |
This will conflict with #81267 so will wait for that one to get in first. It should still be ready for review though, cc @dotnet/jit-contrib PTAL @BruceForstall @kunalspathak |
// | ||
bool Lowering::IsSafeToMarkRegOptional(GenTree* parentNode, GenTree* childNode) const | ||
{ | ||
if (!childNode->OperIs(GT_LCL_VAR)) |
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 a rather subtle difference between LCL_VAR
and LCL_FLD
that LCL_FLD
s, if marked reg optional (which they won't be under normal circumstances), will use spill temps. Something that would be good to write down explicitly I think.
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 can add it with the "indirection" example above? I suppose it's the exact same example.
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.
Yes, just mentioning it would be good.
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.
LGTM
src/coreclr/jit/lower.cpp
Outdated
// true if it is safe to make childNode a contained memory operand. | ||
// Returns: | ||
// True if 'node' can be evaluated at any point between its current | ||
// location and 'parentNode' without giving a different result; otherwise |
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.
// location and 'parentNode' without giving a different result; otherwise | |
// location and 'endExclusive' without giving a different result; otherwise |
The Fuzzlyn failure was #75442. |
/azp run runtime-coreclr jitstress, runtime-coreclr libraries-jitstress, Fuzzlyn, runtime-coreclr jitstressregs, runtime-coreclr libraries-jitstressregs |
Azure Pipelines successfully started running 5 pipeline(s). |
{ | ||
assert((node != nullptr) && (endExclusive != nullptr)); | ||
|
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.
Is there any invariant case we should handle here? That is, the prior handles the linear order being node, endExclusive
. Should this handle node, ignoreNode, endExclusive
?
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'll add an additional assert about this
// interfere is due to it being address exposed. So this is the only unsafe | ||
// case. | ||
// | ||
bool Lowering::IsSafeToMarkRegOptional(GenTree* parentNode, GenTree* childNode) 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.
Is any of this optimization that should also apply to IsSafeToContainMem
?
That is, iirc there is a "pessimization" for the InterferesWith
check today with regards to locals
in that any write is considered interfering, even if its to another local and therefore cannot impact the local being read.
However, based on https://github.com/dotnet/runtime/blob/main/docs/design/specs/Memory-model.md I believe we are allowed to reorder such a non-volatile read of a local assuming there are no volatile writes.
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 what I'm remembering is just the fact that we couldn't mark it reg-optional and the fix is exactly what is being handled here, not something to the general IsSafeToContainMem
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 think most of this applies to IsSafeToContainMem
, the problem there is really that it causes evaluation to happen at a new point in time, which brings along with it all of the extra checking. W.r.t the locals, from looking at AliasSet::InterferesWith
it looks like we are pretty precise:
runtime/src/coreclr/jit/sideeffects.cpp
Lines 319 to 335 in 917f407
//------------------------------------------------------------------------ | |
// AliasSet::InterferesWith: | |
// Returns true if the reads and writes in this alias set interfere | |
// with the given alias set. | |
// | |
// Two alias sets interfere under any of the following conditions: | |
// - Both sets write to any addressable location (e.g. the heap, | |
// address-exposed locals) | |
// - One set reads any addressable location and the other set writes | |
// any addressable location | |
// - Both sets write to the same lclVar | |
// - One set writes to a lclVar that is read by the other set | |
// | |
// Arguments: | |
// other - The other alias set. | |
// | |
bool AliasSet::InterferesWith(const AliasSet& other) 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.
@SingleAccretion managed to find the discussion we had it some time back on Discord
The general scenario I was thinking about is: #76273 (comment)
Many diffs are cases like the following:
- add x0, x0, w26, UXTW - ldrb w0, [x0] + ldrb w0, [x0, w26, UXTW #2]These seem to mostly be due to
ADDEX
not being properly handled in many places whileADD
always is.
There are a few small regressions such as:
+ lsl w0, w0, #8 ldr x1, [fp, #0xA8] // [V192 tmp170] ldrb w1, [x1, #0x01] - add w1, w1, w0, LSL #8 + add w1, w0, w1Notably we're checking if
childNode->gtGetOp1()
(the value to be shifted) is contained, the original doesn't (but it shouldn't be anyways since we need a register for it).We're also checking
IsSafeToContainMem(parentNode, childNode)
where-as the original wasn't. I expect this is the "actual" reason for the "larger diff" since we always do "strict" checking and we don't allow reordering across anything with a "side effect". That being said, there is no actual interdependence between these two and so it is technically "safe" anyways.
The jitstress/jitstressregs failures are all present in the previous rolling run too. I think there's something wrong with arm64-OSX exception handling in general on current main, I'm seeing a lot of Fuzzlyn failures too. According to those the runtime is crashing after printing "Unhandled exception", just like those failures in jitstress/jitstressregs. (Note that Fuzzlyn catches all exceptions, so this is unexpected.) Edit: the symptom is the same as in #81869 |
outerloop commit range between last success and first failure: f01d5a0...215839e |
Sigh, I guess I know what's wrong. My change to prevent unwinding through the bottom of the stack on Alpine is incorrect for secondary threads. |
/azp run Fuzzlyn |
Azure Pipelines successfully started running 1 pipeline(s). |
I can reproduce the problematic codegen in the macOS fuzzlyn example on main too. Will open an issue and investigate that separately. |
This PR does two things:
IsSafeToContainMem
intoIsInvariantInRange
, and useIsInvariantInRange
in the places where we are usingIsSafeToContainMem
today as a general-purpose "can move" check.IsSafeToContainMem
remains and just forwards toIsInvariantInRange
. We still use it for the cases where we're actually containing a load. This is follow-up to Add lowering support for conditional nodes #71705 (comment) that I promised a long time ago.IsSafeToMarkRegOptional
about the checking we actually need to do: