diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index d56abaa17dd..5b24d7b6a10 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -282,6 +282,7 @@ macro(OpaqueLoopStride) macro(OpaqueMultiversioning) macro(OpaqueZeroTripGuard) macro(OpaqueNotNull) +macro(OpaqueGuard) macro(OpaqueInitializedAssertionPredicate) macro(OpaqueTemplateAssertionPredicate) macro(ProfileBoolean) diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index fdb3ab89b82..856e5d6fb82 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -1235,6 +1235,7 @@ bool IdealLoopTree::policy_range_check(PhaseIdealLoop* phase, bool provisional, } if (!bol->is_Bool()) { assert(bol->is_OpaqueNotNull() || + bol->is_OpaqueGuard() || bol->is_OpaqueTemplateAssertionPredicate() || bol->is_OpaqueInitializedAssertionPredicate() || bol->is_OpaqueMultiversioning(), diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 9e3d4681e7c..ba5845f672f 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1705,6 +1705,7 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) { !n->is_MergeMem() && !n->is_CMove() && !n->is_OpaqueNotNull() && + !n->is_OpaqueGuard() && !n->is_OpaqueInitializedAssertionPredicate() && !n->is_OpaqueTemplateAssertionPredicate() && !is_raw_to_oop_cast && // don't extend live ranges of raw oops @@ -2228,7 +2229,7 @@ void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, Node_List &old_new, // split if to break. assert(!use->is_OpaqueTemplateAssertionPredicate(), "should not clone a Template Assertion Predicate which should be removed once it's useless"); - if (use->is_If() || use->is_CMove() || use->is_OpaqueNotNull() || use->is_OpaqueInitializedAssertionPredicate() || + if (use->is_If() || use->is_CMove() || use->is_OpaqueGuard() || use->is_OpaqueNotNull() || use->is_OpaqueInitializedAssertionPredicate() || (use->Opcode() == Op_AllocateArray && use->in(AllocateNode::ValidLengthTest) == old)) { // Since this code is highly unlikely, we lazily build the worklist // of such Nodes to go split. diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 80818a4ddc7..3bcc289a2dd 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2501,6 +2501,7 @@ void PhaseMacroExpand::eliminate_macro_nodes() { n->Opcode() == Op_ModD || n->Opcode() == Op_ModF || n->is_OpaqueNotNull() || + n->is_OpaqueGuard() || n->is_OpaqueInitializedAssertionPredicate() || n->Opcode() == Op_MaxL || n->Opcode() == Op_MinL || @@ -2556,6 +2557,17 @@ void PhaseMacroExpand::eliminate_opaque_looplimit_macro_nodes() { _igvn.replace_node(n, n->in(1)); #else _igvn.replace_node(n, _igvn.intcon(1)); +#endif + success = true; + } else if (n->is_OpaqueGuard()) { + // Tests with OpaqueGuard nodes are implicitly known to be true or false. Replace the node with appropriate value. In debug builds, + // we leave the test in the graph to have an additional sanity check at runtime. If the test fails (i.e. a bug), + // we will execute a Halt node. +#ifdef ASSERT + _igvn.replace_node(n, n->in(1)); +#else + bool is_positive = n->as_OpaqueGuard()->is_positive(); + _igvn.replace_node(n, _igvn.intcon(is_positive?1:0)); #endif success = true; } else if (n->is_OpaqueInitializedAssertionPredicate()) { diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 0adb2072100..3b554f13f00 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -143,6 +143,7 @@ class OpaqueLoopInitNode; class OpaqueLoopStrideNode; class OpaqueMultiversioningNode; class OpaqueNotNullNode; +class OpaqueGuardNode; class OpaqueInitializedAssertionPredicateNode; class OpaqueTemplateAssertionPredicateNode; class OuterStripMinedLoopNode; @@ -822,6 +823,7 @@ public: DEFINE_CLASS_ID(Move, Node, 20) DEFINE_CLASS_ID(LShift, Node, 21) DEFINE_CLASS_ID(Neg, Node, 22) + DEFINE_CLASS_ID(OpaqueGuard, Node, 23) _max_classes = ClassMask_Neg }; @@ -997,6 +999,7 @@ public: DEFINE_CLASS_QUERY(NeverBranch) DEFINE_CLASS_QUERY(Opaque1) DEFINE_CLASS_QUERY(OpaqueNotNull) + DEFINE_CLASS_QUERY(OpaqueGuard) DEFINE_CLASS_QUERY(OpaqueInitializedAssertionPredicate) DEFINE_CLASS_QUERY(OpaqueTemplateAssertionPredicate) DEFINE_CLASS_QUERY(OpaqueLoopInit) diff --git a/src/hotspot/share/opto/opaquenode.cpp b/src/hotspot/share/opto/opaquenode.cpp index 29d625db190..4b7356cb1aa 100644 --- a/src/hotspot/share/opto/opaquenode.cpp +++ b/src/hotspot/share/opto/opaquenode.cpp @@ -112,6 +112,10 @@ const Type* OpaqueNotNullNode::Value(PhaseGVN* phase) const { return phase->type(in(1)); } +const Type* OpaqueGuardNode::Value(PhaseGVN* phase) const { + return phase->type(in(1)); +} + OpaqueTemplateAssertionPredicateNode::OpaqueTemplateAssertionPredicateNode(BoolNode* bol, CountedLoopNode* loop_node) : Node(nullptr, bol), _loop_node(loop_node), diff --git a/src/hotspot/share/opto/opaquenode.hpp b/src/hotspot/share/opto/opaquenode.hpp index 9edfe2a7258..21e64f0745d 100644 --- a/src/hotspot/share/opto/opaquenode.hpp +++ b/src/hotspot/share/opto/opaquenode.hpp @@ -148,6 +148,31 @@ class OpaqueNotNullNode : public Node { virtual const Type* bottom_type() const { return TypeInt::BOOL; } }; +// Similar to OpaqueNotNullNode but for guards. Sometimes we know that a size or limit guard is checked +// (e.g. there is already a guard in the caller) but the compiler cannot prove it. We could in principle avoid +// adding a guard in the intrinsic but in some cases (e.g. when the input is a constant that breaks the guard +// and the caller guard is not inlined) the input of the intrinsic can become top and the data path is folded. +// Similar to OpaqueNotNullNode to ensure that the control path is also properly folded, we insert a +// OpaqueGuardNode before the If node in the guard. During macro expansion, we replace the OpaqueGuardNode with +// the corresponding constant (true/false) in product builds such that the actually unneeded guards +// are folded and do not end up in the emitted code. In debug builds, we keep the actual checks as additional +// verification code (i.e. removing OpaqueGuardNode and use the BoolNode inputs instead). +class OpaqueGuardNode : public Node { + private: + bool _is_positive; + public: + OpaqueGuardNode(Compile* C, Node* tst, bool is_positive) : Node(nullptr, tst), _is_positive(is_positive) { + init_class_id(Class_OpaqueGuard); + init_flags(Flag_is_macro); + C->add_macro_node(this); + } + + virtual int Opcode() const; + virtual const Type* Value(PhaseGVN* phase) const; + virtual const Type* bottom_type() const { return TypeInt::BOOL; } + bool is_positive() const { return _is_positive; } +}; + // This node is used for Template Assertion Predicate BoolNodes. A Template Assertion Predicate is always removed // after loop opts and thus is never converted to actual code. In the post loop opts IGVN phase, the // OpaqueTemplateAssertionPredicateNode is replaced by true in order to fold the Template Assertion Predicate away. diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index 425c0c9e99d..6ba5541288e 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -307,7 +307,7 @@ bool PhaseIdealLoop::clone_cmp_down(Node* n, const Node* blk1, const Node* blk2) assert( bol->is_Bool(), "" ); if (bol->outcnt() == 1) { Node* use = bol->unique_out(); - if (use->is_OpaqueNotNull() || use->is_OpaqueTemplateAssertionPredicate() || + if (use->is_OpaqueNotNull() || use->is_OpaqueGuard() || use->is_OpaqueTemplateAssertionPredicate() || use->is_OpaqueInitializedAssertionPredicate()) { if (use->outcnt() == 1) { Node* iff = use->unique_out(); @@ -331,8 +331,8 @@ bool PhaseIdealLoop::clone_cmp_down(Node* n, const Node* blk1, const Node* blk2) // Recursively sink any BoolNode for (DUIterator j = bol->outs(); bol->has_out(j); j++) { Node* u = bol->out(j); - // Uses are either IfNodes, CMoves, OpaqueNotNull, or Opaque*AssertionPredicate - if (u->is_OpaqueNotNull() || u->is_OpaqueTemplateAssertionPredicate() || + // Uses are either IfNodes, CMoves, OpaqueNotNull, OpaqueGuard or Opaque*AssertionPredicate + if (u->is_OpaqueNotNull() || u->is_OpaqueGuard() || u->is_OpaqueTemplateAssertionPredicate() || u->is_OpaqueInitializedAssertionPredicate()) { assert(u->in(1) == bol, "bad input"); for (DUIterator_Last kmin, k = u->last_outs(kmin); k >= kmin; --k) {