From fbde591803ada158cacc11bc553e1b5061e59ae7 Mon Sep 17 00:00:00 2001 From: Christian Hagedorn Date: Mon, 25 Nov 2024 10:58:53 +0100 Subject: [PATCH] Generalize BFS --- src/hotspot/share/opto/node.cpp | 28 +++++++++ src/hotspot/share/opto/node.hpp | 85 ++++++++++++++++++++------- src/hotspot/share/opto/predicates.cpp | 13 ++-- 3 files changed, 98 insertions(+), 28 deletions(-) diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 3f82c47216303..e4d0609bcf7bd 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -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); + } + } +} diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 47d8711eafd82..a6889aa0f6512 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -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 @@ -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); } }; diff --git a/src/hotspot/share/opto/predicates.cpp b/src/hotspot/share/opto/predicates.cpp index 9e742f41612f9..cb5d8afb561cb 100644 --- a/src/hotspot/share/opto/predicates.cpp +++ b/src/hotspot/share/opto/predicates.cpp @@ -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, @@ -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"); @@ -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); } @@ -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;