-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
JDK-8258603 c1 IR::verify is expensive #6850
Conversation
👋 Welcome back LudwikJaniuk! A progress list of the required criteria for merging this PR into |
@LudwikJaniuk 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 think it's a good idea to narrow the verification down to the blocks which are actually changed and also add some additional verification steps. Even with the newly added checks, it should still be better than running a complete check each time. Did you observe a big gain in the C1 compilation time for debug builds with your new patch? Maybe you also want to check again with the tests from openjdk/jdk16#44.
src/hotspot/share/c1/c1_IR.cpp
Outdated
} | ||
|
||
void IR::verify_local(BlockList& blocks) { | ||
#ifdef ASSERT |
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.
Do we need an ASSERT
here? The code is already guarded with ifndef PRODUCT
. Same for L1447.
src/hotspot/share/c1/c1_IR.cpp
Outdated
BlockBegin* block = blocks.at(h); | ||
|
||
for (int i = 0; i < block->end()->number_of_sux(); i++) { | ||
if (blocks.contains(block->end()->sux_at(i))) continue; |
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.
To avoid confusion, I think it's better to always use braces for one-line ifs and loops instead.
src/hotspot/share/c1/c1_IR.cpp
Outdated
@@ -1359,24 +1370,95 @@ class PredecessorValidator : public BlockClosure { | |||
} | |||
}; | |||
|
|||
class VerifyBlockBeginField : public BlockClosure { | |||
inline void verify_block_begin_field(BlockBegin* block) { | |||
for ( Instruction *cur = block; cur != NULL; cur = cur->next()) { |
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.
Same here, could this method also be directly inlined into block_do
below? Spacing and asterisk position can be improved here.
src/hotspot/share/c1/c1_IR.cpp
Outdated
}; | ||
|
||
// Validation goals: | ||
// * code() length == blocks 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.
Just a minor thing, might be better to use -
for the itemization instead of *
as it looks like they belong to a block comment..
src/hotspot/share/c1/c1_IR.cpp
Outdated
@@ -1261,20 +1261,41 @@ void IR::print(bool cfg_only, bool live_only) { | |||
} | |||
} | |||
|
|||
inline void validate_end_not_null(BlockBegin* block) { | |||
assert(block->end() != NULL, "Expect block end to exist."); |
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.
As this method is just a single assertion and used from one place, you could directly inline it into block_do()
below.
@@ -248,7 +257,9 @@ void CE_Eliminator::block_do(BlockBegin* block) { | |||
tty->print_cr("%d. IfOp in B%d", ifop_count(), block->block_id()); | |||
} | |||
|
|||
_hir->verify(); | |||
#ifdef ASSERT | |||
_hir->verify_local(blocks_to_verify_later); |
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 can use NOT_PRODUCT()
for single line statements (assuming you change the ASSERT
into ifndef PRODUCT
).
#endif // ASSERT | ||
|
||
#ifdef ASSERT |
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.
Can be merged into one ifdef
block.
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'm removing that specific verify_local call anyway, left in by accident
src/hotspot/share/c1/c1_IR.cpp
Outdated
} | ||
}; | ||
|
||
typedef GrowableArray<BlockList*> BlockListList; | ||
|
||
class PredecessorValidator : public BlockClosure { | ||
void verify_successor_xentry_flag(const BlockBegin* block) { |
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.
Could this method also be directly inlined into block_do
below? The class name and assertion message should give enough information about the purpose of the check.
src/hotspot/share/c1/c1_IR.cpp
Outdated
for ( Instruction *cur = block; cur != NULL; cur = cur->next()) { | ||
assert(cur->block() == block, "Block begin is not correct"); | ||
} | ||
void validate_edge_mutuality(BlockBegin* block) { |
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.
Same here, could this method also be directly inlined into block_do
below?
src/hotspot/share/c1/c1_IR.cpp
Outdated
PredecessorValidator pv(this); | ||
EndNotNullValidator(this); | ||
XentryFlagValidator xe; | ||
this->iterate_postorder(&xe); |
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 can remove this->
.
@chhagedorn Thank you for the review! I will get to cleaning up the comments, and I'll also get back about changes in compilations times. |
small local interspersed test with 2 first rounds discarded seems to show a slight increase in compilation time, which can be explained by the added checks:
Invocation: |
Note that the timeouts of openjdk/jdk16#44 were remediated, likely by accident, in https://bugs.openjdk.java.net/browse/JDK-8267806, thus making it a worse candidate for testing this. |
The increase in the previous test was likely not statistically significant, I ran a new test with 100 iterations and the difference seems gone.
|
A test on -version shows a clear reduction in Optimize time, leading to a small reduction in c1 compile time. I call this a "boosted test" because
Invocation: |
And again, it's important to remember that the point of this fix is to protect against "worst cases" with e.g. functions with very long bytecode. The compile time improvement in the general case, if any, is only a bonus. |
Whichever way the PRODUCT/ASSERT pendulum swings, what's for sure is that it needs to be consistent, so I decided to move it around "single source of truth"-style. I'm not from the compiler team so I don't know the implications or the build process, but I'm surprised there isn't an answer to "how do we want to guard debug code?" I need a clear decision and guideline from the compiler team here. |
When applying these changes:
Then you can change My understanding is that assertion/verification code should be under |
Yes, that looks good. It accepts the non-product flag |
I did say 1224: "I suggest to change #ifndef PRODUCT at line 1224 to #ifdef ASSERT" As Christian said, use Note, my suggestion for changing Please, use
Yes, this is correct. The assertion/verification is used in our regular testing when we use fastdebug builds to catch issues. Yes, we have RFE to remove optimized build and replace it with separate value check: |
Thanks for confirming that.
That's good to know! |
There might be a prettier alternative than So I understand the conclusion was to use ASSERT, NOT_DEBUG, and NOT_DEBUG_RETURN for all the code in this PR. Thank you. @vnkozlov apologies if I got the line numbering wrong in my head somewhere there. |
I am not comfortable with repeated I'm fine with using I think you can simply put verification methods declaration undo
And put everything from |
And please update copyright year to 2022 in changed files headers. |
@vnkozlov Done, although using ASSERT in c1_IR.hpp didn't work because c1_Compilation.cpp makes several unguarded calls to IR::verify. I've left it with NOT_DEBUG_RETURN, hope that is ok. I hope I've covered all the issues now, but please point it out if I've missed something. |
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.
@vnkozlov Done, although using ASSERT in c1_IR.hpp didn't work because c1_Compilation.cpp makes several unguarded calls to IR::verify. I've left it with NOT_DEBUG_RETURN, hope that is ok.
I think that is fine. Otherwise, you need to wrap the calls in c1_compilation.cpp
with NOT_DEBUG()
.
Thanks for doing the updates, they look good to me! If not already done, please verify once again that the optimized build is working with your latest changes before eventually integrating this ;-)
Yes, I did verify that. |
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.
/integrate |
@LudwikJaniuk |
@vnkozlov @chhagedorn Could you please sponsor? |
/sponsor |
Going to push as commit d70545d.
Your commit was automatically rebased without conflicts. |
@vnkozlov @LudwikJaniuk Pushed as commit d70545d. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
IR::verify iterates the whole object graph. This proves costly when used in e.g. BlockMerger inside of iterations over BlockLists, leading to quadratic or worse complexities as a function of bytecode length. In several cases, only a few Blocks were changed, and there was no need to go over the whole graph, but until now there was no less blunt tool for verification than IR::verify.
This PR introduces IR::verify_local, intended to be used when only a defined set of blocks have been modified. As a complement, expand_with_neighbors provides a way to also capture the neighbors of the "modified set" ahead of modification, so that afterwards the appropriate asserts can be made on all blocks which might possibly have been changed. All this should let us remove the expensive IR::verify calls, while still performing equivalent (or stricter) assertions.
Some changes have been made in the verifiers along the way. Some amount of refactoring, and even added invariants (see validate_edge_mutiality).
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/6850/head:pull/6850
$ git checkout pull/6850
Update a local copy of the PR:
$ git checkout pull/6850
$ git pull https://git.openjdk.java.net/jdk pull/6850/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 6850
View PR using the GUI difftool:
$ git pr show -t 6850
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/6850.diff