-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Draft for x ^ 0 opt (#65071) #65072
Draft for x ^ 0 opt (#65071) #65072
Conversation
Tagging subscribers to this area: @JulieLeeMSFT |
src/coreclr/jit/lowerxarch.cpp
Outdated
|
||
return op1; | ||
} | ||
} |
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.
Consider moving to morph, e.g. x*0 -> 0
:
runtime/src/coreclr/jit/morph.cpp
Lines 13723 to 13733 in fe24ab3
if (mult == 0) | |
{ | |
// We may be able to throw away op1 (unless it has side-effects) | |
if ((op1->gtFlags & GTF_SIDE_EFFECT) == 0) | |
{ | |
DEBUG_DESTROY_NODE(op1); | |
DEBUG_DESTROY_NODE(mul); | |
return op2; // Just return the "0" node | |
} |
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.
in theory you can also improve this x * 0
by not giving up on side effects and making a COMMA but it's up to you.
Just make sure you don't drop CSE candidates
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.
in theory you can also improve this
x * 0
by not giving up on side effects and making a COMMA but it's up to you. Just make sure you don't drop CSE candidates
Could you give me more details about this? I'm a very newbie in the project and a lot of obvious things are not that obvious for me yet😊
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.
Don't worry, none of those things were obvious, :)
Btw, we already have |
I took a look at the regressions. They are caused by missing CSEs of the following form: -CSE candidate #01, key=$40 in BB01, [cost= 3, size= 3]:
-N003 ( 3, 3) CSE #01 (use)[000087] ------------ * XOR int <l:$40, c:$483>
-N001 ( 1, 1) [000085] ------------ +--* LCL_VAR int V07 tmp3 u:2 (last use) <l:$40, c:$483>
-N002 ( 1, 1) [000761] ------------ \--* CNS_INT int 0 $40 Apparently those help the RA allocate more optimally (even as otherwise they are clearly not useful - the (Notably, not always - we have some improvements in these tests as well) Seeing as the diffs are in basically one test that has been duplicated via code generation, I personally think it would be ok to accept the regressions while leaving the optimization in morph. It should help CSE (and the rest of the frontend) make better decisions in other situations. |
// The optimized tree, currently always a local variable, in case any transformations | ||
// were performed. Otherwise, "nullptr", guaranteeing no state change. | ||
// | ||
GenTree* Compiler::fgOptimizeBitwiseXor(GenTreeOp* xorOp) |
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 could leave a TODO-Cleanup: move the optimizations from "fgMorphSmpOpOptional" to this method.
.
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 moved the xor related code into this method and noticed that the condition of this branch looks odd to me.
runtime/src/coreclr/jit/morph.cpp
Lines 13901 to 13909 in 3449312
else if (op2->IsIntegralConst(1) && op1->OperIsCompare()) | |
{ | |
/* "binaryVal ^ 1" is "!binaryVal" */ | |
gtReverseCond(op1); | |
DEBUG_DESTROY_NODE(op2); | |
DEBUG_DESTROY_NODE(xorOp); | |
return op1; | |
} |
Is it actually possible for a child node of XOR node to be a compare node? If I'm not wrong then it is a good candidate to be removed and extend the previous condition with
|| op2->IsIntegralConst(1)
since "x ^ -1 " = "~x" and "x ^ 1" = "~x". Correct me if I'm wrong :)
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 it actually possible for a child node of XOR node to be a compare node?
Why not?
ldarg 0
ldarg 1
ceq
ldc.i4 1
xor
Would produce a tree that this is looking for. Not sure how often this is hit in practice (you can remove it and see what SPMI tells, I would guess we'll see lots of such patterns in auto-generated IL in tests).
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.
Yeah, you are right. Removing the conditions emits 2 small regressions. What do you think of "x ^ 1" = "~x"? A little investigation gives me this diff
diff_summary.17.md
Doesn't look good, does 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.
What do you think of "x ^ 1" = "~x"?
Are you suggesting to apply it for any x
, not just compares? But that would not be correct, it only affects the lowest bit.
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.
All right. Thank you.
The code changes LGTM. However, it seems there are real regressions on 32 bit platforms, I took a look - they're all CSE-induced as well. It seems we're better of with CSEing a @dotnet/jit-contrib for opinions. |
@SkiFoD sorry for the delay, could you please rebase your PR against latest Main? so we can kick spmi once again |
@SkiFoD I'm really sorry, was distracted a bit - could you please resolve the conflict and tag me back so we can merge it? |
1491f41
to
c7cdc5c
Compare
@EgorBo I updated the branch and run SPMI once again. I got confussed by the results so I'd like to know your opinion on them. |
else if (op2->IsIntegralConst(1) && op1->OperIsCompare()) | ||
{ | ||
/* "binaryVal ^ 1" is "!binaryVal" */ | ||
gtReverseCond(op1); |
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 realize that it's not your code but it looks weird that it doesn't use return value
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 see it modifies the node if it's OperIsCompare
Looks good to me, thanks! let's wait for the last job to finish |
@SkiFoD thank you for the contribution! |
@SingleAccretion @EgorBo Thank you both for guiding me through. Learnt a lot, Pleasure to work with you. |
#65071