Skip to content

Commit

Permalink
8327109: Refactor data graph cloning used for in create_new_if_for_pr…
Browse files Browse the repository at this point in the history
…edicate() into separate class
  • Loading branch information
chhagedorn committed Mar 1, 2024
1 parent 8f6edd8 commit c00cc8f
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 64 deletions.
88 changes: 30 additions & 58 deletions src/hotspot/share/opto/loopPredicate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,14 +215,16 @@ IfProjNode* PhaseIdealLoop::create_new_if_for_predicate(ParsePredicateSuccessPro

// Update ctrl and control inputs of all data nodes starting from 'node' to 'new_ctrl' which have 'old_ctrl' as
// current ctrl.
void PhaseIdealLoop::set_ctrl_of_nodes_with_same_ctrl(Node* node, ProjNode* old_ctrl, Node* new_ctrl) {
Unique_Node_List nodes_with_same_ctrl = find_nodes_with_same_ctrl(node, old_ctrl);
for (uint j = 0; j < nodes_with_same_ctrl.size(); j++) {
Node* next = nodes_with_same_ctrl[j];
if (next->in(0) == old_ctrl) {
_igvn.replace_input_of(next, 0, new_ctrl);
void PhaseIdealLoop::set_ctrl_of_nodes_with_same_ctrl(Node* start_node, ProjNode* old_uncommon_proj,
Node* new_uncommon_proj) {
ResourceMark rm;
const Unique_Node_List nodes_with_same_ctrl = find_nodes_with_same_ctrl(start_node, old_uncommon_proj);
for (uint i = 0; i < nodes_with_same_ctrl.size(); i++) {
Node* node = nodes_with_same_ctrl[i];
if (node->in(0) == old_uncommon_proj) {
_igvn.replace_input_of(node, 0, new_uncommon_proj);
}
set_ctrl(next, new_ctrl);
set_ctrl(node, new_uncommon_proj);
}
}

Expand All @@ -242,61 +244,31 @@ Unique_Node_List PhaseIdealLoop::find_nodes_with_same_ctrl(Node* node, const Pro
return nodes_with_same_ctrl;
}

// Clone all nodes with the same ctrl as 'old_ctrl' starting from 'node' by following its inputs. Rewire the cloned nodes
// to 'new_ctrl'. Returns the clone of 'node'.
Node* PhaseIdealLoop::clone_nodes_with_same_ctrl(Node* node, ProjNode* old_ctrl, Node* new_ctrl) {
// Clone all data nodes with a ctrl to the old uncommon projection from `start_node' by following its inputs. Rewire the
// cloned nodes to the new uncommon projection. Returns the clone of the `start_node`.
Node* PhaseIdealLoop::clone_nodes_with_same_ctrl(Node* start_node, ProjNode* old_uncommon_proj, Node* new_uncommon_proj) {
ResourceMark rm;
DEBUG_ONLY(uint last_idx = C->unique();)
Unique_Node_List nodes_with_same_ctrl = find_nodes_with_same_ctrl(node, old_ctrl);
Dict old_new_mapping = clone_nodes(nodes_with_same_ctrl); // Cloned but not rewired, yet
rewire_cloned_nodes_to_ctrl(old_ctrl, new_ctrl, nodes_with_same_ctrl, old_new_mapping);
Node* clone_phi_input = static_cast<Node*>(old_new_mapping[node]);
assert(clone_phi_input != nullptr && clone_phi_input->_idx >= last_idx, "must exist and be a proper clone");
return clone_phi_input;
const Unique_Node_List nodes_with_same_ctrl = find_nodes_with_same_ctrl(start_node, old_uncommon_proj);
DataNodeGraph data_node_graph(nodes_with_same_ctrl, this);
auto& orig_to_new = data_node_graph.clone(new_uncommon_proj);
fix_cloned_data_node_controls(old_uncommon_proj, new_uncommon_proj, orig_to_new);
Node** cloned_node_ptr = orig_to_new.get(start_node);
assert(cloned_node_ptr != nullptr && (*cloned_node_ptr)->_idx >= last_idx, "must exist and be a proper clone");
return *cloned_node_ptr;
}

// Clone all the nodes on 'list_to_clone' and return an old->new mapping.
Dict PhaseIdealLoop::clone_nodes(const Node_List& list_to_clone) {
Dict old_new_mapping(cmpkey, hashkey);
for (uint i = 0; i < list_to_clone.size(); i++) {
Node* next = list_to_clone[i];
Node* clone = next->clone();
_igvn.register_new_node_with_optimizer(clone);
old_new_mapping.Insert(next, clone);
}
return old_new_mapping;
}

// Rewire inputs of the unprocessed cloned nodes (inputs are not updated, yet, and still point to the old nodes) by
// using the old_new_mapping.
void PhaseIdealLoop::rewire_cloned_nodes_to_ctrl(const ProjNode* old_ctrl, Node* new_ctrl,
const Node_List& nodes_with_same_ctrl, const Dict& old_new_mapping) {
for (uint i = 0; i < nodes_with_same_ctrl.size(); i++) {
Node* next = nodes_with_same_ctrl[i];
Node* clone = static_cast<Node*>(old_new_mapping[next]);
if (next->in(0) == old_ctrl) {
// All data nodes with a control input to the uncommon projection in the chain need to be rewired to the new uncommon
// projection (could not only be the last data node in the chain but also, for example, a DivNode within the chain).
_igvn.replace_input_of(clone, 0, new_ctrl);
set_ctrl(clone, new_ctrl);
// All data nodes with a control input to the uncommon projection in the chain need to be rewired to the new uncommon
// projection (could not only be the last data node in the chain but also, for example, a pinned DivNode within the chain).
void PhaseIdealLoop::fix_cloned_data_node_controls(
const ProjNode* old_uncommon_proj, Node* new_uncommon_proj,
const ResizeableResourceHashtable<Node*, Node*, AnyObj::RESOURCE_AREA, mtCompiler>& orig_to_new) {
orig_to_new.iterate_all([&](Node* node, Node* clone) {
if (node->in(0) == old_uncommon_proj) {
_igvn.replace_input_of(clone, 0, new_uncommon_proj);
set_ctrl(clone, new_uncommon_proj);
}
rewire_inputs_of_clones_to_clones(new_ctrl, clone, old_new_mapping, next);
}
}

// Rewire the inputs of the cloned nodes to the old nodes to the new clones.
void PhaseIdealLoop::rewire_inputs_of_clones_to_clones(Node* new_ctrl, Node* clone, const Dict& old_new_mapping,
const Node* next) {
for (uint i = 1; i < next->req(); i++) {
Node* in = next->in(i);
if (!in->is_Phi()) {
assert(!in->is_CFG(), "must be data node");
Node* in_clone = static_cast<Node*>(old_new_mapping[in]);
if (in_clone != nullptr) {
_igvn.replace_input_of(clone, i, in_clone);
set_ctrl(clone, new_ctrl);
}
}
}
});
}

IfProjNode* PhaseIdealLoop::clone_parse_predicate_to_unswitched_loop(ParsePredicateSuccessProj* parse_predicate_proj,
Expand Down
50 changes: 44 additions & 6 deletions src/hotspot/share/opto/loopnode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
class CmpNode;
class BaseCountedLoopEndNode;
class CountedLoopNode;
class DataInputGraph;
class IdealLoopTree;
class LoopNode;
class Node;
Expand Down Expand Up @@ -1343,13 +1344,13 @@ class PhaseIdealLoop : public PhaseTransform {

private:
// Helper functions for create_new_if_for_predicate()
void set_ctrl_of_nodes_with_same_ctrl(Node* node, ProjNode* old_ctrl, Node* new_ctrl);
void set_ctrl_of_nodes_with_same_ctrl(Node* start_node, ProjNode* old_uncommon_proj, Node* new_uncommon_proj);
Unique_Node_List find_nodes_with_same_ctrl(Node* node, const ProjNode* ctrl);
Node* clone_nodes_with_same_ctrl(Node* node, ProjNode* old_ctrl, Node* new_ctrl);
Dict clone_nodes(const Node_List& list_to_clone);
void rewire_cloned_nodes_to_ctrl(const ProjNode* old_ctrl, Node* new_ctrl, const Node_List& nodes_with_same_ctrl,
const Dict& old_new_mapping);
void rewire_inputs_of_clones_to_clones(Node* new_ctrl, Node* clone, const Dict& old_new_mapping, const Node* next);
const Unique_Node_List& find_nodes_with_same_ctrl(DataInputGraph& data_input_graph, const ProjNode* ctrl);
Node* clone_nodes_with_same_ctrl(Node* start_node, ProjNode* old_uncommon_proj, Node* new_uncommon_proj);
void fix_cloned_data_node_controls(
const ProjNode* old_uncommon_proj, Node* new_uncommon_proj,
const ResizeableResourceHashtable<Node*, Node*, AnyObj::RESOURCE_AREA, mtCompiler>& orig_to_new);
bool has_dominating_loop_limit_check(Node* init_trip, Node* limit, jlong stride_con, BasicType iv_bt,
Node* loop_entry);

Expand Down Expand Up @@ -1882,4 +1883,41 @@ class PathFrequency {
float to(Node* n);
};

// Class to clone a data node graph by taking a list of data nodes. This is done in 2 steps:
// 1. Clone the data nodes
// 2. Fix the cloned data inputs pointing to the old nodes to the cloned inputs by using an old->new mapping.
class DataNodeGraph : public StackObj {
PhaseIdealLoop* const _phase;
const Unique_Node_List& _data_nodes;
ResizeableResourceHashtable<Node*, Node*, AnyObj::RESOURCE_AREA, mtCompiler> _orig_to_new;

public:
DataNodeGraph(const Unique_Node_List& data_nodes, PhaseIdealLoop* phase)
: _phase(phase),
_data_nodes(data_nodes),
// Use 107 as best guess which is the first resize value in ResizeableResourceHashtable::large_table_sizes.
_orig_to_new(107, MaxNodeLimit)
{
#ifdef ASSERT
for (uint i = 0; i < data_nodes.size(); i++) {
assert(!data_nodes[i]->is_CFG(), "only data nodes");
}
#endif
}
NONCOPYABLE(DataNodeGraph);

private:
void clone(Node* node, Node* new_ctrl);
void clone_nodes(Node* new_ctrl);
void rewire_clones_to_cloned_inputs();

public:
// Clone the provided data node collection and rewire the clones in such a way to create an identical graph copy.
// Set `new_ctrl` as ctrl for the cloned nodes.
const ResizeableResourceHashtable<Node*, Node*, AnyObj::RESOURCE_AREA, mtCompiler>& clone(Node* new_ctrl) {
clone_nodes(new_ctrl);
rewire_clones_to_cloned_inputs();
return _orig_to_new;
}
};
#endif // SHARE_OPTO_LOOPNODE_HPP
29 changes: 29 additions & 0 deletions src/hotspot/share/opto/loopopts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4495,3 +4495,32 @@ void PhaseIdealLoop::move_unordered_reduction_out_of_loop(IdealLoopTree* loop) {
assert(phi->outcnt() == 1, "accumulator is the only use of phi");
}
}

// Clone all nodes in _data_nodes.
void DataNodeGraph::clone_nodes(Node* new_ctrl) {
for (uint i = 0; i < _data_nodes.size(); i++) {
clone(_data_nodes[i], new_ctrl);
}
}

// Clone the given node and set it up properly. Set `new_ctrl` as ctrl.
void DataNodeGraph::clone(Node* node, Node* new_ctrl) {
Node* clone = node->clone();
_phase->igvn().register_new_node_with_optimizer(clone);
_orig_to_new.put(node, clone);
_phase->set_ctrl(clone, new_ctrl);
}

// Rewire the data inputs of all (unprocessed) cloned nodes, whose inputs are still pointing to the same inputs as their
// corresponding orig nodes, to the newly cloned inputs to create a separate cloned graph.
void DataNodeGraph::rewire_clones_to_cloned_inputs() {
_orig_to_new.iterate_all([&](Node* node, Node* clone) {
for (uint i = 1; i < node->req(); i++) {
Node** cloned_input = _orig_to_new.get(node->in(i));
if (cloned_input != nullptr) {
// Input was also cloned -> rewire clone to the cloned input.
_phase->igvn().replace_input_of(clone, i, *cloned_input);
}
}
});
}

0 comments on commit c00cc8f

Please sign in to comment.