| /* |
| * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| #include "opto/addnode.hpp" |
| #include "opto/callnode.hpp" |
| #include "opto/castnode.hpp" |
| #include "opto/loopnode.hpp" |
| #include "opto/node.hpp" |
| #include "opto/predicates.hpp" |
| #include "opto/rootnode.hpp" |
| #include "runtime/deoptimization.hpp" |
| |
| // Walk over all Initialized Assertion Predicates and return the entry into the first Initialized Assertion Predicate |
| // (i.e. not belonging to an Initialized Assertion Predicate anymore) |
| Node* AssertionPredicates::find_entry(Node* start_proj) { |
| assert(start_proj != nullptr, "should not be null"); |
| Node* entry = start_proj; |
| while (AssertionPredicate::is_predicate(entry)) { |
| entry = entry->in(0)->in(0); |
| } |
| return entry; |
| } |
| |
| // An Assertion Predicate has always a true projection on the success path. |
| bool may_be_assertion_predicate_if(const Node* node) { |
| assert(node != nullptr, "should not be null"); |
| return node->is_IfTrue() && RegularPredicate::may_be_predicate_if(node->as_IfProj()); |
| } |
| |
| bool AssertionPredicate::is_predicate(const Node* maybe_success_proj) { |
| if (!may_be_assertion_predicate_if(maybe_success_proj)) { |
| return false; |
| } |
| return has_assertion_predicate_opaque(maybe_success_proj) && has_halt(maybe_success_proj->as_IfTrue()); |
| } |
| |
| // Check if the If node of `predicate_proj` has an OpaqueTemplateAssertionPredicate (Template Assertion Predicate) or |
| // an OpaqueInitializedAssertionPredicate (Initialized Assertion Predicate) node as input. |
| bool AssertionPredicate::has_assertion_predicate_opaque(const Node* predicate_proj) { |
| IfNode* iff = predicate_proj->in(0)->as_If(); |
| Node* bol = iff->in(1); |
| return bol->is_OpaqueTemplateAssertionPredicate() || bol->is_OpaqueInitializedAssertionPredicate(); |
| } |
| |
| // Check if the other projection (UCT projection) of `success_proj` has a Halt node as output. |
| bool AssertionPredicate::has_halt(const IfTrueNode* success_proj) { |
| ProjNode* other_proj = success_proj->other_if_proj(); |
| return other_proj->outcnt() == 1 && other_proj->unique_out()->Opcode() == Op_Halt; |
| } |
| |
| // Returns the Parse Predicate node if the provided node is a Parse Predicate success proj. Otherwise, return null. |
| ParsePredicateNode* ParsePredicate::init_parse_predicate(const Node* parse_predicate_proj, |
| Deoptimization::DeoptReason deopt_reason) { |
| assert(parse_predicate_proj != nullptr, "must not be null"); |
| if (parse_predicate_proj->is_IfTrue() && parse_predicate_proj->in(0)->is_ParsePredicate()) { |
| ParsePredicateNode* parse_predicate_node = parse_predicate_proj->in(0)->as_ParsePredicate(); |
| if (parse_predicate_node->deopt_reason() == deopt_reason) { |
| return parse_predicate_node; |
| } |
| } |
| return nullptr; |
| } |
| |
| ParsePredicate ParsePredicate::clone_to_unswitched_loop(Node* new_control, const bool is_false_path_loop, |
| PhaseIdealLoop* phase) const { |
| ParsePredicateSuccessProj* success_proj = phase->create_new_if_for_predicate(_success_proj, new_control, |
| _parse_predicate_node->deopt_reason(), |
| Op_ParsePredicate, is_false_path_loop); |
| NOT_PRODUCT(trace_cloned_parse_predicate(is_false_path_loop, success_proj)); |
| return ParsePredicate(success_proj, _parse_predicate_node->deopt_reason()); |
| } |
| |
| // Kills this Parse Predicate by marking it useless. Will be folded away in the next IGVN round. |
| void ParsePredicate::kill(PhaseIterGVN& igvn) const { |
| _parse_predicate_node->mark_useless(igvn); |
| } |
| |
| #ifndef PRODUCT |
| void ParsePredicate::trace_cloned_parse_predicate(const bool is_false_path_loop, |
| const ParsePredicateSuccessProj* success_proj) { |
| if (TraceLoopPredicate) { |
| tty->print("Parse Predicate cloned to %s path loop: ", is_false_path_loop ? "false" : "true"); |
| success_proj->in(0)->dump(); |
| } |
| } |
| #endif // NOT PRODUCT |
| |
| Deoptimization::DeoptReason RuntimePredicate::uncommon_trap_reason(const IfProjNode* if_proj) { |
| CallStaticJavaNode* uct_call = if_proj->is_uncommon_trap_if_pattern(); |
| if (uct_call == nullptr) { |
| return Deoptimization::Reason_none; |
| } |
| return Deoptimization::trap_request_reason(uct_call->uncommon_trap_request()); |
| } |
| |
| bool RuntimePredicate::is_predicate(const Node* maybe_success_proj) { |
| if (RegularPredicate::may_be_predicate_if(maybe_success_proj)) { |
| return has_valid_uncommon_trap(maybe_success_proj); |
| } else { |
| return false; |
| } |
| } |
| |
| bool RuntimePredicate::has_valid_uncommon_trap(const Node* success_proj) { |
| assert(RegularPredicate::may_be_predicate_if(success_proj), "must have been checked before"); |
| const Deoptimization::DeoptReason deopt_reason = uncommon_trap_reason(success_proj->as_IfProj()); |
| return (deopt_reason == Deoptimization::Reason_loop_limit_check || |
| deopt_reason == Deoptimization::Reason_auto_vectorization_check || |
| deopt_reason == Deoptimization::Reason_predicate || |
| deopt_reason == Deoptimization::Reason_profile_predicate); |
| } |
| |
| bool RuntimePredicate::is_predicate(const Node* node, const Deoptimization::DeoptReason deopt_reason) { |
| if (RegularPredicate::may_be_predicate_if(node)) { |
| return deopt_reason == uncommon_trap_reason(node->as_IfProj()); |
| } else { |
| return false; |
| } |
| } |
| |
| // A Regular Predicate must have an If or a RangeCheck node, while the If should not be a zero trip guard check. |
| // Note that this method can be called during IGVN, so we also need to check that the If is not top. |
| bool RegularPredicate::may_be_predicate_if(const Node* node) { |
| if (node->is_IfProj() && node->in(0)->is_If()) { |
| const IfNode* if_node = node->in(0)->as_If(); |
| const int opcode_if = if_node->Opcode(); |
| if ((opcode_if == Op_If && !if_node->is_zero_trip_guard()) |
| || opcode_if == Op_RangeCheck) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // Rewire any non-CFG nodes dependent on this Template Assertion Predicate (i.e. with a control input to this |
| // Template Assertion Predicate) to the 'target_predicate' based on the 'data_in_loop_body' check. |
| void TemplateAssertionPredicate::rewire_loop_data_dependencies(IfTrueNode* target_predicate, |
| const NodeInLoopBody& data_in_loop_body, |
| const PhaseIdealLoop* phase) const { |
| for (DUIterator i = _success_proj->outs(); _success_proj->has_out(i); i++) { |
| Node* output = _success_proj->out(i); |
| if (!output->is_CFG() && data_in_loop_body.check_node_in_loop_body(output)) { |
| phase->igvn().replace_input_of(output, 0, target_predicate); |
| --i; // account for the just deleted output |
| } |
| } |
| } |
| |
| // A Template Assertion Predicate always has a dedicated OpaqueTemplateAssertionPredicate to identify it. |
| bool TemplateAssertionPredicate::is_predicate(const Node* maybe_success_proj) { |
| if (!may_be_assertion_predicate_if(maybe_success_proj)) { |
| return false; |
| } |
| IfNode* if_node = maybe_success_proj->in(0)->as_If(); |
| bool is_template_assertion_predicate = if_node->in(1)->is_OpaqueTemplateAssertionPredicate(); |
| assert(!is_template_assertion_predicate || AssertionPredicate::has_halt(maybe_success_proj->as_IfTrue()), |
| "Template Assertion Predicate must have a Halt Node on the failing path"); |
| return is_template_assertion_predicate; |
| } |
| |
| // Clone this Template Assertion Predicate without modifying any OpaqueLoop*Node inputs. |
| TemplateAssertionPredicate TemplateAssertionPredicate::clone(Node* new_control, CountedLoopNode* new_loop_node, |
| PhaseIdealLoop* phase) const { |
| DEBUG_ONLY(verify();) |
| TemplateAssertionExpression template_assertion_expression(opaque_node(), phase); |
| OpaqueTemplateAssertionPredicateNode* new_opaque_node = template_assertion_expression.clone(new_control, new_loop_node); |
| AssertionPredicateIfCreator assertion_predicate_if_creator(phase); |
| IfTrueNode* success_proj = assertion_predicate_if_creator.create_for_template(new_control, _if_node->Opcode(), |
| new_opaque_node, |
| _if_node->assertion_predicate_type()); |
| TemplateAssertionPredicate cloned_template_assertion_predicate(success_proj); |
| DEBUG_ONLY(cloned_template_assertion_predicate.verify();) |
| return cloned_template_assertion_predicate; |
| } |
| |
| // Clone this Template Assertion Predicate and use a newly created OpaqueLoopInitNode with 'new_opaque_input' as input. |
| TemplateAssertionPredicate TemplateAssertionPredicate::clone_and_replace_opaque_input(Node* new_control, |
| Node* new_opaque_input, |
| CountedLoopNode* new_loop_node, |
| PhaseIdealLoop* phase) const { |
| DEBUG_ONLY(verify();) |
| OpaqueLoopInitNode* new_opaque_init = new OpaqueLoopInitNode(phase->C, new_opaque_input); |
| phase->register_new_node(new_opaque_init, new_control); |
| TemplateAssertionExpression template_assertion_expression(opaque_node(), phase); |
| OpaqueTemplateAssertionPredicateNode* new_opaque_node = |
| template_assertion_expression.clone_and_replace_init(new_control, new_opaque_init, new_loop_node); |
| AssertionPredicateIfCreator assertion_predicate_if_creator(phase); |
| IfTrueNode* success_proj = assertion_predicate_if_creator.create_for_template(new_control, _if_node->Opcode(), |
| new_opaque_node, |
| _if_node->assertion_predicate_type()); |
| TemplateAssertionPredicate cloned_template_assertion_predicate(success_proj); |
| DEBUG_ONLY(cloned_template_assertion_predicate.verify();) |
| return cloned_template_assertion_predicate; |
| } |
| |
| // This class is used to replace the input to OpaqueLoopStrideNode with a new node while leaving the other nodes |
| // unchanged. |
| class ReplaceOpaqueStrideInput : public BFSActions { |
| Node* _new_opaque_stride_input; |
| PhaseIterGVN& _igvn; |
| |
| public: |
| ReplaceOpaqueStrideInput(Node* new_opaque_stride_input, PhaseIterGVN& igvn) |
| : _new_opaque_stride_input(new_opaque_stride_input), |
| _igvn(igvn) {} |
| NONCOPYABLE(ReplaceOpaqueStrideInput); |
| |
| void replace_for(OpaqueTemplateAssertionPredicateNode* opaque_node) { |
| DataNodeBFS bfs(*this); |
| bfs.run(opaque_node); |
| } |
| |
| bool should_visit(Node* node) const override { |
| return TemplateAssertionExpressionNode::is_maybe_in_expression(node); |
| } |
| |
| bool is_target_node(Node* node) const override { |
| return node->is_OpaqueLoopStride(); |
| } |
| |
| void target_node_action(Node* target_node) override { |
| _igvn.replace_input_of(target_node, 1, _new_opaque_stride_input); |
| } |
| }; |
| |
| // Replace the input to OpaqueLoopStrideNode with 'new_stride' and leave the other nodes unchanged. |
| void TemplateAssertionPredicate::replace_opaque_stride_input(Node* new_stride, PhaseIterGVN& igvn) const { |
| DEBUG_ONLY(verify();) |
| ReplaceOpaqueStrideInput replace_opaque_stride_input(new_stride, igvn); |
| replace_opaque_stride_input.replace_for(opaque_node()); |
| } |
| |
| // Create a new Initialized Assertion Predicate from this template at the template success projection. |
| InitializedAssertionPredicate TemplateAssertionPredicate::initialize(PhaseIdealLoop* phase) const { |
| DEBUG_ONLY(verify();) |
| InitializedAssertionPredicateCreator initialized_assertion_predicate_creator(phase); |
| InitializedAssertionPredicate initialized_assertion_predicate = |
| initialized_assertion_predicate_creator.create_from_template_and_insert_below(*this); |
| DEBUG_ONLY(initialized_assertion_predicate.verify();) |
| return initialized_assertion_predicate; |
| } |
| |
| // Kills this Template Assertion Predicate by marking the associated OpaqueTemplateAssertionPredicate node useless. |
| // It will then be folded away in the next IGVN round. |
| void TemplateAssertionPredicate::kill(PhaseIterGVN& igvn) const { |
| opaque_node()->mark_useless(igvn); |
| } |
| |
| #ifdef ASSERT |
| // Class to verify Initialized and Template Assertion Predicates by trying to find OpaqueLoop*Nodes. |
| class OpaqueLoopNodesVerifier : public BFSActions { |
| bool _found_init; |
| bool _found_stride; |
| |
| public: |
| OpaqueLoopNodesVerifier() |
| : _found_init(false), |
| _found_stride(false) {} |
| |
| // A Template Assertion Predicate has: |
| // - Always an OpaqueLoopInitNode |
| // - Only an OpaqueLoopStrideNode for the last value. |
| void verify(const TemplateAssertionPredicate& template_assertion_predicate) { |
| DataNodeBFS bfs(*this); |
| bfs.run(template_assertion_predicate.opaque_node()); |
| if (template_assertion_predicate.is_last_value()) { |
| assert(_found_init && _found_stride, |
| "must find OpaqueLoopInit and OpaqueLoopStride for last value Template Assertion Predicate"); |
| } else { |
| assert(_found_init && !_found_stride, |
| "must find OpaqueLoopInit but not OpaqueLoopStride for init value Template Assertion Predicate"); |
| } |
| } |
| |
| // An Initialized Assertion Predicate never has any OpaqueLoop*Nodes. |
| void verify(const InitializedAssertionPredicate& initialized_assertion_predicate) { |
| DataNodeBFS bfs(*this); |
| bfs.run(initialized_assertion_predicate.opaque_node()); |
| assert(!_found_init && !_found_stride, |
| "must neither find OpaqueLoopInit nor OpaqueLoopStride for Initialized Assertion Predicate"); |
| } |
| |
| bool should_visit(Node* node) const override { |
| return TemplateAssertionExpressionNode::is_maybe_in_expression(node); |
| } |
| |
| bool is_target_node(Node* node) const override { |
| return node->is_Opaque1(); |
| } |
| |
| void target_node_action(Node* target_node) override { |
| if (target_node->is_OpaqueLoopInit()) { |
| assert(!_found_init, "should only find one OpaqueLoopInitNode"); |
| _found_init = true; |
| } else { |
| assert(target_node->is_OpaqueLoopStride(), "unexpected Opaque1 node"); |
| assert(!_found_stride, "should only find one OpaqueLoopStrideNode"); |
| _found_stride = true; |
| } |
| } |
| }; |
| |
| // Verify that the Template Assertion Predicate has the correct OpaqueLoop*Nodes. |
| void TemplateAssertionPredicate::verify() const { |
| OpaqueLoopNodesVerifier opaque_loop_nodes_verifier; |
| opaque_loop_nodes_verifier.verify(*this); |
| } |
| |
| // Verify that the Initialized Assertion Predicate has no OpaqueLoop*Node. |
| void InitializedAssertionPredicate::verify() const { |
| OpaqueLoopNodesVerifier opaque_loop_nodes_verifier; |
| opaque_loop_nodes_verifier.verify(*this); |
| } |
| #endif // ASSERT |
| |
| // An Initialized Assertion Predicate always has a dedicated OpaqueInitializedAssertionPredicate node to identify it. |
| bool InitializedAssertionPredicate::is_predicate(const Node* maybe_success_proj) { |
| if (!may_be_assertion_predicate_if(maybe_success_proj)) { |
| return false; |
| } |
| IfNode* if_node = maybe_success_proj->in(0)->as_If(); |
| bool is_initialized_assertion_predicate = if_node->in(1)->is_OpaqueInitializedAssertionPredicate(); |
| assert(!is_initialized_assertion_predicate || has_halt(maybe_success_proj->as_IfTrue()), |
| "Initialized Assertion Predicate must have a Halt Node on the failing path"); |
| return is_initialized_assertion_predicate; |
| } |
| |
| #ifdef ASSERT |
| bool InitializedAssertionPredicate::has_halt(const IfTrueNode* success_proj) { |
| ProjNode* other_proj = success_proj->other_if_proj(); |
| if (other_proj->outcnt() != 1) { |
| return false; |
| } |
| |
| Node* out = other_proj->unique_out(); |
| // Either the Halt node is directly the unique output. |
| if (out->is_Halt()) { |
| return true; |
| } |
| // Or we have a Region that merges serval paths to a single Halt node. Even though OpaqueInitializedAssertionPredicate |
| // nodes do not common up (i.e. NO_HASH), we could have Initialized Assertion Predicates from already folded loops |
| // being now part of the innermost loop. When then further splitting this loop, we could be cloning the If node |
| // of the Initialized Assertion Predicate (part of the loop body) while the OpaqueInitializedAssertionPredicate is not |
| // cloned because it's outside the loop body. We end up sharing the OpaqueInitializedAssertionPredicate between the |
| // original and the cloned If. This should be fine. |
| if (out->is_Region() && out->outcnt() == 2) { |
| return out->find_out_with(Op_Halt); |
| } |
| return false; |
| } |
| #endif // ASSERT |
| |
| // Kills this Initialized Assertion Predicate by marking the associated OpaqueInitializedAssertionPredicate node useless. |
| // It will then be folded away in the next IGVN round. |
| void InitializedAssertionPredicate::kill(PhaseIterGVN& igvn) const { |
| opaque_node()->mark_useless(igvn); |
| } |
| |
| #ifdef ASSERT |
| // Check that the block has at most one Parse Predicate and that we only find Regular Predicate nodes (i.e. IfProj, |
| // If, or RangeCheck nodes). |
| void RegularPredicateBlock::verify_block(Node* tail) const { |
| Node* next = tail; |
| while (next != _entry) { |
| assert(!next->is_ParsePredicate(), "can only have one Parse Predicate in a block"); |
| const int opcode = next->Opcode(); |
| assert(next->is_IfProj() || opcode == Op_If || opcode == Op_RangeCheck, |
| "Regular Predicates consist of an IfProj and an If or RangeCheck node"); |
| assert(opcode != Op_If || !next->as_If()->is_zero_trip_guard(), "should not be zero trip guard"); |
| next = next->in(0); |
| } |
| } |
| #endif // ASSERT |
| |
| // This strategy clones the OpaqueLoopInit and OpaqueLoopStride nodes. |
| class CloneStrategy : public TransformStrategyForOpaqueLoopNodes { |
| PhaseIdealLoop* const _phase; |
| Node* const _new_control; |
| |
| public: |
| CloneStrategy(PhaseIdealLoop* phase, Node* new_control) |
| : _phase(phase), |
| _new_control(new_control) {} |
| NONCOPYABLE(CloneStrategy); |
| |
| Node* transform_opaque_init(OpaqueLoopInitNode* opaque_init) const override { |
| return _phase->clone_and_register(opaque_init, _new_control)->as_OpaqueLoopInit(); |
| } |
| |
| Node* transform_opaque_stride(OpaqueLoopStrideNode* opaque_stride) const override { |
| return _phase->clone_and_register(opaque_stride, _new_control)->as_OpaqueLoopStride(); |
| } |
| }; |
| |
| // This strategy replaces the OpaqueLoopInitNode with the provided init node and clones the OpaqueLoopStrideNode. |
| class ReplaceInitAndCloneStrideStrategy : public TransformStrategyForOpaqueLoopNodes { |
| Node* const _new_init; |
| Node* const _new_control; |
| PhaseIdealLoop* const _phase; |
| |
| public: |
| ReplaceInitAndCloneStrideStrategy(Node* new_control, Node* new_init, PhaseIdealLoop* phase) |
| : _new_init(new_init), |
| _new_control(new_control), |
| _phase(phase) {} |
| NONCOPYABLE(ReplaceInitAndCloneStrideStrategy); |
| |
| Node* transform_opaque_init(OpaqueLoopInitNode* opaque_init) const override { |
| return _new_init; |
| } |
| |
| Node* transform_opaque_stride(OpaqueLoopStrideNode* opaque_stride) const override { |
| return _phase->clone_and_register(opaque_stride, _new_control)->as_OpaqueLoopStride(); |
| } |
| }; |
| |
| // This strategy replaces the OpaqueLoopInit and OpaqueLoopStride nodes with the provided init and stride nodes, |
| // respectively. |
| class ReplaceInitAndStrideStrategy : public TransformStrategyForOpaqueLoopNodes { |
| Node* const _new_init; |
| Node* const _new_stride; |
| |
| public: |
| ReplaceInitAndStrideStrategy(Node* new_init, Node* new_stride) |
| : _new_init(new_init), |
| _new_stride(new_stride) {} |
| NONCOPYABLE(ReplaceInitAndStrideStrategy); |
| |
| Node* transform_opaque_init(OpaqueLoopInitNode* opaque_init) const override { |
| return _new_init; |
| } |
| |
| Node* transform_opaque_stride(OpaqueLoopStrideNode* opaque_stride) const override { |
| return _new_stride; |
| } |
| }; |
| |
| // Creates an identical clone of this Template Assertion Expression (i.e.cloning all nodes from the |
| // OpaqueTemplateAssertionPredicate to and including the OpaqueLoop* nodes). The cloned nodes are rewired to reflect the |
| // same graph structure as found for this Template Assertion Expression. The cloned nodes get 'new_control' as control. |
| // There is no other update done for the cloned nodes. Return the newly cloned OpaqueTemplateAssertionPredicate. |
| OpaqueTemplateAssertionPredicateNode* TemplateAssertionExpression::clone(Node* new_control, CountedLoopNode* new_loop_node) const { |
| CloneStrategy clone_init_and_stride_strategy(_phase, new_control); |
| return clone(clone_init_and_stride_strategy, new_control, new_loop_node); |
| } |
| |
| // Same as clone() but instead of cloning the OpaqueLoopInitNode, we replace it with the provided 'new_init' node. |
| OpaqueTemplateAssertionPredicateNode* |
| TemplateAssertionExpression::clone_and_replace_init(Node* new_control, Node* new_init, |
| CountedLoopNode* new_loop_node) const { |
| ReplaceInitAndCloneStrideStrategy replace_init_and_clone_stride_strategy(new_control, new_init, _phase); |
| return clone(replace_init_and_clone_stride_strategy, new_control, new_loop_node); |
| } |
| |
| // Same as clone() but instead of cloning the OpaqueLoopInit and OpaqueLoopStride node, we replace them with the provided |
| // 'new_init' and 'new_stride' nodes, respectively. |
| OpaqueTemplateAssertionPredicateNode* |
| TemplateAssertionExpression::clone_and_replace_init_and_stride(Node* new_control, Node* new_init, Node* new_stride) const { |
| ReplaceInitAndStrideStrategy replace_init_and_stride_strategy(new_init, new_stride); |
| return clone(replace_init_and_stride_strategy, new_control, _opaque_node->loop_node()); |
| } |
| |
| // Class to collect data nodes from a source to target nodes by following the inputs of the source node recursively. |
| // The class takes a node filter to decide which input nodes to follow and a target node predicate to start backtracking |
| // from. All nodes found on all paths from source->target(s) are returned in a Unique_Node_List (without duplicates). |
| class DataNodesOnPathsToTargets : public StackObj { |
| typedef bool (*NodeCheck)(const Node*); |
| |
| // Node filter function to decide if we should process a node or not while searching for targets. |
| NodeCheck _node_filter; |
| // Function to decide if a node is a target node (i.e. where we should start backtracking). This check should also |
| // trivially pass the _node_filter. |
| NodeCheck _is_target_node; |
| // The resulting node collection of all nodes on paths from source->target(s). |
| Unique_Node_List _collected_nodes; |
| // List to track all nodes visited on the search for target nodes starting at a start node. These nodes are then used |
| // in backtracking to find the nodes actually being on a start->target(s) path. This list also serves as visited set |
| // to avoid double visits of a node which could happen with diamonds shapes. |
| Unique_Node_List _nodes_to_visit; |
| |
| public: |
| DataNodesOnPathsToTargets(NodeCheck node_filter, NodeCheck is_target_node) |
| : _node_filter(node_filter), |
| _is_target_node(is_target_node) {} |
| NONCOPYABLE(DataNodesOnPathsToTargets); |
| |
| // Collect all input nodes from 'start_node'->target(s) by applying the node filter to discover new input nodes and |
| // the target node predicate to stop discovering more inputs and start backtracking. The implementation is done |
| // with two BFS traversal: One to collect the target nodes (if any) and one to backtrack from the target nodes to |
| // find all other nodes on the start->target(s) paths. |
| const Unique_Node_List& collect(Node* start_node) { |
| assert(_collected_nodes.size() == 0 && _nodes_to_visit.size() == 0, "should not call this method twice in a row"); |
| assert(!_is_target_node(start_node), "no trivial paths where start node is also a target node"); |
| |
| collect_target_nodes(start_node); |
| backtrack_from_target_nodes(); |
| assert(_collected_nodes.size() == 0 || _collected_nodes.member(start_node), |
| "either target node predicate was never true or must find start node again when doing backtracking work"); |
| return _collected_nodes; |
| } |
| |
| private: |
| // Do a BFS from the start_node to collect all target nodes. We can then do another BFS from the target nodes to |
| // find all nodes on the paths from start->target(s). |
| // Note: We could do a single DFS pass to search targets and backtrack in one walk. But this is much more complex. |
| // Given that the typical Template Assertion Expression only consists of a few nodes, we aim for simplicity here. |
| void collect_target_nodes(Node* start_node) { |
| _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 (_is_target_node(input)) { |
| assert(_node_filter(input), "must also pass node filter"); |
| _collected_nodes.push(input); |
| } else if (_node_filter(input)) { |
| _nodes_to_visit.push(input); |
| } |
| } |
| } |
| } |
| |
| // Backtrack from all previously collected target nodes by using the visited set of the start->target(s) search. If no |
| // node was collected in the first place (i.e. target node predicate was never true), then nothing needs to be done. |
| void backtrack_from_target_nodes() { |
| for (uint i = 0; i < _collected_nodes.size(); i++) { |
| Node* node_on_path = _collected_nodes[i]; |
| for (DUIterator_Fast jmax, j = node_on_path->fast_outs(jmax); j < jmax; j++) { |
| Node* use = node_on_path->fast_out(j); |
| if (_nodes_to_visit.member(use)) { |
| // use must be on a path from start->target(s) because it was also visited in the first BFS starting from |
| // the start node. |
| _collected_nodes.push(use); |
| } |
| } |
| } |
| } |
| }; |
| |
| // Clones this Template Assertion Expression and applies the given strategy to transform the OpaqueLoop* nodes. |
| OpaqueTemplateAssertionPredicateNode* |
| TemplateAssertionExpression::clone(const TransformStrategyForOpaqueLoopNodes& transform_strategy, Node* new_control, |
| CountedLoopNode* new_loop_node) const { |
| ResourceMark rm; |
| auto is_opaque_loop_node = [](const Node* node) { |
| return node->is_Opaque1(); |
| }; |
| DataNodesOnPathsToTargets data_nodes_on_path_to_targets(TemplateAssertionExpressionNode::is_maybe_in_expression, |
| is_opaque_loop_node); |
| Node* bool_into_opaque_node = _opaque_node->in(1); |
| const Unique_Node_List& collected_nodes = data_nodes_on_path_to_targets.collect(bool_into_opaque_node); |
| DataNodeGraph data_node_graph(collected_nodes, _phase); |
| const OrigToNewHashtable& orig_to_new = data_node_graph.clone_with_opaque_loop_transform_strategy(transform_strategy, |
| new_control); |
| assert(orig_to_new.contains(bool_into_opaque_node), "must exist"); |
| BoolNode* bool_into_opaque_node_clone = (*orig_to_new.get(bool_into_opaque_node))->as_Bool(); |
| OpaqueTemplateAssertionPredicateNode* opaque_clone = |
| new OpaqueTemplateAssertionPredicateNode(bool_into_opaque_node_clone, new_loop_node); |
| _phase->C->add_template_assertion_predicate_opaque(opaque_clone); |
| _phase->register_new_node(opaque_clone, new_control); |
| return opaque_clone; |
| } |
| |
| // The transformations of this class fold the OpaqueLoop* nodes by returning their inputs. |
| class RemoveOpaqueLoopNodesStrategy : public TransformStrategyForOpaqueLoopNodes { |
| public: |
| Node* transform_opaque_init(OpaqueLoopInitNode* opaque_init) const override { |
| return opaque_init->in(1); |
| } |
| |
| Node* transform_opaque_stride(OpaqueLoopStrideNode* opaque_stride) const override { |
| return opaque_stride->in(1); |
| } |
| }; |
| |
| OpaqueInitializedAssertionPredicateNode* |
| TemplateAssertionExpression::clone_and_fold_opaque_loop_nodes(Node* new_control) const { |
| RemoveOpaqueLoopNodesStrategy remove_opaque_loop_nodes_strategy; |
| OpaqueTemplateAssertionPredicateNode* cloned_template_opaque = clone(remove_opaque_loop_nodes_strategy, new_control, |
| _opaque_node->loop_node()); |
| OpaqueInitializedAssertionPredicateNode* opaque_initialized_opaque = |
| new OpaqueInitializedAssertionPredicateNode(cloned_template_opaque->in(1)->as_Bool(), _phase->C); |
| _phase->register_new_node(opaque_initialized_opaque, new_control); |
| return opaque_initialized_opaque; |
| } |
| |
| // Check if this node belongs a Template Assertion Expression (including OpaqueLoop* nodes). |
| bool TemplateAssertionExpressionNode::is_in_expression(Node* node) { |
| if (is_maybe_in_expression(node)) { |
| ResourceMark rm; |
| Unique_Node_List list; |
| list.push(node); |
| for (uint i = 0; i < list.size(); i++) { |
| Node* next = list.at(i); |
| if (next->is_OpaqueLoopInit() || next->is_OpaqueLoopStride()) { |
| return true; |
| } else if (is_maybe_in_expression(next)) { |
| list.push_non_cfg_inputs_of(next); |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool TemplateAssertionExpressionNode::is_template_assertion_predicate(const Node* node) { |
| return node->is_If() && node->in(1)->is_OpaqueTemplateAssertionPredicate(); |
| } |
| |
| // This class creates the Assertion Predicate expression to be used for a Template or Initialized Assertion Predicate. |
| class AssertionPredicateExpressionCreator : public StackObj { |
| PhaseIdealLoop* const _phase; |
| const jint _stride; |
| const int _scale; |
| Node* const _offset; |
| Node* const _range; |
| const bool _upper; |
| |
| public: |
| AssertionPredicateExpressionCreator(const int stride, const int scale, Node* offset, Node* range, |
| PhaseIdealLoop* phase) |
| : _phase(phase), |
| _stride(stride), |
| _scale(scale), |
| _offset(offset), |
| _range(range), |
| _upper((_stride > 0) != (_scale > 0)) {} // Make sure rc_predicate() chooses the "scale*init + offset" case. |
| |
| // Create the expression for a Template Assertion Predicate with an OpaqueTemplateAssertionPredicate node. |
| OpaqueTemplateAssertionPredicateNode* create_for_template(Node* new_control, Node* operand, CountedLoopNode* loop_node, |
| bool& does_overflow) const { |
| BoolNode* bool_for_expression = _phase->rc_predicate(new_control, _scale, _offset, operand, nullptr, |
| _stride, _range, _upper, does_overflow); |
| return create_opaque_for_template(new_control, bool_for_expression, loop_node); |
| } |
| |
| private: |
| OpaqueTemplateAssertionPredicateNode* create_opaque_for_template(Node* new_control, BoolNode* bool_for_expression, |
| CountedLoopNode* loop_node) const { |
| OpaqueTemplateAssertionPredicateNode* new_expression = new OpaqueTemplateAssertionPredicateNode(bool_for_expression, |
| loop_node); |
| _phase->C->add_template_assertion_predicate_opaque(new_expression); |
| _phase->register_new_node(new_expression, new_control); |
| return new_expression; |
| } |
| |
| public: |
| // Create the expression for an Initialized Assertion Predicate with an OpaqueInitializedAssertionPredicate node. |
| OpaqueInitializedAssertionPredicateNode* create_for_initialized(Node* new_control, Node* operand, |
| bool& does_overflow) const { |
| BoolNode* bool_for_expression = _phase->rc_predicate(new_control, _scale, _offset, operand, nullptr, |
| _stride, _range, _upper, does_overflow); |
| return create_opaque_for_initialized(new_control, bool_for_expression); |
| } |
| |
| private: |
| OpaqueInitializedAssertionPredicateNode* create_opaque_for_initialized(Node* new_control, |
| BoolNode* bool_for_expression) const { |
| OpaqueInitializedAssertionPredicateNode* new_expression = |
| new OpaqueInitializedAssertionPredicateNode(bool_for_expression, _phase->C); |
| _phase->register_new_node(new_expression, new_control); |
| return new_expression; |
| } |
| }; |
| |
| // Creates an If with a success and a fail path with the given assertion_expression. The only difference to |
| // create_for_initialized() is that we use a template specific Halt message on the fail path. |
| IfTrueNode* AssertionPredicateIfCreator::create_for_template(Node* new_control, const int if_opcode, |
| Node* assertion_expression, |
| const AssertionPredicateType assertion_predicate_type) const { |
| const char* halt_message = "Template Assertion Predicates are always removed before code generation"; |
| return create(new_control, if_opcode, assertion_expression, halt_message, assertion_predicate_type); |
| } |
| |
| // Creates an If with a success and a fail path with the given assertion_expression. The only difference to |
| // create_for_template() is that we use a initialized specific Halt message on the fail path. |
| IfTrueNode* AssertionPredicateIfCreator::create_for_initialized(Node* new_control, const int if_opcode, |
| Node* assertion_expression, |
| const AssertionPredicateType assertion_predicate_type) const { |
| const char* halt_message = "Initialized Assertion Predicate cannot fail"; |
| return create(new_control, if_opcode, assertion_expression, halt_message, assertion_predicate_type); |
| } |
| |
| // Creates the If node for an Assertion Predicate with a success path and a fail path having a Halt node: |
| // |
| // new_control assertion_expression |
| // \ / |
| // If |
| // / \ |
| // success fail path |
| // proj with Halt |
| // |
| IfTrueNode* AssertionPredicateIfCreator::create(Node* new_control, const int if_opcode, Node* assertion_expression, |
| const char* halt_message, |
| const AssertionPredicateType assertion_predicate_type) const { |
| assert(assertion_expression->is_OpaqueTemplateAssertionPredicate() || |
| assertion_expression->is_OpaqueInitializedAssertionPredicate(), "not a valid assertion expression"); |
| IdealLoopTree* loop = _phase->get_loop(new_control); |
| IfNode* if_node = create_if_node(new_control, if_opcode, assertion_expression, loop, assertion_predicate_type); |
| create_fail_path(if_node, loop, halt_message); |
| return create_success_path(if_node, loop); |
| } |
| |
| IfNode* AssertionPredicateIfCreator::create_if_node(Node* new_control, const int if_opcode, Node* assertion_expression, |
| IdealLoopTree* loop, |
| const AssertionPredicateType assertion_predicate_type) const { |
| IfNode* if_node; |
| if (if_opcode == Op_If) { |
| if_node = new IfNode(new_control, assertion_expression, PROB_MAX, COUNT_UNKNOWN, assertion_predicate_type); |
| } else { |
| assert(if_opcode == Op_RangeCheck, "must be range check"); |
| if_node = new RangeCheckNode(new_control, assertion_expression, PROB_MAX, COUNT_UNKNOWN, assertion_predicate_type); |
| } |
| _phase->register_control(if_node, loop, new_control); |
| return if_node; |
| } |
| |
| IfTrueNode* AssertionPredicateIfCreator::create_success_path(IfNode* if_node, IdealLoopTree* loop) const { |
| IfTrueNode* success_proj = new IfTrueNode(if_node); |
| _phase->register_control(success_proj, loop, if_node); |
| return success_proj; |
| } |
| |
| void AssertionPredicateIfCreator::create_fail_path(IfNode* if_node, IdealLoopTree* loop, const char* halt_message) const { |
| IfFalseNode* fail_proj = new IfFalseNode(if_node); |
| _phase->register_control(fail_proj, loop, if_node); |
| create_halt_node(fail_proj, loop, halt_message); |
| } |
| |
| void AssertionPredicateIfCreator::create_halt_node(IfFalseNode* fail_proj, IdealLoopTree* loop, |
| const char* halt_message) const { |
| StartNode* start_node = _phase->C->start(); |
| Node* frame = new ParmNode(start_node, TypeFunc::FramePtr); |
| _phase->register_new_node(frame, start_node); |
| Node* halt = new HaltNode(fail_proj, frame, halt_message); |
| _phase->igvn().add_input_to(_phase->C->root(), halt); |
| _phase->register_control(halt, loop, fail_proj); |
| } |
| |
| OpaqueLoopInitNode* TemplateAssertionPredicateCreator::create_opaque_init(Node* new_control) const { |
| OpaqueLoopInitNode* opaque_init = new OpaqueLoopInitNode(_phase->C, _loop_head->uncasted_init_trip(_loop_head->is_main_loop())); |
| _phase->register_new_node(opaque_init, new_control); |
| return opaque_init; |
| } |
| |
| OpaqueTemplateAssertionPredicateNode* |
| TemplateAssertionPredicateCreator::create_for_init_value(Node* new_control, OpaqueLoopInitNode* opaque_init, |
| bool& does_overflow) const { |
| AssertionPredicateExpressionCreator expression_creator(_loop_head->stride_con(), _scale, _offset, _range, _phase); |
| return expression_creator.create_for_template(new_control, opaque_init, _loop_head, does_overflow); |
| } |
| |
| OpaqueTemplateAssertionPredicateNode* |
| TemplateAssertionPredicateCreator::create_for_last_value(Node* new_control, OpaqueLoopInitNode* opaque_init, |
| bool& does_overflow) const { |
| Node* last_value = create_last_value(new_control, opaque_init); |
| AssertionPredicateExpressionCreator expression_creator(_loop_head->stride_con(), _scale, _offset, _range, _phase); |
| return expression_creator.create_for_template(new_control, last_value, _loop_head, does_overflow); |
| } |
| |
| Node* TemplateAssertionPredicateCreator::create_last_value(Node* new_control, OpaqueLoopInitNode* opaque_init) const { |
| Node* init_stride = _loop_head->stride(); |
| Node* opaque_stride = new OpaqueLoopStrideNode(_phase->C, init_stride); |
| _phase->register_new_node(opaque_stride, new_control); |
| Node* last_value = new SubINode(opaque_stride, init_stride); |
| _phase->register_new_node(last_value, new_control); |
| last_value = new AddINode(opaque_init, last_value); |
| _phase->register_new_node(last_value, new_control); |
| // init + (current stride - initial stride) is within the loop so narrow its type by leveraging the type of the iv phi |
| last_value = new CastIINode(new_control, last_value, _loop_head->phi()->bottom_type()); |
| _phase->register_new_node(last_value, new_control); |
| return last_value; |
| } |
| |
| IfTrueNode* TemplateAssertionPredicateCreator::create_if_node( |
| Node* new_control, OpaqueTemplateAssertionPredicateNode* template_assertion_predicate_expression, |
| const bool does_overflow, const AssertionPredicateType assertion_predicate_type) const { |
| AssertionPredicateIfCreator assertion_predicate_if_creator(_phase); |
| return assertion_predicate_if_creator.create_for_template(new_control, does_overflow ? Op_If : Op_RangeCheck, |
| template_assertion_predicate_expression, |
| assertion_predicate_type); |
| } |
| |
| // Creates an init and last value Template Assertion Predicate connected together with a Halt node on the failing path. |
| // Returns the success projection of the last value Template Assertion Predicate latter. |
| IfTrueNode* TemplateAssertionPredicateCreator::create(Node* new_control) const { |
| OpaqueLoopInitNode* opaque_init = create_opaque_init(new_control); |
| bool does_overflow; |
| OpaqueTemplateAssertionPredicateNode* template_assertion_predicate_expression = |
| create_for_init_value(new_control, opaque_init, does_overflow); |
| IfTrueNode* template_predicate_success_proj = |
| create_if_node(new_control, template_assertion_predicate_expression, does_overflow, |
| AssertionPredicateType::InitValue); |
| DEBUG_ONLY(TemplateAssertionPredicate::verify(template_predicate_success_proj);) |
| |
| template_assertion_predicate_expression = create_for_last_value(template_predicate_success_proj, opaque_init, |
| does_overflow); |
| template_predicate_success_proj = create_if_node(template_predicate_success_proj, |
| template_assertion_predicate_expression, does_overflow, |
| AssertionPredicateType::LastValue); |
| DEBUG_ONLY(TemplateAssertionPredicate::verify(template_predicate_success_proj);) |
| return template_predicate_success_proj; |
| } |
| |
| InitializedAssertionPredicateCreator::InitializedAssertionPredicateCreator(PhaseIdealLoop* phase) |
| : _phase(phase) {} |
| |
| // Create an Initialized Assertion Predicate from the provided template_assertion_predicate at 'new_control'. |
| // We clone the Template Assertion Expression and replace: |
| // - OpaqueTemplateAssertionPredicateNode with OpaqueInitializedAssertionPredicate |
| // - OpaqueLoop*Nodes with new_init and new_stride, respectively. |
| // |
| // / init stride |
| // | | | |
| // | OpaqueLoopInitNode OpaqueLoopStrideNode / new_init new_stride |
| // Template | \ / | \ / |
| // Assertion | ... Assertion | ... |
| // Expression | | Expression | | |
| // | Bool | new Bool |
| // | | | | |
| // \ OpaqueTemplateAssertionPredicate ===> new_control \ OpaqueInitializedAssertionPredicate |
| // | \ / |
| // If new If |
| // / \ / \ |
| // success fail path new success new Halt |
| // proj (Halt or UCT) proj |
| // |
| InitializedAssertionPredicate InitializedAssertionPredicateCreator::create_from_template( |
| const IfNode* template_assertion_predicate, Node* new_control, Node* new_init, Node* new_stride) const { |
| OpaqueInitializedAssertionPredicateNode* assertion_expression = |
| create_assertion_expression_from_template(template_assertion_predicate, new_control, new_init, new_stride); |
| IfTrueNode* success_proj = create_control_nodes(new_control, |
| template_assertion_predicate->Opcode(), |
| assertion_expression, |
| template_assertion_predicate->assertion_predicate_type()); |
| return InitializedAssertionPredicate(success_proj); |
| } |
| |
| // Create a new Initialized Assertion Predicate from the provided Template Assertion Predicate at the template success |
| // projection by cloning it but omitting the OpaqueLoop*Notes (i.e. taking their inputs instead). |
| InitializedAssertionPredicate InitializedAssertionPredicateCreator::create_from_template_and_insert_below( |
| const TemplateAssertionPredicate& template_assertion_predicate) const { |
| TemplateAssertionExpression template_assertion_expression(template_assertion_predicate.opaque_node(), _phase); |
| IfTrueNode* template_assertion_predicate_success_proj = template_assertion_predicate.tail(); |
| OpaqueInitializedAssertionPredicateNode* assertion_expression = |
| template_assertion_expression.clone_and_fold_opaque_loop_nodes(template_assertion_predicate_success_proj); |
| |
| IfNode* template_assertion_predicate_if = template_assertion_predicate.head(); |
| AssertionPredicateType assertion_predicate_type = template_assertion_predicate_if->assertion_predicate_type(); |
| int if_opcode = template_assertion_predicate_if->Opcode(); |
| IfTrueNode* success_proj = create_control_nodes(template_assertion_predicate_success_proj, if_opcode, |
| assertion_expression, assertion_predicate_type); |
| return InitializedAssertionPredicate(success_proj); |
| } |
| |
| // Create a new Initialized Assertion Predicate directly without a template. |
| IfTrueNode* InitializedAssertionPredicateCreator::create(Node* operand, Node* new_control, const jint stride, |
| const int scale, Node* offset, Node* range, |
| const AssertionPredicateType assertion_predicate_type) const { |
| AssertionPredicateExpressionCreator expression_creator(stride, scale, offset, range, _phase); |
| bool does_overflow; |
| OpaqueInitializedAssertionPredicateNode* assertion_expression = |
| expression_creator.create_for_initialized(new_control, operand, does_overflow); |
| IfTrueNode* success_proj = create_control_nodes(new_control, does_overflow ? Op_If : Op_RangeCheck, |
| assertion_expression, assertion_predicate_type); |
| DEBUG_ONLY(InitializedAssertionPredicate::verify(success_proj);) |
| return success_proj; |
| } |
| |
| // Creates the CFG nodes for the Initialized Assertion Predicate. |
| IfTrueNode* InitializedAssertionPredicateCreator::create_control_nodes( |
| Node* new_control, const int if_opcode, OpaqueInitializedAssertionPredicateNode* assertion_expression, |
| const AssertionPredicateType assertion_predicate_type) const { |
| AssertionPredicateIfCreator assertion_predicate_if_creator(_phase); |
| return assertion_predicate_if_creator.create_for_initialized(new_control, if_opcode, assertion_expression, |
| assertion_predicate_type); |
| } |
| |
| // Create a new Assertion Expression based from the given template to be used as bool input for the Initialized |
| // Assertion Predicate IfNode. |
| OpaqueInitializedAssertionPredicateNode* |
| InitializedAssertionPredicateCreator::create_assertion_expression_from_template(const IfNode* template_assertion_predicate, |
| Node* new_control, Node* new_init, |
| Node* new_stride) const { |
| OpaqueTemplateAssertionPredicateNode* template_opaque = |
| template_assertion_predicate->in(1)->as_OpaqueTemplateAssertionPredicate(); |
| TemplateAssertionExpression template_assertion_expression(template_opaque, _phase); |
| OpaqueTemplateAssertionPredicateNode* tmp_opaque = |
| template_assertion_expression.clone_and_replace_init_and_stride(new_control, new_init, new_stride); |
| OpaqueInitializedAssertionPredicateNode* assertion_expression = |
| new OpaqueInitializedAssertionPredicateNode(tmp_opaque->in(1)->as_Bool(), _phase->C); |
| _phase->register_new_node(assertion_expression, new_control); |
| return assertion_expression; |
| } |
| |
| #ifndef PRODUCT |
| void PredicateBlock::dump() const { |
| dump(""); |
| } |
| |
| void PredicateBlock::dump(const char* prefix) const { |
| if (is_non_empty()) { |
| PredicatePrinter printer(prefix); |
| PredicateBlockIterator iterator(_tail, _deopt_reason); |
| iterator.for_each(printer); |
| } else { |
| tty->print_cr("%s- <empty>", prefix); |
| } |
| } |
| |
| // Dumps all predicates from the loop to the earliest predicate in a pretty format. |
| void Predicates::dump() const { |
| if (has_any()) { |
| Node* loop_head = _tail->unique_ctrl_out(); |
| tty->print_cr("%d %s:", loop_head->_idx, loop_head->Name()); |
| tty->print_cr("- Loop Limit Check Predicate Block:"); |
| _loop_limit_check_predicate_block.dump(" "); |
| tty->print_cr("- Auto Vectorization Check Block:"); |
| _auto_vectorization_check_block.dump(" "); |
| tty->print_cr("- Profiled Loop Predicate Block:"); |
| _profiled_loop_predicate_block.dump(" "); |
| tty->print_cr("- Loop Predicate Block:"); |
| _loop_predicate_block.dump(" "); |
| tty->cr(); |
| } else { |
| tty->print_cr("<no predicates>"); |
| } |
| } |
| |
| void Predicates::dump_at(Node* node) { |
| Predicates predicates(node); |
| predicates.dump(); |
| } |
| |
| // Debug method to dump all predicates that are found above 'loop_node'. |
| void Predicates::dump_for_loop(LoopNode* loop_node) { |
| dump_at(loop_node->skip_strip_mined()->in(LoopNode::EntryControl)); |
| } |
| #endif // NOT PRODUCT |
| |
| CreateAssertionPredicatesVisitor::CreateAssertionPredicatesVisitor(CountedLoopNode* target_loop_head, |
| PhaseIdealLoop* phase, |
| const NodeInLoopBody& node_in_loop_body, |
| bool kill_old_template) |
| : _init(target_loop_head->init_trip()), |
| _stride(target_loop_head->stride()), |
| _target_loop_head(target_loop_head), |
| _old_target_loop_entry(target_loop_head->skip_strip_mined()->in(LoopNode::EntryControl)), |
| _current_predicate_chain_head(target_loop_head->skip_strip_mined()), // Initially no predicates, yet. |
| _phase(phase), |
| _node_in_loop_body(node_in_loop_body), |
| _kill_old_template(kill_old_template) {} |
| |
| // Clone Template Assertion Predicate and create an Initialized Assertion Predicate from it. |
| void CreateAssertionPredicatesVisitor::visit(const TemplateAssertionPredicate& template_assertion_predicate) { |
| TemplateAssertionPredicate cloned_template_assertion_predicate = |
| clone_template_and_replace_init_input(template_assertion_predicate); |
| initialize_from_template(template_assertion_predicate, cloned_template_assertion_predicate.tail()); |
| _current_predicate_chain_head = cloned_template_assertion_predicate.head(); |
| if (_kill_old_template) { |
| template_assertion_predicate.kill(_phase->igvn()); |
| } |
| } |
| |
| // Create an Initialized Assertion Predicate from the provided Template Assertion Predicate. |
| InitializedAssertionPredicate CreateAssertionPredicatesVisitor::initialize_from_template( |
| const TemplateAssertionPredicate& template_assertion_predicate, IfTrueNode* cloned_template_predicate_tail) const { |
| DEBUG_ONLY(template_assertion_predicate.verify();) |
| IfNode* template_head = template_assertion_predicate.head(); |
| InitializedAssertionPredicateCreator initialized_assertion_predicate_creator(_phase); |
| InitializedAssertionPredicate initialized_assertion_predicate = |
| initialized_assertion_predicate_creator.create_from_template(template_head, cloned_template_predicate_tail, _init, |
| _stride); |
| |
| DEBUG_ONLY(initialized_assertion_predicate.verify();) |
| template_assertion_predicate.rewire_loop_data_dependencies(cloned_template_predicate_tail, |
| _node_in_loop_body, _phase); |
| rewire_to_old_predicate_chain_head(initialized_assertion_predicate.tail()); |
| return initialized_assertion_predicate; |
| } |
| |
| // Clone the provided Template Assertion Predicate and set '_init' as new input for the OpaqueLoopInitNode. |
| TemplateAssertionPredicate CreateAssertionPredicatesVisitor::clone_template_and_replace_init_input( |
| const TemplateAssertionPredicate& template_assertion_predicate) const { |
| TemplateAssertionPredicate new_template = |
| template_assertion_predicate.clone_and_replace_opaque_input(_old_target_loop_entry, _init, _target_loop_head, |
| _phase); |
| return new_template; |
| } |
| |
| // Rewire the newly created predicates to the old predicate chain head (i.e. '_current_predicate_chain_head') by |
| // rewiring the current control input of '_current_predicate_chain_head' from '_old_target_loop_entry' to |
| // 'initialized_assertion_predicate_success_proj'. This is required because we walk the predicate chain from the loop |
| // up and clone Template Assertion Predicates on the fly: |
| // |
| // x |
| // | old target |
| // Template Assertion loop entry |
| // Predicate 1 old target clone | \ |
| // | loop entry TAP 2 | cloned Template Assertion |
| // Template Assertion | ======> | Predicate 2 |
| // Predicate 2 target loop | |
| // | target loop #_current_predicate_chain_head |
| // source loop |
| // |
| // |
| // old target old target |
| // loop entry loop entry |
| // | \ rewire | |
| // | cloned Template Assertion to old cloned Template Assertion #current_predicate |
| // initialize | Predicate 2 predicate Predicate 2 _chain_head (new) |
| // TAP 2 | | chain head | |
| // ======> | Initialized Assertion ======> Initialized Assertion |
| // | Predicate 2 Predicate 2 |
| // | | |
| // target loop #_current_predicate_chain_head target loop |
| // |
| void CreateAssertionPredicatesVisitor::rewire_to_old_predicate_chain_head( |
| Node* initialized_assertion_predicate_success_proj) const { |
| if (_current_predicate_chain_head->is_Loop()) { |
| assert(_current_predicate_chain_head->in(LoopNode::EntryControl) == _old_target_loop_entry, "must be old loop entry"); |
| _phase->replace_loop_entry(_current_predicate_chain_head->as_Loop(), initialized_assertion_predicate_success_proj); |
| } else { |
| assert(_current_predicate_chain_head->in(0) == _old_target_loop_entry, "must be old loop entry"); |
| _phase->replace_control(_current_predicate_chain_head, initialized_assertion_predicate_success_proj); |
| } |
| } |
| |
| TargetLoopPredicateChain::TargetLoopPredicateChain(LoopNode* loop_head, PhaseIdealLoop* phase) |
| : DEBUG_ONLY(_old_target_loop_entry(loop_head->in(LoopNode::EntryControl)) COMMA) |
| DEBUG_ONLY(_node_index_before_cloning(phase->C->unique()) COMMA) |
| _current_predicate_chain_head(loop_head), |
| _phase(phase) {} |
| |
| // Inserts the provided newly cloned predicate to the head of the target loop predicate chain. |
| void TargetLoopPredicateChain::insert_predicate(const Predicate& predicate) { |
| rewire_to_target_chain_head(predicate.tail()->as_IfTrue()); |
| _current_predicate_chain_head = predicate.head(); |
| assert(predicate.head()->_idx >= _node_index_before_cloning, "must be a newly cloned predicate"); |
| assert(predicate.tail()->_idx >= _node_index_before_cloning, "must be a newly cloned predicate"); |
| assert(_current_predicate_chain_head->in(0) == _old_target_loop_entry && |
| _old_target_loop_entry->unique_ctrl_out() == _current_predicate_chain_head , "must be connected now"); |
| } |
| |
| void TargetLoopPredicateChain::rewire_to_target_chain_head(IfTrueNode* template_assertion_predicate_success_proj) const { |
| if (_current_predicate_chain_head->is_Loop()) { |
| _phase->replace_loop_entry(_current_predicate_chain_head->as_Loop(), template_assertion_predicate_success_proj); |
| } else { |
| _phase->replace_control(_current_predicate_chain_head, template_assertion_predicate_success_proj); |
| } |
| } |
| |
| ClonePredicateToTargetLoop::ClonePredicateToTargetLoop(LoopNode* target_loop_head, const NodeInLoopBody& node_in_loop_body, |
| PhaseIdealLoop* phase) |
| : _target_loop_head(target_loop_head), |
| _target_outer_loop_head(target_loop_head->skip_strip_mined()), |
| _old_target_loop_entry(_target_outer_loop_head->in(LoopNode::EntryControl)), |
| _target_loop_predicate_chain(_target_outer_loop_head, phase), |
| _node_in_loop_body(node_in_loop_body), |
| _phase(phase) {} |
| |
| // Clones the provided Template Assertion Predicate to the head of the current predicate chain at the target loop. |
| void ClonePredicateToTargetLoop::clone_template_assertion_predicate( |
| const TemplateAssertionPredicate& template_assertion_predicate) { |
| TemplateAssertionPredicate cloned_template_assertion_predicate = |
| template_assertion_predicate.clone(_old_target_loop_entry, _target_loop_head->as_CountedLoop(), _phase); |
| template_assertion_predicate.rewire_loop_data_dependencies(cloned_template_assertion_predicate.tail(), |
| _node_in_loop_body, _phase); |
| _target_loop_predicate_chain.insert_predicate(cloned_template_assertion_predicate); |
| } |
| |
| CloneUnswitchedLoopPredicatesVisitor::CloneUnswitchedLoopPredicatesVisitor( |
| LoopNode* true_path_loop_head, LoopNode* false_path_loop_head, |
| const NodeInOriginalLoopBody& node_in_true_path_loop_body, const NodeInClonedLoopBody& node_in_false_path_loop_body, |
| PhaseIdealLoop* phase) |
| : _clone_predicate_to_true_path_loop(true_path_loop_head, node_in_true_path_loop_body, phase), |
| _clone_predicate_to_false_path_loop(false_path_loop_head, node_in_false_path_loop_body, phase), |
| _phase(phase), |
| _is_counted_loop(true_path_loop_head->is_CountedLoop()) {} |
| |
| // The PredicateIterator will always start at the loop entry and first visits the Loop Limit Check Predicate Block. |
| // Does not clone a Loop Limit Check Parse Predicate if a counted loop is unswitched, because it most likely will not be |
| // used anymore (it could only be used when both unswitched loop versions die and the Loop Limit Check Parse Predicate |
| // ends up at a LoopNode without Loop Limit Check Parse Predicate directly following the unswitched loop that can then |
| // be speculatively converted to a counted loop - this is rather rare). |
| void CloneUnswitchedLoopPredicatesVisitor::visit(const ParsePredicate& parse_predicate) { |
| Deoptimization::DeoptReason deopt_reason = parse_predicate.head()->deopt_reason(); |
| if (_is_counted_loop && deopt_reason == Deoptimization::Reason_loop_limit_check) { |
| return; |
| } |
| _clone_predicate_to_true_path_loop.clone_parse_predicate(parse_predicate, false); |
| _clone_predicate_to_false_path_loop.clone_parse_predicate(parse_predicate, true); |
| parse_predicate.kill(_phase->igvn()); |
| } |
| |
| // Clone the Template Assertion Predicate, which is currently found before the newly added unswitched loop selector, |
| // to the true path and false path loop. |
| void CloneUnswitchedLoopPredicatesVisitor::visit(const TemplateAssertionPredicate& template_assertion_predicate) { |
| _clone_predicate_to_true_path_loop.clone_template_assertion_predicate(template_assertion_predicate); |
| _clone_predicate_to_false_path_loop.clone_template_assertion_predicate(template_assertion_predicate); |
| template_assertion_predicate.kill(_phase->igvn()); |
| } |
| |
| // Update the Template Assertion Predicate by setting a new input for the OpaqueLoopStrideNode. Create a new |
| // Initialized Assertion Predicate from the updated Template Assertion Predicate. |
| void UpdateStrideForAssertionPredicates::visit(const TemplateAssertionPredicate& template_assertion_predicate) { |
| if (!template_assertion_predicate.is_last_value()) { |
| // Only Last Value Assertion Predicates have an OpaqueLoopStrideNode. |
| return; |
| } |
| replace_opaque_stride_input(template_assertion_predicate); |
| template_assertion_predicate.update_associated_loop_node(_loop_node); |
| Node* template_tail_control_out = template_assertion_predicate.tail()->unique_ctrl_out(); |
| InitializedAssertionPredicate initialized_assertion_predicate = |
| initialize_from_updated_template(template_assertion_predicate); |
| connect_initialized_assertion_predicate(template_tail_control_out, initialized_assertion_predicate); |
| } |
| |
| // Kill the old Initialized Assertion Predicates with old strides before unrolling. The new Initialized Assertion |
| // Predicates are inserted after the Template Assertion Predicate which ensures that we are not accidentally visiting |
| // and killing a newly created Initialized Assertion Predicate here. |
| void UpdateStrideForAssertionPredicates::visit(const InitializedAssertionPredicate& initialized_assertion_predicate) { |
| if (initialized_assertion_predicate.is_last_value()) { |
| // Only Last Value Initialized Assertion Predicates need to be killed and updated. |
| initialized_assertion_predicate.kill(_phase->igvn()); |
| } |
| } |
| |
| // Replace the input to OpaqueLoopStrideNode with 'new_stride' and leave the other nodes unchanged. |
| void UpdateStrideForAssertionPredicates::replace_opaque_stride_input( |
| const TemplateAssertionPredicate& template_assertion_predicate) const { |
| template_assertion_predicate.replace_opaque_stride_input(_new_stride, _phase->igvn()); |
| } |
| |
| InitializedAssertionPredicate UpdateStrideForAssertionPredicates::initialize_from_updated_template( |
| const TemplateAssertionPredicate& template_assertion_predicate) const { |
| return template_assertion_predicate.initialize(_phase); |
| } |
| |
| // The newly created Initialized Assertion Predicate can safely be inserted because this visitor is already visiting |
| // the Template Assertion Predicate above this. So, we will not accidentally visit this again and kill it with the |
| // visit() method for Initialized Assertion Predicates. |
| void UpdateStrideForAssertionPredicates::connect_initialized_assertion_predicate( |
| Node* new_control_out, const InitializedAssertionPredicate& initialized_assertion_predicate) const { |
| Node* initialized_assertion_predicate_success_proj = initialized_assertion_predicate.tail(); |
| if (new_control_out->is_Loop()) { |
| _phase->replace_loop_entry(new_control_out->as_Loop(), initialized_assertion_predicate_success_proj); |
| } else { |
| _phase->replace_control(new_control_out, initialized_assertion_predicate_success_proj); |
| } |
| } |
| |
| // Do the following to find and eliminate useless Parse and Template Assertion Predicates: |
| // 1. Mark all Parse and Template Assertion Predicates "maybe useful". |
| // 2. Walk through the loop tree and iterate over all Predicates above each loop head. All found Parse and Template |
| // Assertion Predicates are marked "useful". |
| // 3. Those Parse and Template Assertion Predicates that are still marked "maybe useful" are now marked "useless" and |
| // removed in the next round of IGVN. |
| // |
| // Note that we only mark Predicates useless and not actually replace them now. We leave this work for IGVN which is |
| // better suited for this kind of graph surgery. We also not want to replace conditions with a constant to avoid |
| // interference with Predicate matching code when iterating through them. |
| void EliminateUselessPredicates::eliminate() const { |
| mark_all_predicates_maybe_useful(); |
| if (C->has_loops()) { |
| mark_loop_associated_predicates_useful(); |
| } |
| mark_maybe_useful_predicates_useless(); |
| } |
| |
| void EliminateUselessPredicates::mark_all_predicates_maybe_useful() const { |
| mark_predicates_on_list_maybe_useful(_parse_predicates); |
| mark_predicates_on_list_maybe_useful(_template_assertion_predicate_opaques); |
| } |
| |
| template<class PredicateList> |
| void EliminateUselessPredicates::mark_predicates_on_list_maybe_useful(const PredicateList& predicate_list) { |
| for (int i = 0; i < predicate_list.length(); i++) { |
| predicate_list.at(i)->mark_maybe_useful(); |
| } |
| } |
| |
| void EliminateUselessPredicates::mark_loop_associated_predicates_useful() const { |
| for (LoopTreeIterator iterator(_ltree_root); !iterator.done(); iterator.next()) { |
| IdealLoopTree* loop = iterator.current(); |
| if (loop->can_apply_loop_predication()) { |
| mark_useful_predicates_for_loop(loop); |
| } |
| } |
| } |
| |
| // This visitor marks all visited Parse and Template Assertion Predicates useful. |
| class PredicateUsefulMarkerVisitor : public PredicateVisitor { |
| LoopNode* const _loop_node; // The loop node from which we currently iterate through the Predicates. |
| |
| public: |
| explicit PredicateUsefulMarkerVisitor(LoopNode* loop_node) : _loop_node(loop_node) {} |
| NONCOPYABLE(PredicateUsefulMarkerVisitor); |
| |
| using PredicateVisitor::visit; |
| |
| void visit(const ParsePredicate& parse_predicate) override { |
| parse_predicate.head()->mark_useful(); |
| } |
| |
| // If the stored loop node does not match the current loop node from which we iterate from, we found a Template |
| // Assertion Predicate belonging to an already earlier folded loop in the graph. We mark it useless to drop this |
| // Template Assertion Predicate because we are no longer splitting a loop which it belongs to. Moreover, if we do not |
| // remove this Template Assertion Predicate, we could wrongly be creating Initialized Assertion Predicates from it at |
| // the new loop which has completely unrelated loop values. These Initialized Assertion Predicates can then fail at |
| // runtime, and we crash by executing a halt instruction. |
| void visit(const TemplateAssertionPredicate& template_assertion_predicate) override { |
| OpaqueTemplateAssertionPredicateNode* opaque_node = template_assertion_predicate.opaque_node(); |
| if (opaque_node->loop_node() == _loop_node) { |
| // We actually mark the associated OpaqueTemplateAssertionPredicate node useful. |
| template_assertion_predicate.opaque_node()->mark_useful(); |
| } |
| } |
| }; |
| |
| void EliminateUselessPredicates::mark_useful_predicates_for_loop(IdealLoopTree* loop) { |
| LoopNode* loop_node = loop->_head->as_Loop(); |
| Node* loop_entry = loop_node->skip_strip_mined()->in(LoopNode::EntryControl); |
| const PredicateIterator predicate_iterator(loop_entry); |
| PredicateUsefulMarkerVisitor predicate_useful_marker_visitor(loop_node); |
| predicate_iterator.for_each(predicate_useful_marker_visitor); |
| } |
| |
| // All Predicates still being marked MaybeUseful could not be found and thus are now marked useless. |
| void EliminateUselessPredicates::mark_maybe_useful_predicates_useless() const { |
| mark_maybe_useful_predicates_on_list_useless(_parse_predicates); |
| mark_maybe_useful_predicates_on_list_useless(_template_assertion_predicate_opaques); |
| DEBUG_ONLY(verify_loop_nodes_of_useless_templates_assertion_predicates_are_dead();) |
| } |
| |
| #ifdef ASSERT |
| // All now useless Template Assertion Predicates should not refer to any CountedLoopNode that can still be found in the |
| // graph (otherwise, they would have been marked useful instead). This is verified in this method. |
| void EliminateUselessPredicates::verify_loop_nodes_of_useless_templates_assertion_predicates_are_dead() const { |
| ResourceMark rm; |
| Unique_Node_List loop_nodes_of_useless_template_assertion_predicates = |
| collect_loop_nodes_of_useless_template_assertion_predicates(); |
| verify_associated_loop_nodes_are_dead(loop_nodes_of_useless_template_assertion_predicates); |
| } |
| |
| Unique_Node_List EliminateUselessPredicates::collect_loop_nodes_of_useless_template_assertion_predicates() const { |
| Unique_Node_List loop_nodes_of_useless_template_assertion_predicates; |
| for (int i = 0; i < _template_assertion_predicate_opaques.length(); i++) { |
| OpaqueTemplateAssertionPredicateNode* opaque_node = _template_assertion_predicate_opaques.at(i); |
| if (opaque_node->is_useless()) { |
| loop_nodes_of_useless_template_assertion_predicates.push(opaque_node->loop_node()); |
| } |
| } |
| return loop_nodes_of_useless_template_assertion_predicates; |
| } |
| |
| void EliminateUselessPredicates::verify_associated_loop_nodes_are_dead( |
| const Unique_Node_List& loop_nodes_of_useless_template_assertion_predicates) const { |
| if (loop_nodes_of_useless_template_assertion_predicates.size() == 0) { |
| return; |
| } |
| for (LoopTreeIterator iterator(_ltree_root); !iterator.done(); iterator.next()) { |
| IdealLoopTree* loop = iterator.current(); |
| Node* loop_head = loop->head(); |
| if (loop_head->is_CountedLoop()) { |
| assert(!loop_nodes_of_useless_template_assertion_predicates.member(loop_head), |
| "CountedLoopNode should be dead when found in OpaqueTemplateAssertionPredicateNode being marked useless"); |
| } |
| } |
| } |
| #endif // ASSERT |
| |
| template<class PredicateList> |
| void EliminateUselessPredicates::mark_maybe_useful_predicates_on_list_useless( |
| const PredicateList& predicate_list) const { |
| for (int i = 0; i < predicate_list.length(); i++) { |
| auto predicate_node = predicate_list.at(i); |
| if (!predicate_node->is_useful()) { |
| predicate_node->mark_useless(_igvn); |
| } |
| } |
| } |