JDK-8374582: add opaque node

This commit is contained in:
Damon Fenacci 2026-01-09 17:38:19 +01:00
parent 3c466d372b
commit 3c6722d949
8 changed files with 51 additions and 4 deletions

View File

@ -282,6 +282,7 @@ macro(OpaqueLoopStride)
macro(OpaqueMultiversioning)
macro(OpaqueZeroTripGuard)
macro(OpaqueNotNull)
macro(OpaqueGuard)
macro(OpaqueInitializedAssertionPredicate)
macro(OpaqueTemplateAssertionPredicate)
macro(ProfileBoolean)

View File

@ -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(),

View File

@ -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.

View File

@ -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()) {

View File

@ -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)

View File

@ -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),

View File

@ -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.

View File

@ -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) {