-
Notifications
You must be signed in to change notification settings - Fork 5.4k
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
8201516: DebugNonSafepoints generates incorrect information #12806
Conversation
👋 Welcome back thartmann! A progress list of the required criteria for merging this PR into |
@TobiHartmann The following label will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command. |
Webrevs
|
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 have few comments.
src/hotspot/share/opto/phaseX.cpp
Outdated
if (old_node_note_array != NULL) { | ||
C->set_node_note_array(new (C->comp_arena()) GrowableArray<Node_Notes*> (C->comp_arena(), 8, 0, NULL)); |
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.
Use nullptr
in these lines.
Can you use _useful.size()
as initial array length?
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.
Thanks for the review, Vladimir. I updated the NULL
usages.
Can you use _useful.size() as initial array length?
The node_note_array
uses buckets/blocks of size C->_node_notes_block_size == 256
. So the actual required size would be ceil((double)useful.size() / (double)256)
but we could simply use 1 + (useful.size() / 256)
.
Now even when initializing the GrowableArray to that size, setting notes will then still call Compile::grow_node_notes
multiple times to create the Node_Notes
buckets and since it always at least doubles the backing array size, we actually end up with an array that is larger than what is required:
jdk/src/hotspot/share/opto/compile.cpp
Lines 1233 to 1238 in 4619e8b
if (grow_by < num_blocks) grow_by = num_blocks; | |
int num_notes = grow_by * _node_notes_block_size; | |
Node_Notes* notes = NEW_ARENA_ARRAY(node_arena(), Node_Notes, num_notes); | |
Copy::zero_to_bytes(notes, num_notes * sizeof(Node_Notes)); | |
while (num_notes > 0) { | |
arr->append(notes); |
I updated the code to properly pre-size the structure by calling grow_node_notes
. This also has the advantage that the Node_Notes
arena is one big chunk instead of incrementally allocating small ones.
What do you think?
src/hotspot/share/opto/phaseX.cpp
Outdated
@@ -484,6 +489,11 @@ PhaseRenumberLive::PhaseRenumberLive(PhaseGVN* gvn, | |||
assert(_old2new_map.at(n->_idx) == -1, "already seen"); | |||
_old2new_map.at_put(n->_idx, current_idx); | |||
|
|||
if (old_node_note_array != NULL) { |
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.
nullptr
@@ -469,6 +469,13 @@ PhaseRenumberLive::PhaseRenumberLive(PhaseGVN* gvn, | |||
|
|||
uint worklist_size = worklist->size(); | |||
|
|||
GrowableArray<Node_Notes*>* old_node_note_array = C->node_note_array(); | |||
if (old_node_note_array != nullptr) { | |||
int new_size = (_useful.size() >> 8) + 1; // The node note array uses blocks, see C->_log2_node_notes_block_size |
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 should call new_size = MAX2(8, new_size)
to make sure that we have at least 8 elements for initial allocation.
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.
Okay, I added that. The 8 seems arbitrary to me but since we already use that for initial allocation of the array, we can as well be consistent here. Just note that since we are calling C->grow_node_notes
, we will also initialize with Node_Notes*
right away.
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.
Why don't we just use C->_log2_node_notes_block_size
directly in (_useful.size() >> 8)?
I don't understand why we have to add MAX2(8, new_size) either. It looks like c2 doesn't want to have node-level accuracy. It drops the lowest 8bits of node_idx as block_id. I think the minimal number of "block" is 1, or arr is NULL.
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.
Why don't we just use C->_log2_node_notes_block_size directly in (_useful.size() >> 8)?
Because it's private in Compile
. We could make it public but I thought it's not worth it.
I don't understand why we have to add MAX2(8, new_size) either. It looks like c2 doesn't want to have node-level accuracy. It drops the lowest 8bits of node_idx as block_id. I think the minimal number of "block" is 1, or arr is NULL.
I think you are misinterpreting the code in Compile::locate_node_notes
. It first determines the block_idx
by idx >> _log2_node_notes_block_size
and then the position in that block by idx & (_node_notes_block_size-1)
.
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.
Thanks for the clarification. yes, I understanding was wrong.
A block is an array of 256 Node_Note. Is it a particular reason that Compile needs at least 8 blocks? It can grow automatically anyway.
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 a particular reason that Compile needs at least 8 blocks?
It's the value that is used when creating the array:
jdk/src/hotspot/share/opto/compile.cpp
Lines 1060 to 1062 in 4619e8b
set_node_note_array(new(comp_arena()) GrowableArray<Node_Notes*> | |
(comp_arena(), 8, 0, NULL)); | |
set_default_node_notes(Node_Notes::make(this)); |
I don't think there is a particular reason for 8 but it's just one of those more or less reasonable default values/sizes that we use all over the place when creating containers.
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.
Good
@TobiHartmann This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 100 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. ➡️ To integrate this PR with the above commit message to the |
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.
Looks good to me.
Thanks for the review, Vladimir and Roland! |
/integrate |
Going to push as commit 94eda53.
Your commit was automatically rebased without conflicts. |
@TobiHartmann Pushed as commit 94eda53. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
/backport jdk17u-dev |
@apangin the backport was successfully created on the branch apangin-backport-94eda53d in my personal fork of openjdk/jdk17u-dev. To create a pull request with this backport targeting openjdk/jdk17u-dev:master, just click the following link: The title of the pull request is automatically filled in correctly and below you find a suggestion for the pull request body:
If you need to update the source branch of the pull then run the following commands in a local clone of your personal fork of openjdk/jdk17u-dev:
|
C2 emits incorrect debug information when diagnostic
-XX:+DebugNonSafepoints
is enabled. The problem is that renumbering of live nodes (-XX:+RenumberLiveNodes
) introduced by JDK-8129847 in JDK 8u92 / JDK 9 does not update the_node_note_array
side table that links IR node indices to debug information. As a result, after node indices are updated, they point to unrelated debug information.The reproducer shared by the original reporter @jodzga (@jbachorik also reported this issue separately) does not work anymore with recent JDK versions but with a slight adjustment to trigger node renumbering, I could reproduce the wrong JFR method profile:
It suggests that the hottest method of the Test is not the long running loop in Test::arraycopy but several other short running methods. The hot method is not even in the profile. This is obviously wrong.
With the fix, or when running with
-XX:-RenumberLiveNodes
as a workaround, the correct profile looks like this:With the help of the IR framework, it's easy to create a simple regression test (see
testRenumberLiveNodes
).The fix is to create a new
node_note_array
and copy the debug information to the right index after updating node indices. We do the same in the matcher:jdk/src/hotspot/share/opto/matcher.cpp
Lines 337 to 342 in c1e77e0
Another problem is that
Parse::Parse
callsC->set_default_node_notes(caller_nn)
beforedo_exits
, which resets theJVMState
to the caller state. We then set the bci toInvocationEntryBci
in the callerJVMState
. Any new node that is emitted indo_exits
, for example aMemBarRelease
, will have thatJVMState
attached andNonSafepointEmitter::observe_instruction
->DebugInformationRecorder::describe_scope
will then use that information when emitting debug info. The resulting debug info is misleading because it suggests that we are at the beginning of the caller method. The teststestFinalFieldInit
andtestSynchronized
reproduce that scenario.The fix is to move
set_default_node_notes
down to afterdo_exits
.I find it also misleading that we often emit "synchronization entry" for
InvocationEntryBci
at method entry/exit in the debug info, although there is no synchronization happening. I filed JDK-8303451 to fix that.Thanks,
Tobias
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk pull/12806/head:pull/12806
$ git checkout pull/12806
Update a local copy of the PR:
$ git checkout pull/12806
$ git pull https://git.openjdk.org/jdk pull/12806/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 12806
View PR using the GUI difftool:
$ git pr show -t 12806
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/12806.diff