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
// 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);
}
}
@ -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);
const OrigToNewHashtable& orig_to_clone = data_node_graph.clone(new_uncommon_proj);
fix_cloned_data_node_controls(old_uncommon_proj, new_uncommon_proj, orig_to_clone);
Node** cloned_node_ptr = orig_to_clone.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 OrigToNewHashtable& orig_to_clone) {
auto orig_clone_action = [&](Node* orig, Node* clone) {
if (orig->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);
}
}
}
};
orig_to_clone.iterate_all(orig_clone_action);
}
IfProjNode* PhaseIdealLoop::clone_parse_predicate_to_unswitched_loop(ParsePredicateSuccessProj* parse_predicate_proj,

View File

@ -1343,13 +1343,11 @@ public:
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);
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* orig, Node* new_uncommon_proj,
const OrigToNewHashtable& orig_to_clone);
bool has_dominating_loop_limit_check(Node* init_trip, Node* limit, jlong stride_con, BasicType iv_bt,
Node* loop_entry);
@ -1882,4 +1880,42 @@ public:
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

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");
}
}
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;
#endif
typedef ResizeableResourceHashtable<Node*, Node*, AnyObj::RESOURCE_AREA, mtCompiler> OrigToNewHashtable;
// Node Sentinel
#define NodeSentinel (Node*)-1

View File

@ -208,7 +208,7 @@ void ReplacedNodes::apply(Compile* C, Node* ctl) {
hash_table_size++;
}
// 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
for (int i = 0; i < _replaced_nodes->length(); i++) {
ReplacedNode replaced = _replaced_nodes->at(i);