Skip to content
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

Ensure simplifier keeps required DIVCHK operations #7046

Conversation

hzongaro
Copy link
Contributor

@hzongaro hzongaro commented Jun 27, 2023

The divchkSimplifier was first simplifying its child node and then checking whether the DIVCHK is still needed after having simplified its child. It did that by checking whether the simplification of its child results in a node that is not the original child, or the simplified child is not a division or remainder operation.

However, it might be that the simplified child is still a division or remainder operation that needs to be checked for division by zero, but is not the original child node. The existing logic leads divchkSimplifier to replace the DIVCHK with a treetop operation incorrectly.

This change revises the division and remainder simplifiers so that they only directly replace the child of the parent DIVCHK (if any) with a different division or remainder node that must still be checked for division by zero.

This pull request also contains a minor change to a trace message in OMR::Optimization::anchorChildren.

Fixes: eclipse-openj9/openj9#17066

@hzongaro
Copy link
Contributor Author

Devin @jdmpapin, may I ask you to review this change?

I have to admit that I'm a little bit uncomfortable with the handshake that exists between the DIVCHK simplifier and the ldiv and lrem simplifiers, but I didn't see another obvious way of handling this situation. I'm open to suggestions for cleaner approaches to dealing with this problem.

@jdmpapin
Copy link
Contributor

jdmpapin commented Jun 28, 2023

In terms of the handshake, I think it would be better to be more explicit. (edit: I mean in the code itself. Your comments are very explicit already!) Here's an idea:

  1. Add a field to Simplifier to be this side-channel, e.g. TR::Node *_newDivCheckNode;
  2. Have the DIVCHK simplifier handler check to see whether the child has been simplified yet. If so, then it was evaluated above, so eliminate the DIVCHK
  3. If the child hasn't been simplified yet in this pass but is somehow already not a div/mod anyway, eliminate the DIVCHK - I'm not sure if this is possible, but it seems prudent to check for it
  4. Otherwise, simplify the child like we currently do: s->simplify(child, block)
  5. Make the *div and *rem handlers set the _newDivCheckNode, but only after recursively simplifying their own children, and make them stop mutating the parent DIVCHK directly
  6. The child of DIVCHK will therefore be the last to set _newDivCheckNode, so the DIVCHK handler can consult it

I imagine it would be treated something like the following: the DIVCHK handler ignores the result of simplify() and uses _newDivCheckNode instead. If it's null, eliminate the DIVCHK. Otherwise, replace the child. (It could just be the existing child, in which case the replacement does nothing.) I think it would be pretty easy to understand what it means to set _newDivCheckNode to a particular value, though it would certainly take a comment in the DIVCHK handler to explain why it is that we know that _newDivCheckNode has been set while simplifying the child rather than a more distant descendant or some other node from a prior tree

What do you think?

@hzongaro
Copy link
Contributor Author

What do you think?

Thanks for the suggestion, Devin @jdmpapin! Let me play with that. I'll move this pull request back to draft status in the meanwhile.

@hzongaro hzongaro marked this pull request as draft June 28, 2023 12:40
@hzongaro hzongaro force-pushed the DIVCHK-might-be-needed-for-simplified-child branch 2 times, most recently from f4cc9fe to 3c7f62f Compare August 20, 2023 01:21
@hzongaro hzongaro marked this pull request as ready for review August 20, 2023 01:32
@hzongaro
Copy link
Contributor Author

Devin @jdmpapin, as you suggested I revised the changes to have the {l|i}{rem|div}Simplifiers use a field — _nodeToDivchk — in OMR::Simplifier to communicate to divchkSimplifier whether there is a node that still needs to have a DIVCHK applied to it following any simplification that might have happened. May I ask you to review this change again?

@hzongaro
Copy link
Contributor Author

Devin @jdmpapin, when you have the opportunity, may I ask you to review the revised changes for this pull request?

Copy link
Contributor

@jdmpapin jdmpapin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My biggest suggestion is the comment I left in idivSimplifier(), which I think would significantly simplify the logic and perhaps also eliminate the need to change fold*Constant() to indicate success. My only reservation is that by not bothering to precisely distinguish the cases where eliminating DIVCHK is optional vs. mandatory, we'd lose the ability to performTransformation() in the optional cases. But I'm not sure how concerned to be about that, and if we decide it's important, then I think we could do an earlier performTransformation() that covers more logic, e.g. something along the lines of

if (divisor != 0 && performTransformation(...))

compiler/optimizer/OMRSimplifier.hpp Outdated Show resolved Hide resolved
compiler/optimizer/OMRSimplifierHandlers.cpp Outdated Show resolved Hide resolved
compiler/optimizer/OMRSimplifierHandlers.cpp Outdated Show resolved Hide resolved
compiler/optimizer/OMRSimplifierHandlers.cpp Show resolved Hide resolved
compiler/optimizer/OMRSimplifierHandlers.cpp Outdated Show resolved Hide resolved
compiler/optimizer/OMRSimplifierHandlers.cpp Outdated Show resolved Hide resolved
compiler/optimizer/OMRSimplifierHandlers.cpp Outdated Show resolved Hide resolved
compiler/optimizer/OMRSimplifierHandlers.cpp Outdated Show resolved Hide resolved
@hzongaro
Copy link
Contributor Author

Thanks for your review, Devin @jdmpapin. I took your suggestion and performed an early call to performTransformation in the various simplifiers to control whether to set _nodeToDivchk to NULL, effectively controlling whether the DIVCHK can be removed and whether its child can be simplified. With that, I was able to revert the changes to have the various fold*Constant methods return a boolean value, as you had observed.

Once you're OK with these changes, I will squash commits 37c05e2 and 7ebe636.

The one comment I've left open is on BINARY_IDENTITY_OP.

May I ask you to re-review?

Copy link
Contributor

@jdmpapin jdmpapin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update! I have a few more comments

compiler/optimizer/OMRSimplifierHandlers.cpp Outdated Show resolved Hide resolved
compiler/optimizer/OMRSimplifierHandlers.cpp Show resolved Hide resolved
compiler/optimizer/OMRSimplifierHandlers.cpp Outdated Show resolved Hide resolved
compiler/optimizer/OMRSimplifierHandlers.cpp Show resolved Hide resolved
@hzongaro
Copy link
Contributor Author

hzongaro commented Nov 1, 2023

I agree it would be better for BINARY_IDENTITY_OP() to become a function. . . .

Whether we change it to a function or leave it as-is, it might be better to move the test for a constant firstChild to a nested if that guards just the constant folding, and the BINARY_IDENTITY_OP() part could move into the outer if

Yes, that would be better than bulking up the definition of BINARY_IDENTITY_OP(). I'll leave it as a macro for now, and I'll open a follow up issue to change it into a function.

@hzongaro
Copy link
Contributor Author

hzongaro commented Nov 9, 2023

Devin @jdmpapin, this change is ready for another review. Again, once approved, I will squash commits 04b2b82, 37c05e2 and 7ebe636.

Copy link
Contributor

@jdmpapin jdmpapin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the updates! LGTM, with one small oddity

compiler/optimizer/OMRSimplifierHandlers.cpp Show resolved Hide resolved
@jdmpapin
Copy link
Contributor

Awesome, good to squash!

The divchkSimplifier was first simplifying its child node and then
checking whether the DIVCHK is still needed after having simplified its
child.  It did that by checking whether the simplification of its child
results in a node that is not the original child, or the simplified
child is not a division or remainder operation.

However, it might be that the simplified child is still a division or
remainder operation that needs to be checked for division by zero, but
is not the original child node.  That leads divchkSimplifier to replace
the DIVCHK with a treetop operation incorrectly.

This change introduces a _nodeToDivchk field in OMR::Simplifier that the
integer division and remainder simplifiers use to indicate whether the
result of attempting the simplification results in a node that would
still need to be checked for division by zero if the original node
needed to be checked for division by zero.

In most cases that handling of the handshake in the integer division and
remainder simplifiers happens in a new function that's called if the
divisor is a non-zero constant, permitSimplificationOfConstantDivisor.
That function is responsible for checking that the parent of the
operation is a DIVCHK, and if so, that performTransformation allows the
DIVCHK to be removed.

The divchkSimplifier, after simplifying its child division or remainder
operation, checks _nodeToDivchk as a first step in determining whether
the DIVCHK is still needed.  If it is NULL, the DIVCHK is removed; if it
is non-NULL, that node is made the subject of the DIVCHK.

Also, moved references to BINARY_IDENTITY_OP in the integer division
and remainder simplifiers so that it's only used in situations where the
divisor is already known to be a constant, so the handshake with the
divchkSimplifier will already have been handled by those simplifiers.
That avoids having BINARY_IDENTITY_OP deal with the handshake itself.

This change also fixes bugs in some simplifiers where division by zero
could result from attempting to fold the result of an operation where
both operands are constant.
OMR::Optimization::anchorChildren sets hasCommonedAncestor to the result
of the comparison of the node's reference count and 1.  However, the
traceMsg it was emitting claimed that it was always setting
hasCommonedAncestor to true, even if the result of the comparison was
false.  Corrected this to reflect that actual result of the comparison.
@hzongaro hzongaro force-pushed the DIVCHK-might-be-needed-for-simplified-child branch from f3b666d to 707ac80 Compare November 10, 2023 20:36
@hzongaro
Copy link
Contributor Author

Thanks for your thorough reviews and the original suggestion on how to handle the handshake, Devin @jdmpapin. I've squashed the commits.

@jdmpapin
Copy link
Contributor

Jenkins build all

@hzongaro
Copy link
Contributor Author

I think the x86-64 macOS failure is known issue #6516.

@jdmpapin
Copy link
Contributor

All other tests have passed

@jdmpapin jdmpapin merged commit 989261b into eclipse-omr:master Nov 13, 2023
16 of 18 checks passed
@hzongaro hzongaro deleted the DIVCHK-might-be-needed-for-simplified-child branch November 13, 2023 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

JDK 8/11/17 crash when handling divided by zero from a long casted from int
2 participants