Skip to content

Commit 5f338e9

Browse files
committedNov 11, 2024
8342946: Replace predicate walking code in Loop Unrolling with a predicate visitor
Reviewed-by: roland, kvn
1 parent e1d684c commit 5f338e9

File tree

4 files changed

+174
-42
lines changed

4 files changed

+174
-42
lines changed
 

‎src/hotspot/share/opto/loopTransform.cpp

+11-39
Original file line numberDiff line numberDiff line change
@@ -1779,47 +1779,19 @@ bool IdealLoopTree::is_invariant(Node* n) const {
17791779

17801780
// Search the Assertion Predicates added by loop predication and/or range check elimination and update them according
17811781
// to the new stride.
1782-
void PhaseIdealLoop::update_main_loop_assertion_predicates(Node* ctrl, CountedLoopNode* loop_head, Node* init,
1783-
const int stride_con) {
1784-
Node* entry = ctrl;
1785-
Node* prev_proj = ctrl;
1786-
LoopNode* outer_loop_head = loop_head->skip_strip_mined();
1787-
IdealLoopTree* outer_loop = get_loop(outer_loop_head);
1782+
void PhaseIdealLoop::update_main_loop_assertion_predicates(CountedLoopNode* main_loop_head) {
1783+
Node* init = main_loop_head->init_trip();
17881784

17891785
// Compute the value of the loop induction variable at the end of the
17901786
// first iteration of the unrolled loop: init + new_stride_con - init_inc
1791-
int new_stride_con = stride_con * 2;
1792-
Node* max_value = _igvn.intcon(new_stride_con);
1793-
set_ctrl(max_value, C->root());
1794-
1795-
while (entry != nullptr && entry->is_Proj() && entry->in(0)->is_If()) {
1796-
IfNode* iff = entry->in(0)->as_If();
1797-
ProjNode* proj = iff->proj_out(1 - entry->as_Proj()->_con);
1798-
if (!proj->unique_ctrl_out()->is_Halt()) {
1799-
break;
1800-
}
1801-
Node* bol = iff->in(1);
1802-
if (bol->is_OpaqueTemplateAssertionPredicate()) {
1803-
assert(assertion_predicate_has_loop_opaque_node(iff), "must find OpaqueLoop* nodes");
1804-
// This is a Template Assertion Predicate for the initial or last access.
1805-
// Create an Initialized Assertion Predicates for it accordingly:
1806-
// - For the initial access a[init] (same as before)
1807-
// - For the last access a[init+new_stride-orig_stride] (with the new unroll stride)
1808-
prev_proj = create_initialized_assertion_predicate(iff, init, max_value, prev_proj);
1809-
} else if (bol->is_OpaqueInitializedAssertionPredicate()) {
1810-
// This is one of the two Initialized Assertion Predicates:
1811-
// - For the initial access a[init]
1812-
// - For the last access a[init+old_stride-orig_stride]
1813-
// We could keep the one for the initial access but we do not know which one we currently have here. Just kill both.
1814-
_igvn.replace_input_of(iff, 1, _igvn.intcon(1));
1815-
}
1816-
assert(!bol->is_OpaqueNotNull() || !loop_head->is_main_loop(), "OpaqueNotNull should not be at main loop");
1817-
entry = entry->in(0)->in(0);
1818-
}
1819-
if (prev_proj != ctrl) {
1820-
_igvn.replace_input_of(outer_loop_head, LoopNode::EntryControl, prev_proj);
1821-
set_idom(outer_loop_head, prev_proj, dom_depth(outer_loop_head));
1822-
}
1787+
int unrolled_stride_con = main_loop_head->stride_con() * 2;
1788+
Node* unrolled_stride = _igvn.intcon(unrolled_stride_con);
1789+
set_ctrl(unrolled_stride, C->root());
1790+
1791+
Node* loop_entry = main_loop_head->skip_strip_mined()->in(LoopNode::EntryControl);
1792+
PredicateIterator predicate_iterator(loop_entry);
1793+
UpdateStrideForAssertionPredicates update_stride_for_assertion_predicates(unrolled_stride, this);
1794+
predicate_iterator.for_each(update_stride_for_assertion_predicates);
18231795
}
18241796

18251797
// Source Loop: Cloned - peeled_loop_head
@@ -1936,7 +1908,7 @@ void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adj
19361908
assert(old_trip_count > 1 && (!adjust_min_trip || stride_p <=
19371909
MIN2<int>(max_jint / 2 - 2, MAX2(1<<3, Matcher::max_vector_size(T_BYTE)) * loop_head->unrolled_count())), "sanity");
19381910

1939-
update_main_loop_assertion_predicates(ctrl, loop_head, init, stride_con);
1911+
update_main_loop_assertion_predicates(loop_head);
19401912

19411913
// Adjust loop limit to keep valid iterations number after unroll.
19421914
// Use (limit - stride) instead of (((limit - init)/stride) & (-2))*stride

‎src/hotspot/share/opto/loopnode.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@ class PhaseIdealLoop : public PhaseTransform {
948948
private:
949949
DEBUG_ONLY(static void count_opaque_loop_nodes(Node* n, uint& init, uint& stride);)
950950
static void get_assertion_predicates(ParsePredicateSuccessProj* parse_predicate_proj, Unique_Node_List& list, bool get_opaque = false);
951-
void update_main_loop_assertion_predicates(Node* ctrl, CountedLoopNode* loop_head, Node* init, int stride_con);
951+
void update_main_loop_assertion_predicates(CountedLoopNode* main_loop_head);
952952
void initialize_assertion_predicates_for_peeled_loop(CountedLoopNode* peeled_loop_head,
953953
CountedLoopNode* remaining_loop_head,
954954
uint first_node_index_in_cloned_loop_body,

‎src/hotspot/share/opto/predicates.cpp

+128-2
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ void TemplateAssertionPredicate::rewire_loop_data_dependencies(IfTrueNode* targe
152152
}
153153
}
154154

155-
156155
// Template Assertion Predicates always have the dedicated OpaqueTemplateAssertionPredicate to identify them.
157156
bool TemplateAssertionPredicate::is_predicate(Node* node) {
158157
if (!may_be_assertion_predicate_if(node)) {
@@ -179,6 +178,24 @@ IfTrueNode* TemplateAssertionPredicate::clone_and_replace_init(Node* new_control
179178
return success_proj;
180179
}
181180

181+
// Replace the input to OpaqueLoopStrideNode with 'new_stride' and leave the other nodes unchanged.
182+
void TemplateAssertionPredicate::replace_opaque_stride_input(Node* new_stride, PhaseIterGVN& igvn) const {
183+
TemplateAssertionExpression expression(opaque_node());
184+
expression.replace_opaque_stride_input(new_stride, igvn);
185+
}
186+
187+
// Create a new Initialized Assertion Predicate from this template at 'new_control' and return the success projection
188+
// of the newly created Initialized Assertion Predicate.
189+
IfTrueNode* TemplateAssertionPredicate::initialize(PhaseIdealLoop* phase, Node* new_control) const {
190+
assert(phase->assertion_predicate_has_loop_opaque_node(head()),
191+
"must find OpaqueLoop* nodes for Template Assertion Predicate");
192+
InitializedAssertionPredicateCreator initialized_assertion_predicate(phase);
193+
IfTrueNode* success_proj = initialized_assertion_predicate.create_from_template(head(), new_control);
194+
assert(!phase->assertion_predicate_has_loop_opaque_node(success_proj->in(0)->as_If()),
195+
"Initialized Assertion Predicates do not have OpaqueLoop* nodes in the bool expression anymore");
196+
return success_proj;
197+
}
198+
182199
// Initialized Assertion Predicates always have the dedicated OpaqueInitiailizedAssertionPredicate node to identify
183200
// them.
184201
bool InitializedAssertionPredicate::is_predicate(Node* node) {
@@ -189,6 +206,12 @@ bool InitializedAssertionPredicate::is_predicate(Node* node) {
189206
return if_node->in(1)->is_OpaqueInitializedAssertionPredicate();
190207
}
191208

209+
void InitializedAssertionPredicate::kill(PhaseIdealLoop* phase) const {
210+
Node* true_con = phase->igvn().intcon(1);
211+
phase->set_ctrl(true_con, phase->C->root());
212+
phase->igvn().replace_input_of(_if_node, 1, true_con);
213+
}
214+
192215
#ifdef ASSERT
193216
// Check that the block has at most one Parse Predicate and that we only find Regular Predicate nodes (i.e. IfProj,
194217
// If, or RangeCheck nodes).
@@ -388,6 +411,63 @@ TemplateAssertionExpression::clone(const TransformStrategyForOpaqueLoopNodes& tr
388411
return opaque_node_clone->as_OpaqueTemplateAssertionPredicate();
389412
}
390413

414+
// This class is used to replace the input to OpaqueLoopStrideNode with a new node while leaving the other nodes
415+
// unchanged.
416+
class ReplaceOpaqueStrideInput : public StackObj {
417+
PhaseIterGVN& _igvn;
418+
Unique_Node_List _nodes_to_visit;
419+
420+
public:
421+
ReplaceOpaqueStrideInput(OpaqueTemplateAssertionPredicateNode* start_node, PhaseIterGVN& igvn) : _igvn(igvn) {
422+
_nodes_to_visit.push(start_node);
423+
}
424+
NONCOPYABLE(ReplaceOpaqueStrideInput);
425+
426+
void replace(Node* new_opaque_stride_input) {
427+
for (uint i = 0; i < _nodes_to_visit.size(); i++) {
428+
Node* next = _nodes_to_visit[i];
429+
for (uint j = 1; j < next->req(); j++) {
430+
Node* input = next->in(j);
431+
if (input->is_OpaqueLoopStride()) {
432+
assert(TemplateAssertionExpressionNode::is_maybe_in_expression(input), "must also pass node filter");
433+
_igvn.replace_input_of(input, 1, new_opaque_stride_input);
434+
} else if (TemplateAssertionExpressionNode::is_maybe_in_expression(input)) {
435+
_nodes_to_visit.push(input);
436+
}
437+
}
438+
}
439+
}
440+
};
441+
442+
// Replace the input to OpaqueLoopStrideNode with 'new_stride' and leave the other nodes unchanged.
443+
void TemplateAssertionExpression::replace_opaque_stride_input(Node* new_stride, PhaseIterGVN& igvn) {
444+
ReplaceOpaqueStrideInput replace_opaque_stride_input(_opaque_node, igvn);
445+
replace_opaque_stride_input.replace(new_stride);
446+
}
447+
448+
// The transformations of this class fold the OpaqueLoop* nodes by returning their inputs.
449+
class RemoveOpaqueLoopNodesStrategy : public TransformStrategyForOpaqueLoopNodes {
450+
public:
451+
Node* transform_opaque_init(OpaqueLoopInitNode* opaque_init) const override {
452+
return opaque_init->in(1);
453+
}
454+
455+
Node* transform_opaque_stride(OpaqueLoopStrideNode* opaque_stride) const override {
456+
return opaque_stride->in(1);
457+
}
458+
};
459+
460+
OpaqueInitializedAssertionPredicateNode*
461+
TemplateAssertionExpression::clone_and_fold_opaque_loop_nodes(Node* new_control, PhaseIdealLoop* phase) {
462+
RemoveOpaqueLoopNodesStrategy remove_opaque_loop_nodes_strategy;
463+
OpaqueTemplateAssertionPredicateNode* cloned_template_opaque = clone(remove_opaque_loop_nodes_strategy, new_control,
464+
phase);
465+
OpaqueInitializedAssertionPredicateNode* opaque_initialized_opaque =
466+
new OpaqueInitializedAssertionPredicateNode(cloned_template_opaque->in(1)->as_Bool(), phase->C);
467+
phase->register_new_node(opaque_initialized_opaque, new_control);
468+
return opaque_initialized_opaque;
469+
}
470+
391471
// Check if this node belongs a Template Assertion Expression (including OpaqueLoop* nodes).
392472
bool TemplateAssertionExpressionNode::is_in_expression(Node* node) {
393473
if (is_maybe_in_expression(node)) {
@@ -664,6 +744,19 @@ IfTrueNode* InitializedAssertionPredicateCreator::create_from_template(IfNode* t
664744
NOT_PRODUCT(COMMA template_assertion_predicate->assertion_predicate_type()));
665745
}
666746

747+
// Create a new Initialized Assertion Predicate from 'template_assertion_predicate' by cloning it but omitting the
748+
// OpaqueLoop*Notes (i.e. taking their inputs instead).
749+
IfTrueNode* InitializedAssertionPredicateCreator::create_from_template(IfNode* template_assertion_predicate,
750+
Node* new_control) {
751+
OpaqueTemplateAssertionPredicateNode* template_opaque =
752+
template_assertion_predicate->in(1)->as_OpaqueTemplateAssertionPredicate();
753+
TemplateAssertionExpression template_assertion_expression(template_opaque);
754+
OpaqueInitializedAssertionPredicateNode* assertion_expression =
755+
template_assertion_expression.clone_and_fold_opaque_loop_nodes(new_control, _phase);
756+
return create_control_nodes(new_control, template_assertion_predicate->Opcode(), assertion_expression
757+
NOT_PRODUCT(COMMA template_assertion_predicate->assertion_predicate_type()));
758+
}
759+
667760
// Create a new Initialized Assertion Predicate directly without a template.
668761
IfTrueNode* InitializedAssertionPredicateCreator::create(Node* operand, Node* new_control, const jint stride,
669762
const int scale, Node* offset, Node* range NOT_PRODUCT(COMMA
@@ -768,7 +861,7 @@ void CreateAssertionPredicatesVisitor::visit(const TemplateAssertionPredicate& t
768861

769862
// Create an Initialized Assertion Predicate from the provided Template Assertion Predicate.
770863
IfTrueNode* CreateAssertionPredicatesVisitor::initialize_from_template(
771-
const TemplateAssertionPredicate& template_assertion_predicate) const {
864+
const TemplateAssertionPredicate& template_assertion_predicate) const {
772865
IfNode* template_head = template_assertion_predicate.head();
773866
IfTrueNode* initialized_predicate = _phase->create_initialized_assertion_predicate(template_head, _init, _stride,
774867
_new_control);
@@ -783,3 +876,36 @@ IfTrueNode* CreateAssertionPredicatesVisitor::clone_template_and_replace_init_in
783876
_phase->register_new_node(opaque_init, _new_control);
784877
return template_assertion_predicate.clone_and_replace_init(_new_control, opaque_init, _phase);
785878
}
879+
880+
// Clone the Template Assertion Predicate and set a new input for the OpaqueLoopStrideNode.
881+
void UpdateStrideForAssertionPredicates::visit(const TemplateAssertionPredicate& template_assertion_predicate) {
882+
replace_opaque_stride_input(template_assertion_predicate);
883+
Node* template_tail_control_out = template_assertion_predicate.tail()->unique_ctrl_out();
884+
IfTrueNode* initialized_success_proj = initialize_from_updated_template(template_assertion_predicate);
885+
connect_initialized_assertion_predicate(template_tail_control_out, initialized_success_proj);
886+
}
887+
888+
// Replace the input to OpaqueLoopStrideNode with 'new_stride' and leave the other nodes unchanged.
889+
void UpdateStrideForAssertionPredicates::replace_opaque_stride_input(
890+
const TemplateAssertionPredicate& template_assertion_predicate) const {
891+
template_assertion_predicate.replace_opaque_stride_input(_new_stride, _phase->igvn());
892+
}
893+
894+
IfTrueNode* UpdateStrideForAssertionPredicates::initialize_from_updated_template(
895+
const TemplateAssertionPredicate& template_assertion_predicate) const {
896+
IfTrueNode* initialized_success_proj = template_assertion_predicate.initialize(_phase, template_assertion_predicate.tail());
897+
return initialized_success_proj;
898+
}
899+
900+
// The newly created Initialized Assertion Predicate can safely be inserted because this visitor is already visiting
901+
// the Template Assertion Predicate above this. So, we will not accidentally visit this again and kill it with the
902+
// visit() method for Initialized Assertion Predicates.
903+
void UpdateStrideForAssertionPredicates::connect_initialized_assertion_predicate(
904+
Node* new_control_out, IfTrueNode* initialized_success_proj) const {
905+
if (new_control_out->is_Loop()) {
906+
_phase->igvn().replace_input_of(new_control_out, LoopNode::EntryControl, initialized_success_proj);
907+
} else {
908+
_phase->igvn().replace_input_of(new_control_out, 0, initialized_success_proj);
909+
}
910+
_phase->set_idom(new_control_out, initialized_success_proj, _phase->dom_depth(new_control_out));
911+
}

‎src/hotspot/share/opto/predicates.hpp

+34
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,8 @@ class TemplateAssertionPredicate : public Predicate {
404404
}
405405

406406
IfTrueNode* clone_and_replace_init(Node* new_control, OpaqueLoopInitNode* new_opaque_init, PhaseIdealLoop* phase) const;
407+
void replace_opaque_stride_input(Node* new_stride, PhaseIterGVN& igvn) const;
408+
IfTrueNode* initialize(PhaseIdealLoop* phase, Node* new_control) const;
407409
void rewire_loop_data_dependencies(IfTrueNode* target_predicate, const NodeInLoopBody& data_in_loop_body,
408410
PhaseIdealLoop* phase) const;
409411
static bool is_predicate(Node* node);
@@ -434,6 +436,7 @@ class InitializedAssertionPredicate : public Predicate {
434436
return _success_proj;
435437
}
436438

439+
void kill(PhaseIdealLoop* phase) const;
437440
static bool is_predicate(Node* node);
438441
};
439442

@@ -461,6 +464,8 @@ class TemplateAssertionExpression : public StackObj {
461464
OpaqueTemplateAssertionPredicateNode* clone_and_replace_init(Node* new_init, Node* new_ctrl, PhaseIdealLoop* phase);
462465
OpaqueTemplateAssertionPredicateNode* clone_and_replace_init_and_stride(Node* new_control, Node* new_init,
463466
Node* new_stride, PhaseIdealLoop* phase);
467+
void replace_opaque_stride_input(Node* new_stride, PhaseIterGVN& igvn);
468+
OpaqueInitializedAssertionPredicateNode* clone_and_fold_opaque_loop_nodes(Node* new_ctrl, PhaseIdealLoop* phase);
464469
};
465470

466471
// Class to represent a node being part of a Template Assertion Expression. Note that this is not an IR node.
@@ -608,6 +613,7 @@ class InitializedAssertionPredicateCreator : public StackObj {
608613

609614
IfTrueNode* create_from_template(IfNode* template_assertion_predicate, Node* new_control, Node* new_init,
610615
Node* new_stride);
616+
IfTrueNode* create_from_template(IfNode* template_assertion_predicate, Node* new_control);
611617
IfTrueNode* create(Node* operand, Node* new_control, jint stride, int scale, Node* offset, Node* range
612618
NOT_PRODUCT(COMMA AssertionPredicateType assertion_predicate_type));
613619

@@ -1032,4 +1038,32 @@ class TemplateAssertionPredicateCollector : public PredicateVisitor {
10321038
}
10331039
};
10341040

1041+
// This visitor updates the stride for an Assertion Predicate during Loop Unrolling. The inputs to the OpaqueLoopStride
1042+
// nodes Template of Template Assertion Predicates are updated and new Initialized Assertion Predicates are created
1043+
// from the updated templates. The old Initialized Assertion Predicates are killed.
1044+
class UpdateStrideForAssertionPredicates : public PredicateVisitor {
1045+
Node* const _new_stride;
1046+
PhaseIdealLoop* const _phase;
1047+
1048+
void replace_opaque_stride_input(const TemplateAssertionPredicate& template_assertion_predicate) const;
1049+
IfTrueNode* initialize_from_updated_template(const TemplateAssertionPredicate& template_assertion_predicate) const;
1050+
void connect_initialized_assertion_predicate(Node* new_control_out, IfTrueNode* initialized_success_proj) const;
1051+
1052+
public:
1053+
UpdateStrideForAssertionPredicates(Node* const new_stride, PhaseIdealLoop* phase)
1054+
: _new_stride(new_stride),
1055+
_phase(phase) {}
1056+
NONCOPYABLE(UpdateStrideForAssertionPredicates);
1057+
1058+
using PredicateVisitor::visit;
1059+
1060+
void visit(const TemplateAssertionPredicate& template_assertion_predicate) override;
1061+
1062+
// Kill the old Initialized Assertion Predicates with old strides before unrolling. The new Initialized Assertion
1063+
// Predicates are inserted after the Template Assertion Predicate which ensures that we are not accidentally visiting
1064+
// and killing a newly created Initialized Assertion Predicate here.
1065+
void visit(const InitializedAssertionPredicate& initialized_assertion_predicate) override {
1066+
initialized_assertion_predicate.kill(_phase);
1067+
}
1068+
};
10351069
#endif // SHARE_OPTO_PREDICATES_HPP

0 commit comments

Comments
 (0)
Please sign in to comment.