Skip to content

Commit

Permalink
Generalize BFS
Browse files Browse the repository at this point in the history
  • Loading branch information
chhagedorn committed Nov 25, 2024
1 parent 5ae3a4f commit fbde591
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 28 deletions.
28 changes: 28 additions & 0 deletions src/hotspot/share/opto/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3029,3 +3029,31 @@ const Type* TypeNode::Value(PhaseGVN* phase) const { return _type; }
uint TypeNode::ideal_reg() const {
return _type->ideal_reg();
}

// Run the BFS starting from 'start_node' and apply the actions provided to this class.
void CustomNodeInputsBFS::run(Node* start_node) {
assert(_bfs_visit_input_strategy.should_visit_input(start_node), "start node must pass the visiting strategy");
ResourceMark rm;
Unique_Node_List nodes_to_visit;
nodes_to_visit.push(start_node);
for (uint i = 0; i < nodes_to_visit.size(); i++) {
Node* next = nodes_to_visit[i];
visit_inputs_of(next, nodes_to_visit);
}
}

void CustomNodeInputsBFS::visit_inputs_of(const Node* node, Unique_Node_List& nodes_to_visit) {
for (uint i = _bfs_visit_input_strategy.start_input_index(node); i < node->req(); i++) {
Node* input = node->in(i);
if (!_bfs_visit_input_strategy.should_visit_input(input)) {
// Different *NodeInputsBFS classes can define different strategies
continue;
}
if (_bfs_actions.is_target_node(input)) {
assert(_bfs_actions.should_visit(input), "must also pass node filter");
_bfs_actions.target_node_action(input);
} else if (_bfs_actions.should_visit(input)) {
nodes_to_visit.push(input);
}
}
}
85 changes: 63 additions & 22 deletions src/hotspot/share/opto/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2111,8 +2111,8 @@ inline int Op_DivModIL(BasicType bt, bool is_unsigned) {
}
}

// Interface to define actions that should be taken when running DataNodeBFS. Each use can extend this class to specify
// a customized BFS.
// Interface to define actions that should be taken when running a *NodeInputsBFS. Each use can extend this class to
// specify a customized BFS.
class BFSActions : public StackObj {
public:
// Should a node's inputs further be visited in the BFS traversal? By default, we visit all data inputs. Override this
Expand All @@ -2130,31 +2130,72 @@ class BFSActions : public StackObj {
virtual void target_node_action(Node* target_node) = 0;
};

// Class to perform a BFS traversal on the data nodes from a given start node. The provided BFSActions guide which
// data node's inputs should be further visited, which data nodes are target nodes and what to do with the target nodes.
class DataNodeBFS : public StackObj {
// Interface to define the node input visiting strategy. This interface is only used for CustomNodeInputsBFS and their
// users.
class BFSInputVisitStrategy : public StackObj {
public:
// Should this input be visited? This method can fundamentally restrict a BFS, for example, if only data nodes should
// be visited.
virtual bool should_visit_input(const Node* input) const = 0;

// This method returns the starting input index that should be visited when processing 'node'.
// Example implementations:
// - Only visiting data nodes? Return the constant index 1.
// - Visiting CFG nodes? Return index 0 but if 'node' is a Region, we should start at index 1.
virtual uint start_input_index(const Node* node) const = 0;
};

// Generic and customizable class to perform a BFS traversal on input nodes, dictated by the given BFSInputVisitStrategy,
// from a given start node. The provided BFSActions guide which of a visited node's inputs should be further visited,
// which nodes are target nodes and what to do with the target nodes.
//
// A user to traverse the node inputs of a graph is encouraged to first have a look at the common use cases covered by
// the specialized *NodeInputsBFS classes found below that use this class internally. For example, DataNodeInputsBFS
// only traverses data node inputs.
class CustomNodeInputsBFS : public StackObj {
// Strategy defined by different *NodeInputsBFS classes.
const BFSInputVisitStrategy& _bfs_visit_input_strategy;

// BFS actions defined by a user of a *NodeInputsBFS class.
BFSActions& _bfs_actions;

void visit_inputs_of(const Node* node, Unique_Node_List& nodes_to_visit);

public:
CustomNodeInputsBFS(const BFSInputVisitStrategy& bfs_input_visit_strategy, BFSActions& bfs_actions)
: _bfs_visit_input_strategy(bfs_input_visit_strategy),
_bfs_actions(bfs_actions) {}

void run(Node* start_node);
};

// This class defines a data node visiting strategy for CustomNodeInputsBFS.
class BFSDataNodeInputVisitStrategy : public BFSInputVisitStrategy {
public:
explicit DataNodeBFS(BFSActions& bfs_action) : _bfs_actions(bfs_action) {}
// Only visit data nodes.
bool should_visit_input(const Node* input) const override {
return !input->is_CFG();
}

// Data node inputs start at index 1 for data nodes.
uint start_input_index(const Node* node) const override {
return 1;
}
};

// Class to perform a BFS traversal on the data input nodes from a given start data node. The provided BFSActions guide
// which of a data node's inputs should be further visited, which data nodes are target nodes and what to do with the
// target nodes.
class DataNodeInputsBFS : public StackObj {
const BFSDataNodeInputVisitStrategy _bfs_data_node_input_visit_strategy;
CustomNodeInputsBFS _custom_node_inputs_bfs;

public:
explicit DataNodeInputsBFS(BFSActions& bfs_actions)
: _custom_node_inputs_bfs(_bfs_data_node_input_visit_strategy, bfs_actions) {}

// Run the BFS starting from 'start_node' and apply the actions provided to this class.
void run(Node* start_node) {
ResourceMark rm;
Unique_Node_List _nodes_to_visit;
_nodes_to_visit.push(start_node);
for (uint i = 0; i < _nodes_to_visit.size(); i++) {
Node* next = _nodes_to_visit[i];
for (uint j = 1; j < next->req(); j++) {
Node* input = next->in(j);
if (_bfs_actions.is_target_node(input)) {
assert(_bfs_actions.should_visit(input), "must also pass node filter");
_bfs_actions.target_node_action(input);
} else if (_bfs_actions.should_visit(input)) {
_nodes_to_visit.push(input);
}
}
}
_custom_node_inputs_bfs.run(start_node);
}
};

Expand Down
13 changes: 7 additions & 6 deletions src/hotspot/share/opto/predicates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ class OpaqueLoopNodesVerifier : public BFSActions {
// - Always an OpaqueLoopInitNode
// - Only an OpaqueLoopStrideNode for the last value.
void verify(const TemplateAssertionPredicate& template_assertion_predicate) {
DataNodeBFS bfs(*this);
DataNodeInputsBFS bfs(*this);
bfs.run(template_assertion_predicate.opaque_node());
if (template_assertion_predicate.is_last_value()) {
assert(_found_init && _found_stride,
Expand All @@ -224,7 +224,7 @@ class OpaqueLoopNodesVerifier : public BFSActions {

// An Initialized Assertion Predicate never has any OpaqueLoop*Nodes.
void verify(const InitializedAssertionPredicate& initialized_assertion_predicate) {
DataNodeBFS bfs(*this);
DataNodeInputsBFS bfs(*this);
bfs.run(initialized_assertion_predicate.opaque_node());
assert(!_found_init && !_found_stride,
"must neither find OpaqueLoopInit nor OpaqueLoopStride for Initialized Assertion Predicate");
Expand Down Expand Up @@ -491,7 +491,7 @@ class ReplaceOpaqueStrideInput : public BFSActions {
NONCOPYABLE(ReplaceOpaqueStrideInput);

void replace_for(OpaqueTemplateAssertionPredicateNode* opaque_node) {
DataNodeBFS bfs(*this);
DataNodeInputsBFS bfs(*this);
bfs.run(opaque_node);
}

Expand Down Expand Up @@ -905,9 +905,10 @@ IfTrueNode* CreateAssertionPredicatesVisitor::initialize_from_template(
const TemplateAssertionPredicate& template_assertion_predicate) const {
DEBUG_ONLY(template_assertion_predicate.verify();)
IfNode* template_head = template_assertion_predicate.head();
InitializedAssertionPredicateCreator initialized_assertion_predicate(_phase);
IfTrueNode* initialized_predicate = initialized_assertion_predicate.create_from_template(template_head,_new_control,
_init, _stride);
InitializedAssertionPredicateCreator initialized_assertion_predicate_creator(_phase);
IfTrueNode* initialized_predicate = initialized_assertion_predicate_creator.create_from_template(template_head,
_new_control,
_init, _stride);
DEBUG_ONLY(InitializedAssertionPredicate::verify(initialized_predicate);)
template_assertion_predicate.rewire_loop_data_dependencies(initialized_predicate, _node_in_loop_body, _phase);
return initialized_predicate;
Expand Down

0 comments on commit fbde591

Please sign in to comment.