-
Notifications
You must be signed in to change notification settings - Fork 5.6k
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
8325746: Refactor Loop Unswitching code #17842
Conversation
👋 Welcome back chagedorn! A progress list of the required criteria for merging this PR into |
@chhagedorn 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. |
if (TraceLoopOpts) { | ||
tty->print("Unswitch %d ", head->unswitch_count()+1); | ||
loop->dump_head(); |
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.
Extracted to trace_loop_unswitching_count()
if (head->is_CountedLoop() && !head->as_CountedLoop()->is_normal_loop()) { | ||
head->as_CountedLoop()->set_normal_loop(); |
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.
Extracted to revert_to_normal_loop()
LoopNode* original_head = loop->_head->as_Loop(); | ||
if (has_control_dependencies_from_predicates(original_head)) { | ||
NOT_PRODUCT(trace_loop_unswitching_impossible(original_head);) | ||
return; | ||
} |
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.
Extracted to hoist_invariant_check_casts()
LoopNode* head_clone = old_new[head->_idx]->as_Loop(); | ||
int nct = head->unswitch_count() + 1; | ||
head->set_unswitch_count(nct); | ||
head_clone->set_unswitch_count(nct); |
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.
Extracted to increment_unswitch_counts()
.
// Reoptimize loops | ||
loop->record_for_igvn(); | ||
for(int i = loop->_body.size() - 1; i >= 0 ; i--) { | ||
Node *n = loop->_body[i]; | ||
Node *n_clone = old_new[n->_idx]; | ||
_igvn._worklist.push(n_clone); | ||
} |
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.
Extracted to add_unswitched_loop_version_bodies_to_igvn()
#ifndef PRODUCT | ||
if (TraceLoopUnswitching) { | ||
tty->print_cr("Loop unswitching orig: %d @ %d new: %d @ %d", | ||
head->_idx, unswitch_iff->_idx, | ||
old_new[head->_idx]->_idx, unswitch_iff_clone->_idx); | ||
} | ||
#endif |
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.
Extracted to trace_loop_unswitching_result()
IfNode* invar_iff = create_slow_version_of_loop(loop, old_new, unswitch_iff, CloneIncludesStripMined); | ||
ProjNode* proj_true = invar_iff->proj_out(1); | ||
verify_fast_loop(head, proj_true); |
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.
Refactored to create_unswitched_loop_versions()
.
_igvn.rehash_node_delayed(unswitch_iff); | ||
dominated_by(proj_true->as_IfProj(), unswitch_iff); | ||
NOT_PRODUCT(trace_loop_unswitching_count(loop, original_head);) | ||
C->print_method(PHASE_BEFORE_LOOP_UNSWITCHING, 4, original_head); | ||
|
||
IfNode* unswitch_iff_clone = old_new[unswitch_iff->_idx]->as_If(); | ||
_igvn.rehash_node_delayed(unswitch_iff_clone); | ||
ProjNode* proj_false = invar_iff->proj_out(0); | ||
dominated_by(proj_false->as_IfProj(), unswitch_iff_clone); |
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.
Extracted to remove_unswitch_candidate_from_loops()
and put inside create_unswitched_loop_versions()
.
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.
Nice work! A first batch of comments.
// Loop Unswitching is a loop optimization to move an invariant, non-loop-exiting test in the loop body before the loop. | ||
// This is done by duplicating the loop and placing the if-path in one unswitched loop version (i.e. the true-path-loop) | ||
// and the else-path in the other (i.e. the false-path-loop). The invariant test only needs to be checked once and | ||
// depending on the outcome, one or the other unswitched loop version is executed: |
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.
// Loop Unswitching is a loop optimization to move an invariant, non-loop-exiting test in the loop body before the loop. | |
// This is done by duplicating the loop and placing the if-path in one unswitched loop version (i.e. the true-path-loop) | |
// and the else-path in the other (i.e. the false-path-loop). The invariant test only needs to be checked once and | |
// depending on the outcome, one or the other unswitched loop version is executed: | |
// Loop Unswitching is a loop optimization to move an invariant, non-loop-exiting test in the loop body before the loop. | |
// For one loop execution, this test is either always true or always false. Hence, we duplicate the loop: the true-path-loop always takes the true-path and the false-path-loop always takes the false-path. We move the test | |
// before the loops, turning it into a loop selector. The true-proj leads to the true-path-loop, and the false-proj | |
// leads to the false-path-loop. Now the invariant test is only checked once before the loop, and decides which | |
// unswitched loop version is executed: |
Make sure to be consistent. I think you use if-path
and if-block
as synonymes, right?
Btw: are these only blocks, as in basic blocks, and hence the if-statements are only such "basic diamonds", that only have one block per if-else side? Or could the CFG be more complex?
placing the if-path in one unswitched loop version
That is a bit confusing. It sounds like you are taking the hoisted if-true-projection, and putting it inside a loop, which does not make sense.
later you name the hoisted if a "loop selector". We could already name it the same here.
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.
BTW: why do we not hoist exit conditions also? Would that not also be profitable? Just out of curiosity...
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.
BTW: why do we not hoist exit conditions also? Would that not also be profitable? Just out of curiosity...
This can better be done with a simple loop peeling which can hoist the invariant checks. See:
jdk/src/hotspot/share/opto/loopTransform.cpp
Lines 499 to 502 in 0c2def0
// Condition is not a member of this loop? | |
if (!is_member(phase->get_loop(ctrl)) && is_loop_exit(test)) { | |
return estimate; // Found reason to peel! | |
} |
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.
Ah yes, makes sense!
Maybe add a small comment about loop peeling then? Just because you do mention that we don't handle exits, that automatically raises that question.
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.
Ah yes, makes sense! Maybe add a small comment about loop peeling then? Just because you do mention that we don't handle exits, that automatically raises that question.
I'll add a comment.
Make sure to be consistent. I think you use if-path and if-block as synonymes, right?
Btw: are these only blocks, as in basic blocks, and hence the if-statements are only such "basic diamonds", that only have one block per if-else side? Or could the CFG be more complex?
The only requirement for Loop Unswitching is that both paths are inside the loop (I make this more explicit in the header comment together with mentioning loop peeling as alternative for loop-exiting tests). When this is given, we will at some point merge both paths again with a region. So, yes, I use if-path and if-block interchangeable. I've cleaned that up in the comments.
For example, this example can also be unswitched on flag
with a more complex CFG and even with a method call:
static int iFld;
static boolean flag2;
// Run with -Xcomp.
static void test() {
boolean flag = iFld < 3;
for (int i = 0; i < 1000; i++) {
if (flag) {
iFld = 34;
if (flag2) {
dontInline();
}
}
}
}
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've rewritten this paragraph inspired by your suggestions. Please have another look at it :-)
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.
Nice Work @chhagedorn . I like that you packed things into classes. It nicely packs things into more digestable concepts. And allows you to create objects of those classes, and then pass then around as const
, after which the reader knows they are not changed anymore. Very helpful!
} | ||
|
||
// Remove the unswitch candidate If nodes in both unswitched loop versions which are now dominated by the loop selector | ||
// If node. Keep the true path block in the true-path-loop and the false path block in the false-path-loop by setting |
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 elsewhere: is it really right to talk about "blocks"? Or could the CFG be more complicated?
#endif // ASSERT | ||
|
||
public: | ||
// Unswitch on the invariant loop selector by creating two unswitched loop versions. | ||
void unswitch(const UnswitchedLoopSelector& unswitched_loop_selector) { |
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 suggestion, I am also ok with what it is now:
I wonder if the name unswitch
is right here. Because at this point you only clone the loops, you don't yet remove the if. Maybe this should for now just be called "duplicate", and create_unswitched_loop_versions
then really does the unswitching, by both duplicating and removing the ifs?
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've moved remove_unswitch_candidate_from_loop()
into this method to really have the complete unswitching process in this method. This also allowed to get rid of create_unswitched_loop_versions()
which now was only a redundant delegation. This required to move the OriginalLoop
class further up.
class OriginalLoop : public StackObj { | ||
LoopNode* const _strip_mined_loop_head; | ||
IdealLoopTree* const _loop; | ||
Node_List* const _old_new; |
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 also be held "by reference". Then you don't have convert it from and to pointer. Well, idk, maybe it would be just as bad the other way around.
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.
Done
void PhaseIdealLoop::revert_to_normal_loop(const LoopNode* loop_head) { | ||
if (loop_head->is_CountedLoop() && !loop_head->as_CountedLoop()->is_normal_loop()) { | ||
loop_head->as_CountedLoop()->set_normal_loop(); | ||
} |
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.
void PhaseIdealLoop::revert_to_normal_loop(const LoopNode* loop_head) { | |
if (loop_head->is_CountedLoop() && !loop_head->as_CountedLoop()->is_normal_loop()) { | |
loop_head->as_CountedLoop()->set_normal_loop(); | |
} | |
void PhaseIdealLoop::revert_to_normal_loop(const LoopNode* loop_head) { | |
CountedLoopNode* cl = loop_head->isa_CountedLoop(); | |
if (cl != nullptr && !cl->is_normal_loop()) { | |
cl->set_normal_loop(); | |
} |
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.
Would make the code easier to read
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 just moved the code but I can clean it up as you suggested.
Co-authored-by: Emanuel Peter <emanuel.peter@oracle.com>
Co-authored-by: Emanuel Peter <emanuel.peter@oracle.com>
Co-authored-by: Emanuel Peter <emanuel.peter@oracle.com>
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 a lot for your suggestions! I've pushed updates addressing them all.
// Loop Unswitching is a loop optimization to move an invariant, non-loop-exiting test in the loop body before the loop. | ||
// This is done by duplicating the loop and placing the if-path in one unswitched loop version (i.e. the true-path-loop) | ||
// and the else-path in the other (i.e. the false-path-loop). The invariant test only needs to be checked once and | ||
// depending on the outcome, one or the other unswitched loop version is executed: |
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.
Ah yes, makes sense! Maybe add a small comment about loop peeling then? Just because you do mention that we don't handle exits, that automatically raises that question.
I'll add a comment.
Make sure to be consistent. I think you use if-path and if-block as synonymes, right?
Btw: are these only blocks, as in basic blocks, and hence the if-statements are only such "basic diamonds", that only have one block per if-else side? Or could the CFG be more complex?
The only requirement for Loop Unswitching is that both paths are inside the loop (I make this more explicit in the header comment together with mentioning loop peeling as alternative for loop-exiting tests). When this is given, we will at some point merge both paths again with a region. So, yes, I use if-path and if-block interchangeable. I've cleaned that up in the comments.
For example, this example can also be unswitched on flag
with a more complex CFG and even with a method call:
static int iFld;
static boolean flag2;
// Run with -Xcomp.
static void test() {
boolean flag = iFld < 3;
for (int i = 0; i < 1000; i++) {
if (flag) {
iFld = 34;
if (flag2) {
dontInline();
}
}
}
}
// Loop Unswitching is a loop optimization to move an invariant, non-loop-exiting test in the loop body before the loop. | ||
// This is done by duplicating the loop and placing the if-path in one unswitched loop version (i.e. the true-path-loop) | ||
// and the else-path in the other (i.e. the false-path-loop). The invariant test only needs to be checked once and | ||
// depending on the outcome, one or the other unswitched loop version is executed: |
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've rewritten this paragraph inspired by your suggestions. Please have another look at it :-)
class OriginalLoop : public StackObj { | ||
LoopNode* const _strip_mined_loop_head; | ||
IdealLoopTree* const _loop; | ||
Node_List* const _old_new; |
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.
Done
#endif // ASSERT | ||
|
||
public: | ||
// Unswitch on the invariant loop selector by creating two unswitched loop versions. | ||
void unswitch(const UnswitchedLoopSelector& unswitched_loop_selector) { |
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've moved remove_unswitch_candidate_from_loop()
into this method to really have the complete unswitching process in this method. This also allowed to get rid of create_unswitched_loop_versions()
which now was only a redundant delegation. This required to move the OriginalLoop
class further up.
// if-block =========> [Cloned Template [Cloned Template | ||
// else Assertion Predicates] Assertion Predicates] | ||
// // could be empty | | | ||
// [else-block] True-Path-Loop False-Path-Loop |
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 it also be the other way around, that the if-path is empty but the else-path has code?
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 definitely, but this is more a reflection of Java code where you always have an if block but not always an else block. But it might be cleaner to just remove the "could be empty" comment and make it explicit and not worry about whether one block is empty. I'll do that. The image should just give a hint :-)
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 updates, we are almost there :)
// stmt2 cloned stmt1 cloned stmt1 | ||
// Endloop cloned if-path [cloned else-path] | ||
// cloned stmt2 cloned stmt2 | ||
// Endloop Endloop |
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 it be the other way around: the if-path is empty and the else-path has code? Or are you just trying to suggest that there could be an empty path, and that this would really simplify one of the cloned loops now?
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, it would be a simplification. But as suggested in the other comment. It might create more confusion than actually helping.
Co-authored-by: Emanuel Peter <emanuel.peter@oracle.com>
src/hotspot/share/opto/loopnode.hpp
Outdated
@@ -770,6 +771,11 @@ class IdealLoopTree : public ResourceObj { | |||
return _has_range_checks; | |||
} | |||
|
|||
// Return the strip mined loop tree if there is an outer strip mined loop. Otherwise, return this. |
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.
The comment is not clear for me. Something like next?:
"// Return parent's IdealLoopTree for strip mined loop and this
for other cases."
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 refactoring. My main complain is long comments which are difficult to read if you edit file in terminal.
// - Original loop -> true-path-loop: The true-path of the invariant, non-loop-exiting test in the original loop | ||
// is kept while the false-path is killed. We call this unswitched loop version | ||
// the true-path-loop. | ||
// - Cloned loop -> False-path-loop: The false-path of the invariant, non-loop-exiting test in the cloned loop | ||
// is kept while the true-path is killed. We call this unswitched loop version | ||
// the false-path loop. |
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.
Comments are too wide and difficult to read. Suggestion is to put explanation on new line with shift:
// - Original loop -> true-path-loop:
// The true-path of the invariant, non-loop-exiting test in the original loop
// is kept while the false-path is killed. We call this unswitched loop version
// the true-path-loop.
// Endloop cloned stmt1 cloned stmt1 | ||
// cloned if-path cloned else-path | ||
// cloned stmt2 cloned stmt2 | ||
// Endloop Endloop |
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 you put resulting unstitched graph on separate following lines? Comment line are too long.
Thanks @eme64 and @vnkozlov for your reviews! I've addressed your latest comments. About the long lines: I'm using 120 chars by default which fits nicely on the screen in the IDE and is also the default suggested by the IDE. But I guess that's also a matter of taste. Unfortunately, our style guide also does not mention anything about it - maybe because it's a personal preference. But anyway, I agree that some comments were a bit too densely packed. I've updated them according to your suggestions. Let me know if there are other comments that you think should be updated as well. |
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.
Much better.
@chhagedorn 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 176 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.
Nice work, Christian!
Co-authored-by: Emanuel Peter <emanuel.peter@oracle.com>
Thanks Vladimir and Emanuel for your reviews and also Emanuel for the offline discussion! |
I'll resubmit some testing again before integration. |
Testing showed 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.
Update makes sense. A few suggestions below.
* @requires vm.compiler2.enabled | ||
* @run main/othervm -XX:CompileCommand=compileonly,compiler.loopopts.TestBaseCountedEndLoopUnswitchCandidate::test* | ||
* -Xcomp -XX:LoopMaxUnroll=0 -XX:-UseLoopPredicate -XX:-RangeCheckElimination | ||
* compiler.loopopts.TestBaseCountedEndLoopUnswitchCandidate |
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.
Might it make sense to have a run with fewer flags?
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 you mean an additional run? For the current run, we require the C2 flags in order to have loop peeling -> loop unswitching to get unswitching on a BaseCountedLoopEndNode
. We might be able to find a test that requires fewer flags but I think it's okay to opt for a simpler test + flags.
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, I thought you could just add another @run
with fewer flags, so that external flags have more influence, and potentially trigger other cases.
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 good, added such a run.
src/hotspot/share/opto/ifnode.cpp
Outdated
// the same profiling if that actually makes sense. Some If node subtypes should not be cloned in this way. | ||
// In theory, we should not clone BaseCountedLoopEndNodes. But they can end up being used as normal If nodes when | ||
// peeling a loop - they serve as zero-trip guard. Allow them as well. | ||
assert(if_node_profile->Opcode() == Op_If || if_node_profile->is_RangeCheck() |
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.
if_node_profile->is_RangeCheck()
seems excluded by the if-else above...
You could move the assert before the if, then it would be correct, I think
} | ||
|
||
|
||
public static void testLongCountedLoopEnd() { |
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.
Remove a newline
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 Emanuel for your re-review! Pushed an update.
* @requires vm.compiler2.enabled | ||
* @run main/othervm -XX:CompileCommand=compileonly,compiler.loopopts.TestBaseCountedEndLoopUnswitchCandidate::test* | ||
* -Xcomp -XX:LoopMaxUnroll=0 -XX:-UseLoopPredicate -XX:-RangeCheckElimination | ||
* compiler.loopopts.TestBaseCountedEndLoopUnswitchCandidate |
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 you mean an additional run? For the current run, we require the C2 flags in order to have loop peeling -> loop unswitching to get unswitching on a BaseCountedLoopEndNode
. We might be able to find a test that requires fewer flags but I think it's okay to opt for a simpler test + flags.
To simplify the review of #16877, I've decided to split off some code into separate sub-tasks.
This sub-task focuses on the code refactoring done in Loop Unswitching. I've incorporated the feedback from @eme64 at #16877 in the proposed changes.
Changes
fast loop
->true-path-loop
andslow loop
->false-path-loop
since we don't really know which one is faster and to avoid keeping a mental map of which path goes into which unswitched loop version.UnswitchedLoopSelector
to create the unswitched loop selectorIf
(i.e. the If node that selects at runtime which unswitched loop version is executed) based on the invariant and non-loop-exiting unswitch candidateIf
.OriginalLoop
to do the actual unswitching (i.e. loop cloning, predicates cloning and fixing the graph to the loop selectorIf
).do_unswitching()
to clean it up further.TraceLoopUnswitching
result printing.Possible Follow-Up RFEs
There are possible follow-up opportunities to further improve the loop unswitching which, however, would go to far in the context of fixing Assertion Predicates. We could follow up with RFEs to tackle these:
LoopUnswitching
class to split all the methods off fromPhaseIdealLoop
. This would also avoid noisy "loop_unswitching" prefixes.has_control_dependencies_from_predicates()
is too conservative. I have already some code to improve that in the full fix for Assertion Predicates which I plan to split off and propose separatly.TraceLoopUnswitching
currently only prints the result and if it failed. We might want to improve that in the future to cover more steps to make it useful for debugging (would require some more fine-grained debug levels, though).Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/17842/head:pull/17842
$ git checkout pull/17842
Update a local copy of the PR:
$ git checkout pull/17842
$ git pull https://git.openjdk.org/jdk.git pull/17842/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 17842
View PR using the GUI difftool:
$ git pr show -t 17842
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/17842.diff
Webrev
Link to Webrev Comment