8327109: Refactor data graph cloning used in create_new_if_for_predicate() into separate class

Reviewed-by: epeter, thartmann
This commit is contained in:
Christian Hagedorn 2024-03-13 13:58:47 +00:00
parent a4a5196351
commit 7d8561d56b
5 changed files with 103 additions and 65 deletions

View File

@ -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 // Update ctrl and control inputs of all data nodes starting from 'node' to 'new_ctrl' which have 'old_ctrl' as
// current ctrl. // current ctrl.
void PhaseIdealLoop::set_ctrl_of_nodes_with_same_ctrl(Node* node, ProjNode* old_ctrl, Node* new_ctrl) { void PhaseIdealLoop::set_ctrl_of_nodes_with_same_ctrl(Node* start_node, ProjNode* old_uncommon_proj,
Unique_Node_List nodes_with_same_ctrl = find_nodes_with_same_ctrl(node, old_ctrl); Node* new_uncommon_proj) {
for (uint j = 0; j < nodes_with_same_ctrl.size(); j++) { ResourceMark rm;
Node* next = nodes_with_same_ctrl[j]; const Unique_Node_List nodes_with_same_ctrl = find_nodes_with_same_ctrl(start_node, old_uncommon_proj);
if (next->in(0) == old_ctrl) { for (uint i = 0; i < nodes_with_same_ctrl.size(); i++) {
_igvn.replace_input_of(next, 0, new_ctrl); 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);
} }
} }
@ -242,61 +244,31 @@ Unique_Node_List PhaseIdealLoop::find_nodes_with_same_ctrl(Node* node, const Pro
return nodes_with_same_ctrl; 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 // Clone all data nodes with a ctrl to the old uncommon projection from `start_node' by following its inputs. Rewire the
// to 'new_ctrl'. Returns the clone of 'node'. // cloned nodes to the new uncommon projection. Returns the clone of the `start_node`.
Node* PhaseIdealLoop::clone_nodes_with_same_ctrl(Node* node, ProjNode* old_ctrl, Node* new_ctrl) { 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();) DEBUG_ONLY(uint last_idx = C->unique();)
Unique_Node_List nodes_with_same_ctrl = find_nodes_with_same_ctrl(node, old_ctrl); const Unique_Node_List nodes_with_same_ctrl = find_nodes_with_same_ctrl(start_node, old_uncommon_proj);
Dict old_new_mapping = clone_nodes(nodes_with_same_ctrl); // Cloned but not rewired, yet DataNodeGraph data_node_graph(nodes_with_same_ctrl, this);
rewire_cloned_nodes_to_ctrl(old_ctrl, new_ctrl, nodes_with_same_ctrl, old_new_mapping); const OrigToNewHashtable& orig_to_clone = data_node_graph.clone(new_uncommon_proj);
Node* clone_phi_input = static_cast<Node*>(old_new_mapping[node]); fix_cloned_data_node_controls(old_uncommon_proj, new_uncommon_proj, orig_to_clone);
assert(clone_phi_input != nullptr && clone_phi_input->_idx >= last_idx, "must exist and be a proper clone"); Node** cloned_node_ptr = orig_to_clone.get(start_node);
return clone_phi_input; 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. // All data nodes with a control input to the uncommon projection in the chain need to be rewired to the new uncommon
Dict PhaseIdealLoop::clone_nodes(const Node_List& list_to_clone) { // projection (could not only be the last data node in the chain but also, for example, a pinned DivNode within the chain).
Dict old_new_mapping(cmpkey, hashkey); void PhaseIdealLoop::fix_cloned_data_node_controls(const ProjNode* old_uncommon_proj, Node* new_uncommon_proj,
for (uint i = 0; i < list_to_clone.size(); i++) { const OrigToNewHashtable& orig_to_clone) {
Node* next = list_to_clone[i]; auto orig_clone_action = [&](Node* orig, Node* clone) {
Node* clone = next->clone(); if (orig->in(0) == old_uncommon_proj) {
_igvn.register_new_node_with_optimizer(clone); _igvn.replace_input_of(clone, 0, new_uncommon_proj);
old_new_mapping.Insert(next, clone); set_ctrl(clone, new_uncommon_proj);
}
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);
} }
rewire_inputs_of_clones_to_clones(new_ctrl, clone, old_new_mapping, next); };
} orig_to_clone.iterate_all(orig_clone_action);
}
// 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, IfProjNode* PhaseIdealLoop::clone_parse_predicate_to_unswitched_loop(ParsePredicateSuccessProj* parse_predicate_proj,

View File

@ -1343,13 +1343,11 @@ public:
private: private:
// Helper functions for create_new_if_for_predicate() // 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); 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); Node* clone_nodes_with_same_ctrl(Node* start_node, ProjNode* old_uncommon_proj, Node* new_uncommon_proj);
Dict clone_nodes(const Node_List& list_to_clone); void fix_cloned_data_node_controls(const ProjNode* orig, Node* new_uncommon_proj,
void rewire_cloned_nodes_to_ctrl(const ProjNode* old_ctrl, Node* new_ctrl, const Node_List& nodes_with_same_ctrl, const OrigToNewHashtable& orig_to_clone);
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);
bool has_dominating_loop_limit_check(Node* init_trip, Node* limit, jlong stride_con, BasicType iv_bt, bool has_dominating_loop_limit_check(Node* init_trip, Node* limit, jlong stride_con, BasicType iv_bt,
Node* loop_entry); Node* loop_entry);
@ -1882,4 +1880,42 @@ public:
float to(Node* n); 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;
OrigToNewHashtable _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_data_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 OrigToNewHashtable& clone(Node* new_ctrl) {
assert(_orig_to_new.number_of_entries() == 0, "should not call this method twice in a row");
clone_data_nodes(new_ctrl);
rewire_clones_to_cloned_inputs();
return _orig_to_new;
}
};
#endif // SHARE_OPTO_LOOPNODE_HPP #endif // SHARE_OPTO_LOOPNODE_HPP

View File

@ -4495,3 +4495,31 @@ void PhaseIdealLoop::move_unordered_reduction_out_of_loop(IdealLoopTree* loop) {
assert(phi->outcnt() == 1, "accumulator is the only use of phi"); assert(phi->outcnt() == 1, "accumulator is the only use of phi");
} }
} }
void DataNodeGraph::clone_data_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);
}
}
});
}

View File

@ -207,6 +207,8 @@ typedef Node** DUIterator_Fast;
typedef Node** DUIterator_Last; typedef Node** DUIterator_Last;
#endif #endif
typedef ResizeableResourceHashtable<Node*, Node*, AnyObj::RESOURCE_AREA, mtCompiler> OrigToNewHashtable;
// Node Sentinel // Node Sentinel
#define NodeSentinel (Node*)-1 #define NodeSentinel (Node*)-1

View File

@ -208,7 +208,7 @@ void ReplacedNodes::apply(Compile* C, Node* ctl) {
hash_table_size++; hash_table_size++;
} }
// Map from current node to cloned/replaced node // Map from current node to cloned/replaced node
ResizeableResourceHashtable<Node*, Node*, AnyObj::RESOURCE_AREA, mtCompiler> clones(hash_table_size, hash_table_size); OrigToNewHashtable clones(hash_table_size, hash_table_size);
// Record mapping from initial to improved nodes // Record mapping from initial to improved nodes
for (int i = 0; i < _replaced_nodes->length(); i++) { for (int i = 0; i < _replaced_nodes->length(); i++) {
ReplacedNode replaced = _replaced_nodes->at(i); ReplacedNode replaced = _replaced_nodes->at(i);