8278413: C2 crash when allocating array of size too large #30
Conversation
👋 Welcome back roland! A progress list of the required criteria for merging this PR into |
Have you run any micros to verify that nothing regresses? |
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 general I really like the change. I simplifies things nicely.
} else { | ||
Node* valid_length_test = call->in(AllocateNode::ValidLengthTest); | ||
const Type* valid_length_test_t = phase->type(valid_length_test); | ||
if (valid_length_test_t->isa_int() && valid_length_test_t->is_int()->is_con(0)) { |
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.
Here you do:
Node* valid_length_test = call->in(AllocateNode::ValidLengthTest);
const Type* valid_length_test_t = phase->type(valid_length_test);
if (valid_length_test_t->isa_int() && valid_length_test_t->is_int()->is_con(0)) {
But in compile.cpp:3766 you do:
Node* valid_length_test = call->in(call->req());
call->rm_prec(call->req());
if (valid_length_test->find_int_con(1) == 0) {
Why "call->req()" and not "call->in(AllocateNode::ValidLengthTest)"?
And why not "if (valid_length_test->find_int_con(1) == 0) {" in both places?
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.
Here you do:
Node* valid_length_test = call->in(AllocateNode::ValidLengthTest); const Type* valid_length_test_t = phase->type(valid_length_test); if (valid_length_test_t->isa_int() && valid_length_test_t->is_int()->is_con(0)) {
But in compile.cpp:3766 you do:
Node* valid_length_test = call->in(call->req()); call->rm_prec(call->req()); if (valid_length_test->find_int_con(1) == 0) {
Why "call->req()" and not "call->in(AllocateNode::ValidLengthTest)"?
call->in(AllocateNode::ValidLengthTest) only works for allocateArrayNode. The code I modified in compile.cpp runs after macro expansion so there's no AllocateArrayNode anymore. Instead there's a call to the runtime. When the AllocateArrayNode is macro expanded I move in(AllocateNode::ValidLengthTest) to the new runtime call as a precedence edge that is at req(). I tried adding it as an extra parameter that would be removed in compile.cpp but that messed up debug infos and I hit some asserts.
And why not "if (valid_length_test->find_int_con(1) == 0) {" in both places?
So`that the Value() call does the right thing during CCP where a node can be registered as a constant but not transformed yet to a ConNode.
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.
That's make sense. Thanks for explaining!
Thanks for looking at this. This fixes a crash for a corner case (array size close to max_jint). I don't think it can affect performance so I haven't run any performance testing. |
Passes testing tier1-3 |
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!
@rwestrel 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 17 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. Thank you for fixing it.
/integrate |
Going to push as commit deaf75a.
Your commit was automatically rebased without conflicts. |
On the fallthrough path from an AllocateArray, the length of the
allocated array is casted (with a CastII) to [0, max_size] with
max_size some number that depends on the array type and can be less
than max_jint.
Allocating an array of a length that's not in [0, max_size] causes the
CastII to become top. The fallthrough path must be killed as well in
that case otherwise this can lead to a broken graph. Currently c2 has
logic to protect against an allocation of array of negative size in
AllocateArrayNode::Ideal(). That call replaces the fallthrough path
with an Halt node. But if the size is too big, then the fallthrough
path is left as is.
This patch fixes that issues. It also reworks the length negative
case. I added a Bool/CmpU input to the AllocateArray that tests for a
valid length. If that input becomes false, CatchNode::Value() kills
the fallthrough path. That logic is similar to that for a virtual call
with a null receiver. I also removed AllocateArrayNode::Ideal() now
that CatchNode::Value() takes care of the same corner case. The code
in AllocateArrayNode::Ideal() was added by Vladimir and he told me he
tried extending CatchNode::Value() at the time but that caused test
failures. I had no issues in my testing so I assume doing it that way
is ok now.
The new input to AllocateArray is moved to the CallStaticJava runtime
call for array allocation on macro expansion as a precedence edge. The
reason for that is that final graph reshape needs a way to tell
whether the missing path out of the allocation is legal or not. final
graph reshape then removes the then useless precedence edge.
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk18 pull/30/head:pull/30
$ git checkout pull/30
Update a local copy of the PR:
$ git checkout pull/30
$ git pull https://git.openjdk.java.net/jdk18 pull/30/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 30
View PR using the GUI difftool:
$ git pr show -t 30
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk18/pull/30.diff