blob: a8d9a5d740bc4455700088d5449ef3a0ad5b798c [file] [edit]
/*
* Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "memory/allocation.inline.hpp"
#include "opto/castnode.hpp"
#include "opto/cfgnode.hpp"
#include "opto/connode.hpp"
#include "opto/convertnode.hpp"
#include "opto/loopnode.hpp"
#include "opto/opaquenode.hpp"
#include "opto/predicates.hpp"
#include "opto/rootnode.hpp"
// Multiversioning:
// A loop is cloned, and a selector If decides which loop is taken at run-time: the true-path-loop (original) or the
// false-path-loop (cloned).
//
// Use-cases:
// - Speculative compilation:
// The selector If checks some assumptions which allow stronger optimization in the true-path-loop. If the assumptions
// do not hold, we can still execute in the false-path-loop, although with fewer optimizations.
// See: PhaseIdealLoop::maybe_multiversion_for_auto_vectorization_runtime_checks
// PhaseIdealLoop::create_new_if_for_multiversion
//
// - Unswitching:
// The selector If has the same (loop invariant) condition as some unswitching candidate If inside the loop. This
// allows us to constant-fold the unswitching candidate If to true in the true-path-loop and to false in the
// false-path-loop, thus eliminating the unswitching candidate If from the loop.
//
//
// Loop Unswitching is a loop optimization to move an invariant, non-loop-exiting test in the loop body before the loop.
// Such a test is either always true or always false in all loop iterations and could therefore only be executed once.
// To achieve that, we duplicate the loop and change the original and cloned loop as follows:
// - 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.
//
// The invariant, non-loop-exiting test can now be moved before both loops (to only execute it once) and turned into a
// loop selector If node to select at runtime which unswitched loop version should be executed.
// - Loop selector true? Execute the true-path-loop.
// - Loop selector false? Execute the false-path-loop.
//
// Note that even though an invariant test that exits the loop could also be optimized with Loop Unswitching, it is more
// efficient to simply peel the loop which achieves the same result in a simpler manner (also see policy_peeling()).
//
// The following graphs summarizes the Loop Unswitching optimization.
// We start with the original loop:
//
// [Predicates]
// |
// Original Loop
// stmt1
// if (invariant-test)
// if-path
// else
// else-path
// stmt2
// Endloop
//
//
// which is unswitched into a true-path-loop and a false-path-loop together with a loop selector:
//
//
// [Initialized Assertion Predicates]
// |
// loop selector If (invariant-test)
// / \
// true? false?
// / \
// [Cloned Parse Predicates] [Cloned Parse Predicates]
// [Cloned Template [Cloned Template
// Assertion Predicates] Assertion Predicates]
// | |
// True-Path-Loop False-Path-Loop
// cloned stmt1 cloned stmt1
// cloned if-path cloned else-path
// cloned stmt2 cloned stmt2
// Endloop Endloop
// Return true if the loop should be unswitched or false otherwise.
bool IdealLoopTree::policy_unswitching(PhaseIdealLoop* phase) const {
if (!LoopUnswitching) {
return false;
}
if (!_head->is_Loop()) {
return false;
}
// If nodes are depleted, some transform has miscalculated its needs.
assert(!phase->exceeding_node_budget(), "sanity");
// check for vectorized loops, any unswitching was already applied
if (_head->is_CountedLoop() && _head->as_CountedLoop()->is_unroll_only()) {
return false;
}
LoopNode* head = _head->as_Loop();
if (head->unswitch_count() + 1 > head->unswitch_max()) {
return false;
}
if (phase->find_unswitch_candidate(this) == nullptr) {
return false;
}
// Too speculative if running low on nodes.
return phase->may_require_nodes(est_loop_clone_sz(2));
}
// Find an invariant test in the loop body that does not exit the loop. If multiple tests are found, we pick the first
// one in the loop body. Return the "unswitch candidate" If to apply Loop Unswitching on.
IfNode* PhaseIdealLoop::find_unswitch_candidate(const IdealLoopTree* loop) const {
LoopNode* head = loop->_head->as_Loop();
IfNode* unswitch_candidate = nullptr;
Node* n = head->in(LoopNode::LoopBackControl);
while (n != head) {
Node* n_dom = idom(n);
if (n->is_Region()) {
if (n_dom->is_If()) {
IfNode* iff = n_dom->as_If();
if (iff->in(1)->is_Bool()) {
BoolNode* bol = iff->in(1)->as_Bool();
if (bol->in(1)->is_Cmp()) {
// If condition is invariant and not a loop exit,
// then found reason to unswitch.
if (loop->is_invariant(bol) && !loop->is_loop_exit(iff)) {
assert(iff->Opcode() == Op_If || iff->is_RangeCheck() || iff->is_BaseCountedLoopEnd(), "valid ifs");
unswitch_candidate = iff;
}
}
}
}
}
n = n_dom;
}
return unswitch_candidate;
}
// LoopSelector is used for loop multiversioning and unswitching. This class creates an If node (i.e. loop selector)
// that selects if the true-path-loop or the false-path-loop should be executed at runtime.
class LoopSelector : public StackObj {
// Cached fields for construction.
PhaseIdealLoop* const _phase;
IdealLoopTree* const _outer_loop;
Node* const _original_loop_entry;
const uint _dom_depth; // of original_loop_entry
// Constructed selector if with its projections.
IfNode* const _selector;
IfTrueNode* const _true_path_loop_proj;
IfFalseNode* const _false_path_loop_proj;
enum PathToLoop { TRUE_PATH, FALSE_PATH };
public:
// For multiversioning: create a new selector (multiversion_if) from a bol condition.
LoopSelector(IdealLoopTree* loop, Node* bol, float prob, float fcnt)
: _phase(loop->_phase),
_outer_loop(loop->skip_strip_mined()->_parent),
_original_loop_entry(loop->_head->as_Loop()->skip_strip_mined()->in(LoopNode::EntryControl)),
_dom_depth(_phase->dom_depth(_original_loop_entry)),
_selector(create_multiversioning_if(bol, prob, fcnt)), // multiversioning
_true_path_loop_proj(create_proj_to_loop(TRUE_PATH)->as_IfTrue()),
_false_path_loop_proj(create_proj_to_loop(FALSE_PATH)->as_IfFalse()) {
}
// For unswitching: create an unswitching if before the loop, from a pre-existing
// unswitching_candidate inside the loop.
LoopSelector(IdealLoopTree* loop, IfNode* unswitch_candidate)
: _phase(loop->_phase),
_outer_loop(loop->skip_strip_mined()->_parent),
_original_loop_entry(loop->_head->as_Loop()->skip_strip_mined()->in(LoopNode::EntryControl)),
_dom_depth(_phase->dom_depth(_original_loop_entry)),
_selector(create_unswitching_if(unswitch_candidate)), // unswitching
_true_path_loop_proj(create_proj_to_loop(TRUE_PATH)->as_IfTrue()),
_false_path_loop_proj(create_proj_to_loop(FALSE_PATH)->as_IfFalse()) {
}
NONCOPYABLE(LoopSelector);
IfNode* create_multiversioning_if(Node* bol, float prob, float fcnt) {
_phase->igvn().rehash_node_delayed(_original_loop_entry);
IfNode* selector_if = new IfNode(_original_loop_entry, bol, prob, fcnt);
_phase->register_node(selector_if, _outer_loop, _original_loop_entry, _dom_depth);
return selector_if;
}
IfNode* create_unswitching_if(IfNode* unswitch_candidate) {
_phase->igvn().rehash_node_delayed(_original_loop_entry);
BoolNode* unswitch_candidate_bool = unswitch_candidate->in(1)->as_Bool();
IfNode* selector_if = IfNode::make_with_same_profile(unswitch_candidate, _original_loop_entry,
unswitch_candidate_bool);
_phase->register_node(selector_if, _outer_loop, _original_loop_entry, _dom_depth);
return selector_if;
}
private:
IfProjNode* create_proj_to_loop(const PathToLoop path_to_loop) {
IfProjNode* proj_to_loop;
if (path_to_loop == TRUE_PATH) {
proj_to_loop = new IfTrueNode(_selector);
} else {
proj_to_loop = new IfFalseNode(_selector);
}
_phase->register_node(proj_to_loop, _outer_loop, _selector, _dom_depth);
return proj_to_loop;
}
public:
IfNode* selector() const {
return _selector;
}
IfTrueNode* true_path_loop_proj() const {
return _true_path_loop_proj;
}
IfFalseNode* false_path_loop_proj() const {
return _false_path_loop_proj;
}
};
// This class creates an If node (i.e. loop selector) that selects if the true-path-loop or the false-path-loop should be
// executed at runtime. This is done by finding an invariant and non-loop-exiting unswitch candidate If node (guaranteed
// to exist at this point) to perform Loop Unswitching on.
class UnswitchedLoopSelector : public StackObj {
IfNode* const _unswitch_candidate;
const LoopSelector _loop_selector;
public:
UnswitchedLoopSelector(IdealLoopTree* loop)
: _unswitch_candidate(find_unswitch_candidate(loop)),
_loop_selector(loop, _unswitch_candidate) {}
NONCOPYABLE(UnswitchedLoopSelector);
private:
static IfNode* find_unswitch_candidate(IdealLoopTree* loop) {
IfNode* unswitch_candidate = loop->_phase->find_unswitch_candidate(loop);
assert(unswitch_candidate != nullptr, "guaranteed to exist by policy_unswitching");
assert(loop->_phase->is_member(loop, unswitch_candidate), "must be inside original loop");
return unswitch_candidate;
}
public:
IfNode* unswitch_candidate() const {
return _unswitch_candidate;
}
const LoopSelector& loop_selector() const {
return _loop_selector;
}
};
// Class to unswitch the original loop and create Predicates at the new unswitched loop versions. The newly cloned loop
// becomes the false-path-loop while original loop becomes the true-path-loop.
class OriginalLoop : public StackObj {
LoopNode* const _loop_head;
LoopNode* const _outer_loop_head; // OuterStripMinedLoopNode if loop strip mined, else just the loop head.
IdealLoopTree* const _loop;
Node_List& _old_new;
PhaseIdealLoop* const _phase;
public:
OriginalLoop(IdealLoopTree* loop, Node_List& old_new)
: _loop_head(loop->_head->as_Loop()),
_outer_loop_head(loop->_head->as_Loop()->skip_strip_mined()),
_loop(loop),
_old_new(old_new),
_phase(loop->_phase) {}
NONCOPYABLE(OriginalLoop);
// Unswitch the original loop on the invariant loop selector by creating a true-path-loop and a false-path-loop.
// Remove the unswitch candidate If from both unswitched loop versions which are now covered by the loop selector If.
void unswitch(const UnswitchedLoopSelector& unswitched_loop_selector) {
multiversion(unswitched_loop_selector.loop_selector());
remove_unswitch_candidate_from_loops(unswitched_loop_selector);
}
// Multiversion the original loop. The loop selector if selects between the original loop (true-path-loop), and
// a copy of it (false-path-loop).
void multiversion(const LoopSelector& loop_selector) {
const uint first_false_path_loop_node_index = _phase->C->unique();
clone_loop(loop_selector);
move_parse_and_template_assertion_predicates_to_unswitched_loops(loop_selector,
first_false_path_loop_node_index);
DEBUG_ONLY(verify_loop_versions(_loop->_head->as_Loop(), loop_selector);)
_phase->recompute_dom_depth();
}
private:
void clone_loop(const LoopSelector& loop_selector) {
_phase->clone_loop(_loop, _old_new, _phase->dom_depth(_outer_loop_head),
PhaseIdealLoop::CloneIncludesStripMined, loop_selector.selector());
fix_loop_entries(loop_selector);
}
void fix_loop_entries(const LoopSelector& loop_selector) const {
_phase->replace_loop_entry(_outer_loop_head, loop_selector.true_path_loop_proj());
LoopNode* false_path_loop_strip_mined_head = old_to_new(_outer_loop_head)->as_Loop();
_phase->replace_loop_entry(false_path_loop_strip_mined_head,
loop_selector.false_path_loop_proj());
}
// Moves the Parse And Template Assertion Predicates to the true and false path loop. They are inserted between the
// loop heads and the loop selector If projections. The old Parse and Template Assertion Predicates before
// the unswitched loop selector are killed.
void move_parse_and_template_assertion_predicates_to_unswitched_loops(
const LoopSelector& loop_selector, const uint first_false_path_loop_node_index) const {
const NodeInOriginalLoopBody node_in_true_path_loop_body(first_false_path_loop_node_index, _old_new);
const NodeInClonedLoopBody node_in_false_path_loop_body(first_false_path_loop_node_index);
CloneUnswitchedLoopPredicatesVisitor
clone_unswitched_loop_predicates_visitor(_loop_head, old_to_new(_loop_head)->as_Loop(), node_in_true_path_loop_body,
node_in_false_path_loop_body, _phase);
Node* source_loop_entry = loop_selector.selector()->in(0);
PredicateIterator predicate_iterator(source_loop_entry);
predicate_iterator.for_each(clone_unswitched_loop_predicates_visitor);
}
#ifdef ASSERT
void verify_loop_versions(LoopNode* true_path_loop_head,
const LoopSelector& loop_selector) const {
verify_loop_version(true_path_loop_head,
loop_selector.true_path_loop_proj());
verify_loop_version(old_to_new(true_path_loop_head)->as_Loop(),
loop_selector.false_path_loop_proj());
}
static void verify_loop_version(LoopNode* loop_head, IfProjNode* loop_selector_if_proj) {
Node* entry = loop_head->skip_strip_mined()->in(LoopNode::EntryControl);
const Predicates predicates(entry);
// When skipping all predicates, we should end up at 'loop_selector_if_proj'.
assert(loop_selector_if_proj == predicates.entry(), "should end up at loop selector If");
}
#endif // ASSERT
Node* old_to_new(const Node* old) const {
return _old_new[old->_idx];
}
// 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-path in the true-path-loop and the false-path-path in the false-path-loop by setting
// the bool input accordingly. The unswitch candidate If nodes are folded in the next IGVN round.
void remove_unswitch_candidate_from_loops(const UnswitchedLoopSelector& unswitched_loop_selector) {
const LoopSelector& loop_selector = unswitched_loop_selector.loop_selector();;
IfNode* unswitch_candidate = unswitched_loop_selector.unswitch_candidate();
_phase->igvn().rehash_node_delayed(unswitch_candidate);
_phase->dominated_by(loop_selector.true_path_loop_proj(), unswitch_candidate);
IfNode* unswitch_candidate_clone = _old_new[unswitch_candidate->_idx]->as_If();
_phase->igvn().rehash_node_delayed(unswitch_candidate_clone);
_phase->dominated_by(loop_selector.false_path_loop_proj(), unswitch_candidate_clone);
}
};
// See comments below file header for more information about Loop Unswitching.
void PhaseIdealLoop::do_unswitching(IdealLoopTree* loop, Node_List& old_new) {
assert(LoopUnswitching, "LoopUnswitching must be enabled");
LoopNode* original_head = loop->_head->as_Loop();
if (has_control_dependencies_from_predicates(original_head)) {
NOT_PRODUCT(trace_loop_unswitching_impossible(original_head);)
return;
}
NOT_PRODUCT(trace_loop_unswitching_count(loop, original_head);)
C->print_method(PHASE_BEFORE_LOOP_UNSWITCHING, 4, original_head);
revert_to_normal_loop(original_head);
const UnswitchedLoopSelector unswitched_loop_selector(loop);
OriginalLoop original_loop(loop, old_new);
original_loop.unswitch(unswitched_loop_selector);
hoist_invariant_check_casts(loop, old_new, unswitched_loop_selector);
add_unswitched_loop_version_bodies_to_igvn(loop, old_new);
LoopNode* new_head = old_new[original_head->_idx]->as_Loop();
increment_unswitch_counts(original_head, new_head);
NOT_PRODUCT(trace_loop_unswitching_result(unswitched_loop_selector, original_head, new_head);)
C->print_method(PHASE_AFTER_LOOP_UNSWITCHING, 4, new_head);
C->set_major_progress();
}
void PhaseIdealLoop::do_multiversioning(IdealLoopTree* lpt, Node_List& old_new) {
#ifndef PRODUCT
if (TraceLoopOpts || TraceLoopMultiversioning) {
tty->print("Multiversion ");
lpt->dump_head();
}
#endif
assert(LoopMultiversioning, "LoopMultiversioning must be enabled");
CountedLoopNode* original_head = lpt->_head->as_CountedLoop();
C->print_method(PHASE_BEFORE_LOOP_MULTIVERSIONING, 4, original_head);
Node* one = _igvn.intcon(1);
set_ctrl(one, C->root());
Node* opaque = new OpaqueMultiversioningNode(C, one);
set_ctrl(opaque, C->root());
_igvn.register_new_node_with_optimizer(opaque);
_igvn.set_type(opaque, TypeInt::BOOL);
const LoopSelector loop_selector(lpt, opaque, PROB_LIKELY_MAG(3), COUNT_UNKNOWN);
OriginalLoop original_loop(lpt, old_new);
original_loop.multiversion(loop_selector);
add_unswitched_loop_version_bodies_to_igvn(lpt, old_new);
CountedLoopNode* new_head = old_new[original_head->_idx]->as_CountedLoop();
original_head->set_multiversion_fast_loop();
new_head->set_multiversion_delayed_slow_loop();
NOT_PRODUCT(trace_loop_multiversioning_result(loop_selector, original_head, new_head);)
C->print_method(PHASE_AFTER_LOOP_MULTIVERSIONING, 4, new_head);
C->set_major_progress();
}
// Create a new if in the multiversioning pattern, adding an additional condition for the
// multiversioning fast-loop.
//
// Before:
// entry opaque
// | |
// multiversion_if
// | |
// +----------------+ +---------------+
// | |
// multiversion_fast_proj multiversion_slow_proj
// |
// +--------+
// |
// slow_path
//
//
// After:
// entry opaque <-- to be replaced by caller
// | |
// new_if
// | |
// | +-----------------------------+
// | |
// new_if_true opaque new_if_false
// | | |
// multiversion_if |
// | | |
// +----------------+ +---------------+ |
// | | |
// multiversion_fast_proj new_multiversion_slow_proj |
// | |
// +------+ |
// | |
// region
// |
// slow_path
//
IfTrueNode* PhaseIdealLoop::create_new_if_for_multiversion(IfTrueNode* multiversioning_fast_proj) {
// Give all nodes in the old sub-graph a name.
IfNode* multiversion_if = multiversioning_fast_proj->in(0)->as_If();
Node* entry = multiversion_if->in(0);
OpaqueMultiversioningNode* opaque = multiversion_if->in(1)->as_OpaqueMultiversioning();
IfFalseNode* multiversion_slow_proj = multiversion_if->proj_out(0)->as_IfFalse();
Node* slow_path = multiversion_slow_proj->unique_ctrl_out();
// The slow_loop may still be delayed, and waiting for runtime-checks to be added to the
// multiversion_if. Now that we have at least one condition for the multiversioning,
// we should resume optimizations for the slow loop.
opaque->notify_slow_loop_that_it_can_resume_optimizations();
// Create new_if with its projections.
IfNode* new_if = IfNode::make_with_same_profile(multiversion_if, entry, opaque);
IdealLoopTree* lp = get_loop(entry);
register_control(new_if, lp, entry);
IfTrueNode* new_if_true = new IfTrueNode(new_if);
IfFalseNode* new_if_false = new IfFalseNode(new_if);
register_control(new_if_true, lp, new_if);
register_control(new_if_false, lp, new_if);
// Hook new_if_true into multiversion_if.
_igvn.replace_input_of(multiversion_if, 0, new_if_true);
// Clone multiversion_slow_path - this allows us to easily carry the dependencies to
// the new region below.
IfFalseNode* new_multiversion_slow_proj = multiversion_slow_proj->clone()->as_IfFalse();
register_control(new_multiversion_slow_proj, lp, multiversion_if);
// Create new Region.
RegionNode* region = new RegionNode(1);
region->add_req(new_multiversion_slow_proj);
region->add_req(new_if_false);
register_control(region, lp, new_multiversion_slow_proj);
// Hook region into slow_path, in stead of the multiversion_slow_proj.
// This also moves all other dependencies of the multiversion_slow_proj to the region.
_igvn.replace_node(multiversion_slow_proj, region);
return new_if_true;
}
OpaqueMultiversioningNode* find_multiversion_opaque_from_multiversion_if_false(Node* maybe_multiversion_if_false) {
IfFalseNode* multiversion_if_false = maybe_multiversion_if_false->isa_IfFalse();
if (multiversion_if_false == nullptr) { return nullptr; }
IfNode* multiversion_if = multiversion_if_false->in(0)->isa_If();
if (multiversion_if == nullptr) { return nullptr; }
return multiversion_if->in(1)->isa_OpaqueMultiversioning();
}
bool PhaseIdealLoop::try_resume_optimizations_for_delayed_slow_loop(IdealLoopTree* lpt) {
CountedLoopNode* cl = lpt->_head->as_CountedLoop();
assert(cl->is_multiversion_delayed_slow_loop(), "must currently be delayed");
// Find multiversion_if.
Node* entry = cl->skip_strip_mined()->in(LoopNode::EntryControl);
const Predicates predicates(entry);
Node* slow_path = predicates.entry();
// Find opaque.
OpaqueMultiversioningNode* opaque = nullptr;
if (slow_path->is_Region()) {
for (uint i = 1; i < slow_path->req(); i++) {
Node* n = slow_path->in(i);
opaque = find_multiversion_opaque_from_multiversion_if_false(n);
if (opaque != nullptr) { break; }
}
} else {
opaque = find_multiversion_opaque_from_multiversion_if_false(slow_path);
}
assert(opaque != nullptr, "must have found multiversion opaque node");
if (opaque == nullptr) { return false; }
// We may still be delayed, if there were not yet any runtime-checks added
// for the multiversioning. We may never add any, and then this loop would
// fold away. So we wait until some runtime-checks are added, then we know
// that this loop will be reachable and it is worth optimizing further.
if (opaque->is_delayed_slow_loop()) { return false; }
// Clear away the "delayed" status, i.e. resume optimizations.
cl->set_no_multiversion();
cl->set_multiversion_slow_loop();
#ifndef PRODUCT
if (TraceLoopOpts) {
tty->print("Resume Optimizations ");
lpt->dump_head();
}
#endif
return true;
}
bool PhaseIdealLoop::has_control_dependencies_from_predicates(LoopNode* head) {
Node* entry = head->skip_strip_mined()->in(LoopNode::EntryControl);
const Predicates predicates(entry);
if (predicates.has_any()) {
assert(entry->is_IfProj(), "sanity - must be ifProj since there is at least one predicate");
if (entry->outcnt() > 1) {
// Bailout if there are predicates from which there are additional control dependencies (i.e. from loop
// entry 'entry') to previously partially peeled statements since this case is not handled and can lead
// to a wrong execution. Remove this bailout, once this is fixed.
return true;
}
}
return false;
}
#ifndef PRODUCT
void PhaseIdealLoop::trace_loop_unswitching_impossible(const LoopNode* original_head) {
if (TraceLoopUnswitching) {
tty->print_cr("Loop Unswitching \"%d %s\" not possible due to control dependencies",
original_head->_idx, original_head->Name());
}
}
void PhaseIdealLoop::trace_loop_unswitching_count(IdealLoopTree* loop, LoopNode* original_head) {
if (TraceLoopOpts) {
tty->print("Unswitch %d ", original_head->unswitch_count() + 1);
loop->dump_head();
}
}
void PhaseIdealLoop::trace_loop_unswitching_result(const UnswitchedLoopSelector& unswitched_loop_selector,
const LoopNode* original_head, const LoopNode* new_head) {
if (TraceLoopUnswitching) {
IfNode* unswitch_candidate = unswitched_loop_selector.unswitch_candidate();
IfNode* loop_selector = unswitched_loop_selector.loop_selector().selector();
tty->print_cr("Loop Unswitching:");
tty->print_cr("- Unswitch-Candidate-If: %d %s", unswitch_candidate->_idx, unswitch_candidate->Name());
tty->print_cr("- Loop-Selector-If: %d %s", loop_selector->_idx, loop_selector->Name());
tty->print_cr("- True-Path-Loop (=Orig): %d %s", original_head->_idx, original_head->Name());
tty->print_cr("- False-Path-Loop (=Clone): %d %s", new_head->_idx, new_head->Name());
}
}
void PhaseIdealLoop::trace_loop_multiversioning_result(const LoopSelector& loop_selector,
const LoopNode* original_head, const LoopNode* new_head) {
if (TraceLoopMultiversioning) {
IfNode* selector_if = loop_selector.selector();
tty->print_cr("Loop Multiversioning:");
tty->print_cr("- Loop-Selector-If: %d %s", selector_if->_idx, selector_if->Name());
tty->print_cr("- True-Path-Loop (=Orig / Fast): %d %s", original_head->_idx, original_head->Name());
tty->print_cr("- False-Path-Loop (=Clone / Slow): %d %s", new_head->_idx, new_head->Name());
}
}
#endif
// When unswitching a counted loop, we need to convert it back to a normal loop since it's not a proper pre, main or,
// post loop anymore after loop unswitching. We also lose the multiversion structure, with access to the multiversion_if.
void PhaseIdealLoop::revert_to_normal_loop(const LoopNode* loop_head) {
CountedLoopNode* cl = loop_head->isa_CountedLoop();
if (cl == nullptr) { return; }
if (!cl->is_normal_loop()) { cl->set_normal_loop(); }
if (cl->is_multiversion()) { cl->set_no_multiversion(); }
}
// Hoist invariant CheckCastPPNodes out of each unswitched loop version to the appropriate loop selector If projection.
void PhaseIdealLoop::hoist_invariant_check_casts(const IdealLoopTree* loop, const Node_List& old_new,
const UnswitchedLoopSelector& unswitched_loop_selector) {
IfNode* unswitch_candidate = unswitched_loop_selector.unswitch_candidate();
IfNode* loop_selector = unswitched_loop_selector.loop_selector().selector();
ResourceMark rm;
GrowableArray<CheckCastPPNode*> loop_invariant_check_casts;
for (DUIterator_Fast imax, i = unswitch_candidate->fast_outs(imax); i < imax; i++) {
IfProjNode* proj = unswitch_candidate->fast_out(i)->as_IfProj();
// Copy to a worklist for easier manipulation
for (DUIterator_Fast jmax, j = proj->fast_outs(jmax); j < jmax; j++) {
CheckCastPPNode* check_cast = proj->fast_out(j)->isa_CheckCastPP();
if (check_cast != nullptr && loop->is_invariant(check_cast->in(1))) {
loop_invariant_check_casts.push(check_cast);
}
}
IfProjNode* loop_selector_if_proj = loop_selector->proj_out(proj->_con)->as_IfProj();
while (loop_invariant_check_casts.length() > 0) {
CheckCastPPNode* cast = loop_invariant_check_casts.pop();
Node* cast_clone = cast->clone();
cast_clone->set_req(0, loop_selector_if_proj);
_igvn.replace_input_of(cast, 1, cast_clone);
register_new_node(cast_clone, loop_selector_if_proj);
// Same for the clone
Node* use_clone = old_new[cast->_idx];
_igvn.replace_input_of(use_clone, 1, cast_clone);
}
}
}
// Enable more optimizations possibilities in the next IGVN round.
void PhaseIdealLoop::add_unswitched_loop_version_bodies_to_igvn(IdealLoopTree* loop, const Node_List& old_new) {
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);
}
}
void PhaseIdealLoop::increment_unswitch_counts(LoopNode* original_head, LoopNode* new_head) {
const int unswitch_count = original_head->unswitch_count() + 1;
original_head->set_unswitch_count(unswitch_count);
new_head->set_unswitch_count(unswitch_count);
}