Skip to content

Commit f26e430

Browse files
committed
8327110: Refactor create_bool_from_template_assertion_predicate() to separate class and fix identical cloning cases used for Loop Unswitching and Split If
Reviewed-by: epeter, kvn
1 parent e5e21a8 commit f26e430

File tree

9 files changed

+378
-11
lines changed

9 files changed

+378
-11
lines changed

src/hotspot/share/opto/loopPredicate.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,12 +365,15 @@ void PhaseIdealLoop::get_assertion_predicates(Node* predicate, Unique_Node_List&
365365
// Clone an Assertion Predicate for an unswitched loop. OpaqueLoopInit and OpaqueLoopStride nodes are cloned and uncommon
366366
// traps are kept for the predicate (a Halt node is used later when creating pre/main/post loops and copying this cloned
367367
// predicate again).
368-
IfProjNode* PhaseIdealLoop::clone_assertion_predicate_for_unswitched_loops(Node* iff, IfProjNode* predicate,
368+
IfProjNode* PhaseIdealLoop::clone_assertion_predicate_for_unswitched_loops(IfNode* template_assertion_predicate,
369+
IfProjNode* predicate,
369370
Deoptimization::DeoptReason reason,
370371
ParsePredicateSuccessProj* parse_predicate_proj) {
371-
Node* bol = create_bool_from_template_assertion_predicate(iff, nullptr, nullptr, parse_predicate_proj);
372-
IfProjNode* if_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, iff->Opcode(), false);
373-
_igvn.replace_input_of(if_proj->in(0), 1, bol);
372+
TemplateAssertionPredicateExpression template_assertion_predicate_expression(
373+
template_assertion_predicate->in(1)->as_Opaque4());
374+
Opaque4Node* cloned_opaque4_node = template_assertion_predicate_expression.clone(parse_predicate_proj, this);
375+
IfProjNode* if_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, template_assertion_predicate->Opcode(), false);
376+
_igvn.replace_input_of(if_proj->in(0), 1, cloned_opaque4_node);
374377
_igvn.replace_input_of(parse_predicate_proj->in(0), 0, if_proj);
375378
set_idom(parse_predicate_proj->in(0), if_proj, dom_depth(if_proj));
376379
return if_proj;

src/hotspot/share/opto/loopnode.hpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "opto/cfgnode.hpp"
2929
#include "opto/multnode.hpp"
3030
#include "opto/phaseX.hpp"
31+
#include "opto/predicates.hpp"
3132
#include "opto/subnode.hpp"
3233
#include "opto/type.hpp"
3334
#include "utilities/checkedCast.hpp"
@@ -1659,7 +1660,7 @@ class PhaseIdealLoop : public PhaseTransform {
16591660
Deoptimization::DeoptReason reason, IfProjNode* old_predicate_proj,
16601661
ParsePredicateSuccessProj* fast_loop_parse_predicate_proj,
16611662
ParsePredicateSuccessProj* slow_loop_parse_predicate_proj);
1662-
IfProjNode* clone_assertion_predicate_for_unswitched_loops(Node* iff, IfProjNode* predicate,
1663+
IfProjNode* clone_assertion_predicate_for_unswitched_loops(IfNode* template_assertion_predicate, IfProjNode* predicate,
16631664
Deoptimization::DeoptReason reason,
16641665
ParsePredicateSuccessProj* parse_predicate_proj);
16651666
static void check_cloned_parse_predicate_for_unswitching(const Node* new_entry, bool is_fast_loop) PRODUCT_RETURN;
@@ -1673,6 +1674,12 @@ class PhaseIdealLoop : public PhaseTransform {
16731674
bool created_loop_node() { return _created_loop_node; }
16741675
void register_new_node(Node* n, Node* blk);
16751676

1677+
Node* clone_and_register(Node* n, Node* ctrl) {
1678+
n = n->clone();
1679+
register_new_node(n, ctrl);
1680+
return n;
1681+
}
1682+
16761683
#ifdef ASSERT
16771684
void dump_bad_graph(const char* msg, Node* n, Node* early, Node* LCA);
16781685
#endif
@@ -1906,7 +1913,10 @@ class DataNodeGraph : public StackObj {
19061913
private:
19071914
void clone(Node* node, Node* new_ctrl);
19081915
void clone_data_nodes(Node* new_ctrl);
1916+
void clone_data_nodes_and_transform_opaque_loop_nodes(const TransformStrategyForOpaqueLoopNodes& transform_strategy,
1917+
Node* new_ctrl);
19091918
void rewire_clones_to_cloned_inputs();
1919+
void transform_opaque_node(const TransformStrategyForOpaqueLoopNodes& transform_strategy, Node* node);
19101920

19111921
public:
19121922
// Clone the provided data node collection and rewire the clones in such a way to create an identical graph copy.
@@ -1917,5 +1927,18 @@ class DataNodeGraph : public StackObj {
19171927
rewire_clones_to_cloned_inputs();
19181928
return _orig_to_new;
19191929
}
1930+
1931+
// Create a copy of the data nodes provided to the constructor by doing the following:
1932+
// Clone all non-OpaqueLoop* nodes and rewire them to create an identical subgraph copy. For the OpaqueLoop* nodes,
1933+
// apply the provided transformation strategy and include the transformed node into the subgraph copy to get a complete
1934+
// "cloned-and-transformed" graph copy. For all newly cloned nodes (which could also be new OpaqueLoop* nodes), set
1935+
// `new_ctrl` as ctrl.
1936+
const OrigToNewHashtable& clone_with_opaque_loop_transform_strategy(
1937+
const TransformStrategyForOpaqueLoopNodes& transform_strategy,
1938+
Node* new_ctrl) {
1939+
clone_data_nodes_and_transform_opaque_loop_nodes(transform_strategy, new_ctrl);
1940+
rewire_clones_to_cloned_inputs();
1941+
return _orig_to_new;
1942+
}
19201943
};
19211944
#endif // SHARE_OPTO_LOOPNODE_HPP

src/hotspot/share/opto/loopopts.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4502,7 +4502,7 @@ void DataNodeGraph::clone_data_nodes(Node* new_ctrl) {
45024502
}
45034503
}
45044504

4505-
// Clone the given node and set it up properly. Set `new_ctrl` as ctrl.
4505+
// Clone the given node and set it up properly. Set 'new_ctrl' as ctrl.
45064506
void DataNodeGraph::clone(Node* node, Node* new_ctrl) {
45074507
Node* clone = node->clone();
45084508
_phase->igvn().register_new_node_with_optimizer(clone);
@@ -4523,3 +4523,30 @@ void DataNodeGraph::rewire_clones_to_cloned_inputs() {
45234523
}
45244524
});
45254525
}
4526+
4527+
// Clone all non-OpaqueLoop* nodes and apply the provided transformation strategy for OpaqueLoop* nodes.
4528+
// Set 'new_ctrl' as ctrl for all cloned non-OpaqueLoop* nodes.
4529+
void DataNodeGraph::clone_data_nodes_and_transform_opaque_loop_nodes(
4530+
const TransformStrategyForOpaqueLoopNodes& transform_strategy,
4531+
Node* new_ctrl) {
4532+
for (uint i = 0; i < _data_nodes.size(); i++) {
4533+
Node* data_node = _data_nodes[i];
4534+
if (data_node->is_Opaque1()) {
4535+
transform_opaque_node(transform_strategy, data_node);
4536+
} else {
4537+
clone(data_node, new_ctrl);
4538+
}
4539+
}
4540+
}
4541+
4542+
void DataNodeGraph::transform_opaque_node(const TransformStrategyForOpaqueLoopNodes& transform_strategy, Node* node) {
4543+
Node* transformed_node;
4544+
if (node->is_OpaqueLoopInit()) {
4545+
transformed_node = transform_strategy.transform_opaque_init(node->as_OpaqueLoopInit());
4546+
} else {
4547+
assert(node->is_OpaqueLoopStride(), "must be OpaqueLoopStrideNode");
4548+
transformed_node = transform_strategy.transform_opaque_stride(node->as_OpaqueLoopStride());
4549+
}
4550+
// Add an orig->new mapping to correctly update the inputs of the copied graph in rewire_clones_to_cloned_inputs().
4551+
_orig_to_new.put(node, transformed_node);
4552+
}

src/hotspot/share/opto/node.hpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ class NegNode;
134134
class NegVNode;
135135
class NeverBranchNode;
136136
class Opaque1Node;
137+
class OpaqueLoopInitNode;
138+
class OpaqueLoopStrideNode;
139+
class Opaque4Node;
137140
class OuterStripMinedLoopNode;
138141
class OuterStripMinedLoopEndNode;
139142
class Node;
@@ -786,9 +789,12 @@ class Node {
786789
DEFINE_CLASS_ID(ClearArray, Node, 14)
787790
DEFINE_CLASS_ID(Halt, Node, 15)
788791
DEFINE_CLASS_ID(Opaque1, Node, 16)
789-
DEFINE_CLASS_ID(Move, Node, 17)
790-
DEFINE_CLASS_ID(LShift, Node, 18)
791-
DEFINE_CLASS_ID(Neg, Node, 19)
792+
DEFINE_CLASS_ID(OpaqueLoopInit, Opaque1, 0)
793+
DEFINE_CLASS_ID(OpaqueLoopStride, Opaque1, 1)
794+
DEFINE_CLASS_ID(Opaque4, Node, 17)
795+
DEFINE_CLASS_ID(Move, Node, 18)
796+
DEFINE_CLASS_ID(LShift, Node, 19)
797+
DEFINE_CLASS_ID(Neg, Node, 20)
792798

793799
_max_classes = ClassMask_Neg
794800
};
@@ -955,6 +961,9 @@ class Node {
955961
DEFINE_CLASS_QUERY(NegV)
956962
DEFINE_CLASS_QUERY(NeverBranch)
957963
DEFINE_CLASS_QUERY(Opaque1)
964+
DEFINE_CLASS_QUERY(Opaque4)
965+
DEFINE_CLASS_QUERY(OpaqueLoopInit)
966+
DEFINE_CLASS_QUERY(OpaqueLoopStride)
958967
DEFINE_CLASS_QUERY(OuterStripMinedLoop)
959968
DEFINE_CLASS_QUERY(OuterStripMinedLoopEnd)
960969
DEFINE_CLASS_QUERY(Parm)

src/hotspot/share/opto/opaquenode.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,15 @@ class Opaque1Node : public Node {
6060
class OpaqueLoopInitNode : public Opaque1Node {
6161
public:
6262
OpaqueLoopInitNode(Compile* C, Node *n) : Opaque1Node(C, n) {
63+
init_class_id(Class_OpaqueLoopInit);
6364
}
6465
virtual int Opcode() const;
6566
};
6667

6768
class OpaqueLoopStrideNode : public Opaque1Node {
6869
public:
6970
OpaqueLoopStrideNode(Compile* C, Node *n) : Opaque1Node(C, n) {
71+
init_class_id(Class_OpaqueLoopStride);
7072
}
7173
virtual int Opcode() const;
7274
};
@@ -120,6 +122,7 @@ class Opaque3Node : public Node {
120122
class Opaque4Node : public Node {
121123
public:
122124
Opaque4Node(Compile* C, Node *tst, Node* final_tst) : Node(nullptr, tst, final_tst) {
125+
init_class_id(Class_Opaque4);
123126
init_flags(Flag_is_macro);
124127
C->add_macro_node(this);
125128
}

src/hotspot/share/opto/predicates.cpp

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
#include "precompiled.hpp"
2626
#include "opto/callnode.hpp"
27+
#include "opto/loopnode.hpp"
28+
#include "opto/node.hpp"
2729
#include "opto/predicates.hpp"
2830

2931
// Walk over all Initialized Assertion Predicates and return the entry into the first Initialized Assertion Predicate
@@ -147,3 +149,127 @@ Node* PredicateBlock::skip_regular_predicates(Node* regular_predicate_proj, Deop
147149
}
148150
return entry;
149151
}
152+
153+
// This strategy clones the OpaqueLoopInit and OpaqueLoopStride nodes.
154+
class CloneStrategy : public TransformStrategyForOpaqueLoopNodes {
155+
PhaseIdealLoop* const _phase;
156+
Node* const _new_ctrl;
157+
158+
public:
159+
CloneStrategy(PhaseIdealLoop* phase, Node* new_ctrl)
160+
: _phase(phase),
161+
_new_ctrl(new_ctrl) {}
162+
NONCOPYABLE(CloneStrategy);
163+
164+
Node* transform_opaque_init(OpaqueLoopInitNode* opaque_init) const override {
165+
return _phase->clone_and_register(opaque_init, _new_ctrl)->as_OpaqueLoopInit();
166+
}
167+
168+
Node* transform_opaque_stride(OpaqueLoopStrideNode* opaque_stride) const override {
169+
return _phase->clone_and_register(opaque_stride, _new_ctrl)->as_OpaqueLoopStride();
170+
}
171+
};
172+
173+
// Creates an identical clone of this Template Assertion Predicate Expression (i.e.cloning all nodes from the Opaque4Node
174+
// to and including the OpaqueLoop* nodes). The cloned nodes are rewired to reflect the same graph structure as found for
175+
// this Template Assertion Predicate Expression. The cloned nodes get 'new_ctrl' as ctrl. There is no other update done
176+
// for the cloned nodes. Return the newly cloned Opaque4Node.
177+
Opaque4Node* TemplateAssertionPredicateExpression::clone(Node* new_ctrl, PhaseIdealLoop* phase) {
178+
CloneStrategy clone_init_and_stride_strategy(phase, new_ctrl);
179+
return clone(clone_init_and_stride_strategy, new_ctrl, phase);
180+
}
181+
182+
// Class to collect data nodes from a source to target nodes by following the inputs of the source node recursively.
183+
// The class takes a node filter to decide which input nodes to follow and a target node predicate to start backtracking
184+
// from. All nodes found on all paths from source->target(s) are returned in a Unique_Node_List (without duplicates).
185+
class DataNodesOnPathsToTargets : public StackObj {
186+
typedef bool (*NodeCheck)(const Node*);
187+
188+
// Node filter function to decide if we should process a node or not while searching for targets.
189+
NodeCheck _node_filter;
190+
// Function to decide if a node is a target node (i.e. where we should start backtracking). This check should also
191+
// trivially pass the _node_filter.
192+
NodeCheck _is_target_node;
193+
// The resulting node collection of all nodes on paths from source->target(s).
194+
Unique_Node_List _collected_nodes;
195+
// List to track all nodes visited on the search for target nodes starting at a start node. These nodes are then used
196+
// in backtracking to find the nodes actually being on a start->target(s) path. This list also serves as visited set
197+
// to avoid double visits of a node which could happen with diamonds shapes.
198+
Unique_Node_List _nodes_to_visit;
199+
200+
public:
201+
DataNodesOnPathsToTargets(NodeCheck node_filter, NodeCheck is_target_node)
202+
: _node_filter(node_filter),
203+
_is_target_node(is_target_node) {}
204+
NONCOPYABLE(DataNodesOnPathsToTargets);
205+
206+
// Collect all input nodes from 'start_node'->target(s) by applying the node filter to discover new input nodes and
207+
// the target node predicate to stop discovering more inputs and start backtracking. The implementation is done
208+
// with two BFS traversal: One to collect the target nodes (if any) and one to backtrack from the target nodes to
209+
// find all other nodes on the start->target(s) paths.
210+
const Unique_Node_List& collect(Node* start_node) {
211+
assert(_collected_nodes.size() == 0 && _nodes_to_visit.size() == 0, "should not call this method twice in a row");
212+
assert(!_is_target_node(start_node), "no trivial paths where start node is also a target node");
213+
214+
collect_target_nodes(start_node);
215+
backtrack_from_target_nodes();
216+
assert(_collected_nodes.size() == 0 || _collected_nodes.member(start_node),
217+
"either target node predicate was never true or must find start node again when doing backtracking work");
218+
return _collected_nodes;
219+
}
220+
221+
private:
222+
// Do a BFS from the start_node to collect all target nodes. We can then do another BFS from the target nodes to
223+
// find all nodes on the paths from start->target(s).
224+
// Note: We could do a single DFS pass to search targets and backtrack in one walk. But this is much more complex.
225+
// Given that the typical Template Assertion Predicate Expression only consists of a few nodes, we aim for
226+
// simplicity here.
227+
void collect_target_nodes(Node* start_node) {
228+
_nodes_to_visit.push(start_node);
229+
for (uint i = 0; i < _nodes_to_visit.size(); i++) {
230+
Node* next = _nodes_to_visit[i];
231+
for (uint j = 1; j < next->req(); j++) {
232+
Node* input = next->in(j);
233+
if (_is_target_node(input)) {
234+
assert(_node_filter(input), "must also pass node filter");
235+
_collected_nodes.push(input);
236+
} else if (_node_filter(input)) {
237+
_nodes_to_visit.push(input);
238+
}
239+
}
240+
}
241+
}
242+
243+
// Backtrack from all previously collected target nodes by using the visited set of the start->target(s) search. If no
244+
// node was collected in the first place (i.e. target node predicate was never true), then nothing needs to be done.
245+
void backtrack_from_target_nodes() {
246+
for (uint i = 0; i < _collected_nodes.size(); i++) {
247+
Node* node_on_path = _collected_nodes[i];
248+
for (DUIterator_Fast jmax, j = node_on_path->fast_outs(jmax); j < jmax; j++) {
249+
Node* use = node_on_path->fast_out(j);
250+
if (_nodes_to_visit.member(use)) {
251+
// use must be on a path from start->target(s) because it was also visited in the first BFS starting from
252+
// the start node.
253+
_collected_nodes.push(use);
254+
}
255+
}
256+
}
257+
}
258+
};
259+
260+
// Clones this Template Assertion Predicate Expression and applies the given strategy to transform the OpaqueLoop* nodes.
261+
Opaque4Node* TemplateAssertionPredicateExpression::clone(const TransformStrategyForOpaqueLoopNodes& transform_strategy,
262+
Node* new_ctrl, PhaseIdealLoop* phase) {
263+
ResourceMark rm;
264+
auto is_opaque_loop_node = [](const Node* node) {
265+
return node->is_Opaque1();
266+
};
267+
DataNodesOnPathsToTargets data_nodes_on_path_to_targets(TemplateAssertionPredicateExpression::maybe_contains,
268+
is_opaque_loop_node);
269+
const Unique_Node_List& collected_nodes = data_nodes_on_path_to_targets.collect(_opaque4_node);
270+
DataNodeGraph data_node_graph(collected_nodes, phase);
271+
const OrigToNewHashtable& orig_to_new = data_node_graph.clone_with_opaque_loop_transform_strategy(transform_strategy, new_ctrl);
272+
assert(orig_to_new.contains(_opaque4_node), "must exist");
273+
Node* opaque4_clone = *orig_to_new.get(_opaque4_node);
274+
return opaque4_clone->as_Opaque4();
275+
}

src/hotspot/share/opto/predicates.hpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#define SHARE_OPTO_PREDICATES_HPP
2727

2828
#include "opto/cfgnode.hpp"
29+
#include "opto/opaquenode.hpp"
2930

3031
/*
3132
* There are different kinds of predicates throughout the code. We differentiate between the following predicates:
@@ -263,6 +264,51 @@ class RuntimePredicate : public StackObj {
263264
static bool is_success_proj(Node* node, Deoptimization::DeoptReason deopt_reason);
264265
};
265266

267+
// Interface to transform OpaqueLoopInit and OpaqueLoopStride nodes of a Template Assertion Predicate Expression.
268+
class TransformStrategyForOpaqueLoopNodes : public StackObj {
269+
public:
270+
virtual Node* transform_opaque_init(OpaqueLoopInitNode* opaque_init) const = 0;
271+
virtual Node* transform_opaque_stride(OpaqueLoopStrideNode* opaque_stride) const = 0;
272+
};
273+
274+
// A Template Assertion Predicate Expression represents the Opaque4Node for the initial value or the last value of a
275+
// Template Assertion Predicate and all the nodes up to and including the OpaqueLoop* nodes.
276+
class TemplateAssertionPredicateExpression : public StackObj {
277+
Opaque4Node* _opaque4_node;
278+
279+
public:
280+
explicit TemplateAssertionPredicateExpression(Opaque4Node* opaque4_node) : _opaque4_node(opaque4_node) {}
281+
282+
private:
283+
Opaque4Node* clone(const TransformStrategyForOpaqueLoopNodes& transform_strategy, Node* new_ctrl, PhaseIdealLoop* phase);
284+
285+
public:
286+
// Is 'n' a node that could be part of a Template Assertion Predicate Expression (i.e. could be found on the input
287+
// chain of a Template Assertion Predicate Opaque4Node up to and including the OpaqueLoop* nodes)?
288+
static bool maybe_contains(const Node* n) {
289+
const int opcode = n->Opcode();
290+
return (opcode == Op_OpaqueLoopInit ||
291+
opcode == Op_OpaqueLoopStride ||
292+
n->is_Bool() ||
293+
n->is_Cmp() ||
294+
opcode == Op_AndL ||
295+
opcode == Op_OrL ||
296+
opcode == Op_RShiftL ||
297+
opcode == Op_LShiftL ||
298+
opcode == Op_LShiftI ||
299+
opcode == Op_AddL ||
300+
opcode == Op_AddI ||
301+
opcode == Op_MulL ||
302+
opcode == Op_MulI ||
303+
opcode == Op_SubL ||
304+
opcode == Op_SubI ||
305+
opcode == Op_ConvI2L ||
306+
opcode == Op_CastII);
307+
}
308+
309+
Opaque4Node* clone(Node* new_ctrl, PhaseIdealLoop* phase);
310+
};
311+
266312
// This class represents a Predicate Block (i.e. either a Loop Predicate Block, a Profiled Loop Predicate Block,
267313
// or a Loop Limit Check Predicate Block). It contains zero or more Regular Predicates followed by a Parse Predicate
268314
// which, however, does not need to exist (we could already have decided to remove Parse Predicates for this loop).

src/hotspot/share/opto/split_if.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "opto/movenode.hpp"
3131
#include "opto/node.hpp"
3232
#include "opto/opaquenode.hpp"
33+
#include "opto/predicates.hpp"
3334

3435
//------------------------------split_thru_region------------------------------
3536
// Split Node 'n' through merge point.
@@ -101,8 +102,9 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) {
101102
Node* m = wq.at(i);
102103
if (m->is_If()) {
103104
assert(assertion_predicate_has_loop_opaque_node(m->as_If()), "opaque node not reachable from if?");
104-
Node* bol = create_bool_from_template_assertion_predicate(m, nullptr, nullptr, m->in(0));
105-
_igvn.replace_input_of(m, 1, bol);
105+
TemplateAssertionPredicateExpression template_assertion_predicate_expression(m->in(1)->as_Opaque4());
106+
Opaque4Node* cloned_opaque4_node = template_assertion_predicate_expression.clone(m->in(0), this);
107+
_igvn.replace_input_of(m, 1, cloned_opaque4_node);
106108
} else {
107109
assert(!m->is_CFG(), "not CFG expected");
108110
for (DUIterator_Fast jmax, j = m->fast_outs(jmax); j < jmax; j++) {

0 commit comments

Comments
 (0)