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
8263361: Incorrect arraycopy stub selected by C2 for SATB collectors #3008
Conversation
|
/label add hotspot-compiler |
@neliasso |
@neliasso |
Webrevs
|
@@ -661,10 +661,13 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* | |||
MergeMemNode* local_mem = MergeMemNode::make(mem); | |||
transform_later(local_mem); | |||
|
|||
// A tightly coupled arraycopy of reference types might touch uninitialized memory | |||
// which will make cardbarriers fail. | |||
bool may_be_uninitialized = dest_uninitialized || (ac->is_alloc_tightly_coupled() && !ReduceInitialCardMarks && is_reference_type(basic_elem_type)); |
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 wonder if you can just adjust the way that dest_uninitialized is computed way above to unconditionally check if ac->is_alloc_tightly_coupled(), so that the value of dest_uninitialized makes sense in all cases.
Part of the reason I'm asking is that the embedded information about !ReduceInitialCardMarks really belongs to the GC, and is utilized by some but not all GCs, sometimes in not trivial ways, in BarrierSetC2::array_copy_requires_gc_barriers(), which I'm guessing is what you are referring to here. So in a way we rely on the implementation details of BarrierSetC2::array_copy_requires_gc_barriers() to compute whether we need to correctly annotate the arraycopy stub or not, regarding whether the destination is uninitialized or not. It would be nice if it was always annotated correctly, regardless of whether correct annotation is required by the GC implementation backend or not. By unconditionally performing a correct computation of dest_uninitialized, it seems like we would solve that. Or are there non-obvious reasons why that can not be done?
I guess what I'm proposing is to modify the condition where dest_uninitialized is computed, around where this hilarious comment is: "You break it, you buy it."
In fact, reading the comment just above that, it is kind of hinting at how something like is_alloc_tightly_coupled() had been used there before. It's saying that "Note: Because tightly_coupled_allocation performs checks on the out-edges of the dest, we need to avoid making derived pointers from it until we have checked its uses.", however there is no such check to be found there, which seems like the cause of the bug.
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.
There is a bit of a conceptual disconnect in the code. In generate_arraycopy dest_uninitialized is set only when the allocation isn't already "complete" - that is we haven't already been able to prove that no zeroing is neccessary. Clones will for example create complete allocations from the beginning - but they aren't initialized.
The code that is guarded by dest_uninitialized will add zeroing for the array allocation that is outside the copy range. The part that is inside the copy range will still not be initialized. (But that is ok - since dest_uninitialized is set).
The problem here is that there are acopies that are proven to be complete from the beginning (like clones) that will copy uninitialized memory - and they don't need anything of the zeroing code that is guarded by dest_uninitialized.
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.
My plan is to rename dest_uninitialized to dest_needs_zeroing and add a separate variable for when the acopy is done to uninitilialized memory.
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.
Sounds like a good plan!
Node* alloc = tightly_coupled_allocation(alloc_obj, NULL); | ||
ArrayCopyNode* ac = ArrayCopyNode::make(this, true, obj, intcon(0), alloc_obj, intcon(0), obj_length, alloc != NULL, false); | ||
// Clones are always tightly coupled. | ||
ArrayCopyNode* ac = ArrayCopyNode::make(this, true, obj, intcon(0), alloc_obj, intcon(0), obj_length, true, false); |
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 also fix a bug in library_call kit where arraycopy for clones doesn't get marked as tightly coupled - The call to tightly_coupled_allocation can't be used when the IR isn't constructed yet.
Can you elaborate, please, on the problem with tightly_coupled_allocation()
?
If it can't be relied upon during parsing, then all other usages in LibraryCallKit
have the same problem, don't they?
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 - they might all be wrong - but it depends. In this particular case - tightly_coupled_allocation tries to look at the false path too see if it is a uncommon_trap - which is the only allowed control flow. But that part hasn't been expanded yet - so it is only a safepoint holding the parsing state.
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.
Ok. Please, file a bug to inspect other usages of LibraryCallKit::tightly_coupled_allocation()
.
@neliasso This change now passes all automated pre-integration checks. 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 47 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.
|
/integrate |
@neliasso Since your change was applied there have been 53 commits pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit 5d87a21. |
This bug has only been reproduced with -XX:+StressReflectiveCode and -XX:-ReduceInitialCardMarks. If StressReflectiveCode is necessary isn't obvious.
I am fixing three things:
The generate_arraycopy method in PhaseMacroExpand have a bool dest_uninitialized field - but that field denotes that we have proven that no zeroing is necessary (since all fields will be written anyway).
To fix this bug generate_unchecked_arraycopy with the param dest_uninitialized will need to be appended with a check for tighly coupled object-copies when ReduceInitialCardMarks is disabled.
I also fix a bug in library_call kit where arraycopy for clones doesn't get marked as tightly coupled - The call to tightly_coupled_allocation can't be used when the IR isn't constructed yet.
In stubroutines.cpp I fix so that the name of the arraycopy stubroutines gets appended with _uninit when appropriate. This shows up in ir-dumps and IGV.
Please review,
Nils Eliasson
Progress
Issue
Reviewers
Download
To checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/3008/head:pull/3008
$ git checkout pull/3008
To update a local copy of the PR:
$ git checkout pull/3008
$ git pull https://git.openjdk.java.net/jdk pull/3008/head