JIT: Transform '(cmp & x) | (cmp & y)' to 'cmp & (x | y)'#126070
JIT: Transform '(cmp & x) | (cmp & y)' to 'cmp & (x | y)'#126070BoyBaykiller wants to merge 3 commits intodotnet:mainfrom
Conversation
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
|
I am having a problem identifying certain cases. Which works fine for When adding explicit parentheses like But this shouldn't be needed. It shouldnt get confused by some extra commutative arithmetic like the ADD(1) here. What is the general way to fix this? |
The operator priority of |
src/coreclr/jit/morph.cpp
Outdated
| // Fold "(cmp & x) | (cmp & y)" to "cmp & (x | y)". | ||
| if (varTypeIsIntegralOrI(orOp) && op1->OperIs(GT_AND) && op2->OperIs(GT_AND)) | ||
| { | ||
| if (GenTree::Compare(op1->gtGetOp1(), op2->gtGetOp1())) |
There was a problem hiding this comment.
I don't think this is legal - cmp may have side-effect or be a local whose value is changed via x or y
There was a problem hiding this comment.
can you give am example?
There was a problem hiding this comment.
can you give am example?
of a side effect? just a tree that increments a field. it does it twice, you remove one of them.
There was a problem hiding this comment.
I copied a check from fgRecognizeAndMorphBitwiseRotation that should make it safe:
if (((tree->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) != 0) || ((tree->gtFlags & GTF_ORDER_SIDEEFF) != 0))
{
// We can't do anything if the tree has stores, calls, or volatile reads. Note that we allow
// GTF_EXCEPT side effect since any exceptions thrown by the original tree will be thrown by
// the transformed tree as well.
return nullptr;
}This means these trees are no longer transformed which is correct:
int BugPersSideEffects(int flags)
{
int res = (Consume(flags) & 256) | (Consume(flags) & 512);
return res;
}
int BugOrderSideeff(ref int flags)
{
Consume(flags);
return (Volatile.Read(ref flags) & 256) | (Volatile.Read(ref flags) & 512);
}Interestingly, I've run superpmi and there wasn't a single case where this fires.
There was a problem hiding this comment.
Interestingly, I've run superpmi and there wasn't a single case where this fires.
🤷 the fact that your current PR finds diffs implies there are such patterns, but we just can't do it in morph.
There was a problem hiding this comment.
Adding the PERSISTENT_SIDE_EFFECTS/ORDER_SIDEEFF correctnes check doesnt result in any additional diffs compared to not having this correctnes check. I've tested this by putting assert(false) in it and running replay.
There was a problem hiding this comment.
Adding the
PERSISTENT_SIDE_EFFECTS/ORDER_SIDEEFFcorrectnes check doesnt result in any additional diffs compared to not having this correctnes check. I've tested this by puttingassert(false)in it and running replay.
Ah, I misunderstood you. Well, you still need to add the checks
There was a problem hiding this comment.
Yeah of course : )
However the check in it's current form miss some useful cases. For example:
int TransformOnInd(ref int flags)
{
Consume(flags); // null check
int res = (flags & 256) | (flags & 512);
return res;
}Here we bail because of GTF_ORDER_SIDEEFF != 0 check. Am I supposed to use a different flag? What I want to know is "can this IND be removed (no null check attached and not volatile)?".
If I knew how to do that then I could also handle this:
int TransformOnInd2(ref int flags)
{
int res = (flags & 256) | (flags & 512);
return res;
}The first load can't be removed because it's also the nullcheck if I understand correctly. But the second still can. So the transformation can still be done. For this case there is the additional problem that GenTree::Compare returns false when the IND flags aren't the same.
@huoyaoyuan Bad example from me. Say we have |
* add check for GTF_PERSISTENT_SIDE_EFFECTS so we dont remove a tree when we shouldnt (volatile check still missing)
|
I will close this for now as I've encountered 2 general existing issues:
Fixing these would help existing transformations too. @EgorBo |
No description provided.