|
24 | 24 |
|
25 | 25 | #include "precompiled.hpp" |
26 | 26 | #include "opto/callnode.hpp" |
| 27 | +#include "opto/loopnode.hpp" |
| 28 | +#include "opto/node.hpp" |
27 | 29 | #include "opto/predicates.hpp" |
28 | 30 |
|
29 | 31 | // Walk over all Initialized Assertion Predicates and return the entry into the first Initialized Assertion Predicate |
@@ -147,3 +149,127 @@ Node* PredicateBlock::skip_regular_predicates(Node* regular_predicate_proj, Deop |
147 | 149 | } |
148 | 150 | return entry; |
149 | 151 | } |
| 152 | + |
| 153 | +// This strategy clones the OpaqueLoopInit and OpaqueLoopStride nodes. |
| 154 | +class CloneStrategy : public TransformStrategyForOpaqueLoopNodes { |
| 155 | + PhaseIdealLoop* const _phase; |
| 156 | + Node* const _new_ctrl; |
| 157 | + |
| 158 | + public: |
| 159 | + CloneStrategy(PhaseIdealLoop* phase, Node* new_ctrl) |
| 160 | + : _phase(phase), |
| 161 | + _new_ctrl(new_ctrl) {} |
| 162 | + NONCOPYABLE(CloneStrategy); |
| 163 | + |
| 164 | + Node* transform_opaque_init(OpaqueLoopInitNode* opaque_init) const override { |
| 165 | + return _phase->clone_and_register(opaque_init, _new_ctrl)->as_OpaqueLoopInit(); |
| 166 | + } |
| 167 | + |
| 168 | + Node* transform_opaque_stride(OpaqueLoopStrideNode* opaque_stride) const override { |
| 169 | + return _phase->clone_and_register(opaque_stride, _new_ctrl)->as_OpaqueLoopStride(); |
| 170 | + } |
| 171 | +}; |
| 172 | + |
| 173 | +// Creates an identical clone of this Template Assertion Predicate Expression (i.e.cloning all nodes from the Opaque4Node |
| 174 | +// to and including the OpaqueLoop* nodes). The cloned nodes are rewired to reflect the same graph structure as found for |
| 175 | +// this Template Assertion Predicate Expression. The cloned nodes get 'new_ctrl' as ctrl. There is no other update done |
| 176 | +// for the cloned nodes. Return the newly cloned Opaque4Node. |
| 177 | +Opaque4Node* TemplateAssertionPredicateExpression::clone(Node* new_ctrl, PhaseIdealLoop* phase) { |
| 178 | + CloneStrategy clone_init_and_stride_strategy(phase, new_ctrl); |
| 179 | + return clone(clone_init_and_stride_strategy, new_ctrl, phase); |
| 180 | +} |
| 181 | + |
| 182 | +// Class to collect data nodes from a source to target nodes by following the inputs of the source node recursively. |
| 183 | +// The class takes a node filter to decide which input nodes to follow and a target node predicate to start backtracking |
| 184 | +// from. All nodes found on all paths from source->target(s) are returned in a Unique_Node_List (without duplicates). |
| 185 | +class DataNodesOnPathsToTargets : public StackObj { |
| 186 | + typedef bool (*NodeCheck)(const Node*); |
| 187 | + |
| 188 | + // Node filter function to decide if we should process a node or not while searching for targets. |
| 189 | + NodeCheck _node_filter; |
| 190 | + // Function to decide if a node is a target node (i.e. where we should start backtracking). This check should also |
| 191 | + // trivially pass the _node_filter. |
| 192 | + NodeCheck _is_target_node; |
| 193 | + // The resulting node collection of all nodes on paths from source->target(s). |
| 194 | + Unique_Node_List _collected_nodes; |
| 195 | + // List to track all nodes visited on the search for target nodes starting at a start node. These nodes are then used |
| 196 | + // in backtracking to find the nodes actually being on a start->target(s) path. This list also serves as visited set |
| 197 | + // to avoid double visits of a node which could happen with diamonds shapes. |
| 198 | + Unique_Node_List _nodes_to_visit; |
| 199 | + |
| 200 | + public: |
| 201 | + DataNodesOnPathsToTargets(NodeCheck node_filter, NodeCheck is_target_node) |
| 202 | + : _node_filter(node_filter), |
| 203 | + _is_target_node(is_target_node) {} |
| 204 | + NONCOPYABLE(DataNodesOnPathsToTargets); |
| 205 | + |
| 206 | + // Collect all input nodes from 'start_node'->target(s) by applying the node filter to discover new input nodes and |
| 207 | + // the target node predicate to stop discovering more inputs and start backtracking. The implementation is done |
| 208 | + // with two BFS traversal: One to collect the target nodes (if any) and one to backtrack from the target nodes to |
| 209 | + // find all other nodes on the start->target(s) paths. |
| 210 | + const Unique_Node_List& collect(Node* start_node) { |
| 211 | + assert(_collected_nodes.size() == 0 && _nodes_to_visit.size() == 0, "should not call this method twice in a row"); |
| 212 | + assert(!_is_target_node(start_node), "no trivial paths where start node is also a target node"); |
| 213 | + |
| 214 | + collect_target_nodes(start_node); |
| 215 | + backtrack_from_target_nodes(); |
| 216 | + assert(_collected_nodes.size() == 0 || _collected_nodes.member(start_node), |
| 217 | + "either target node predicate was never true or must find start node again when doing backtracking work"); |
| 218 | + return _collected_nodes; |
| 219 | + } |
| 220 | + |
| 221 | + private: |
| 222 | + // Do a BFS from the start_node to collect all target nodes. We can then do another BFS from the target nodes to |
| 223 | + // find all nodes on the paths from start->target(s). |
| 224 | + // Note: We could do a single DFS pass to search targets and backtrack in one walk. But this is much more complex. |
| 225 | + // Given that the typical Template Assertion Predicate Expression only consists of a few nodes, we aim for |
| 226 | + // simplicity here. |
| 227 | + void collect_target_nodes(Node* start_node) { |
| 228 | + _nodes_to_visit.push(start_node); |
| 229 | + for (uint i = 0; i < _nodes_to_visit.size(); i++) { |
| 230 | + Node* next = _nodes_to_visit[i]; |
| 231 | + for (uint j = 1; j < next->req(); j++) { |
| 232 | + Node* input = next->in(j); |
| 233 | + if (_is_target_node(input)) { |
| 234 | + assert(_node_filter(input), "must also pass node filter"); |
| 235 | + _collected_nodes.push(input); |
| 236 | + } else if (_node_filter(input)) { |
| 237 | + _nodes_to_visit.push(input); |
| 238 | + } |
| 239 | + } |
| 240 | + } |
| 241 | + } |
| 242 | + |
| 243 | + // Backtrack from all previously collected target nodes by using the visited set of the start->target(s) search. If no |
| 244 | + // node was collected in the first place (i.e. target node predicate was never true), then nothing needs to be done. |
| 245 | + void backtrack_from_target_nodes() { |
| 246 | + for (uint i = 0; i < _collected_nodes.size(); i++) { |
| 247 | + Node* node_on_path = _collected_nodes[i]; |
| 248 | + for (DUIterator_Fast jmax, j = node_on_path->fast_outs(jmax); j < jmax; j++) { |
| 249 | + Node* use = node_on_path->fast_out(j); |
| 250 | + if (_nodes_to_visit.member(use)) { |
| 251 | + // use must be on a path from start->target(s) because it was also visited in the first BFS starting from |
| 252 | + // the start node. |
| 253 | + _collected_nodes.push(use); |
| 254 | + } |
| 255 | + } |
| 256 | + } |
| 257 | + } |
| 258 | +}; |
| 259 | + |
| 260 | +// Clones this Template Assertion Predicate Expression and applies the given strategy to transform the OpaqueLoop* nodes. |
| 261 | +Opaque4Node* TemplateAssertionPredicateExpression::clone(const TransformStrategyForOpaqueLoopNodes& transform_strategy, |
| 262 | + Node* new_ctrl, PhaseIdealLoop* phase) { |
| 263 | + ResourceMark rm; |
| 264 | + auto is_opaque_loop_node = [](const Node* node) { |
| 265 | + return node->is_Opaque1(); |
| 266 | + }; |
| 267 | + DataNodesOnPathsToTargets data_nodes_on_path_to_targets(TemplateAssertionPredicateExpression::maybe_contains, |
| 268 | + is_opaque_loop_node); |
| 269 | + const Unique_Node_List& collected_nodes = data_nodes_on_path_to_targets.collect(_opaque4_node); |
| 270 | + DataNodeGraph data_node_graph(collected_nodes, phase); |
| 271 | + const OrigToNewHashtable& orig_to_new = data_node_graph.clone_with_opaque_loop_transform_strategy(transform_strategy, new_ctrl); |
| 272 | + assert(orig_to_new.contains(_opaque4_node), "must exist"); |
| 273 | + Node* opaque4_clone = *orig_to_new.get(_opaque4_node); |
| 274 | + return opaque4_clone->as_Opaque4(); |
| 275 | +} |
0 commit comments