oracle/graal · error · PermanentBailoutException

NormalizeCompareNode connected to %s (%s %s %s)

Error message

NormalizeCompareNode connected to %s (%s %s %s)

What it means

During CompareNode canonicalization, comparing an AbstractNormalizeCompareNode against a constant is only supported for the specific subclass overloads (e.g., Integer/Float CompareNode); the base-class fallback optimizeNormalizeCompare throws PermanentBailoutException listing the node, constant, normalize node, and mirrored flag. Hitting it means an unexpected comparison shape reached a compare node that cannot fold it — an internal invariant, not user configuration.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/calc/CompareNode.java:289

            return supported;
        }

        private static ConstantNode canonicalConvertConstant(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, CanonicalCondition condition, ConvertNode convert,
                        Constant constant, NodeView view) {
            if (convert.preservesOrder(condition, constant, constantReflection)) {
                Constant reverseConverted = convert.reverse(constant, constantReflection);
                if (reverseConverted != null && convert.convert(reverseConverted, constantReflection).equals(constant)) {
                    return ConstantNode.forConstant(convert.getValue().stamp(view), reverseConverted, metaAccess);
                }
            }
            return null;
        }

        @SuppressWarnings("unused")
        protected LogicNode optimizeNormalizeCompare(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth,
                        Constant constant, AbstractNormalizeCompareNode normalizeNode, boolean mirrored, NodeView view) {
            throw new PermanentBailoutException("NormalizeCompareNode connected to %s (%s %s %s)", this, constant, normalizeNode, mirrored);
        }

        private static LogicNode optimizeConditional(Constant constant, ConditionalNode conditionalNode, ConstantReflectionProvider constantReflection, Condition cond, boolean unorderedIsTrue) {
            Constant trueConstant = conditionalNode.trueValue().asConstant();
            Constant falseConstant = conditionalNode.falseValue().asConstant();

            if (falseConstant != null && trueConstant != null && constantReflection != null) {
                Stamp compareStamp = conditionalNode.trueValue().stamp(NodeView.DEFAULT);
                TriState trueResult = cond.foldCondition(compareStamp, trueConstant, constant, constantReflection, unorderedIsTrue);
                TriState falseResult = cond.foldCondition(compareStamp, falseConstant, constant, constantReflection, unorderedIsTrue);

                if (trueResult.isKnown() && trueResult.equals(falseResult)) {
                    return LogicConstantNode.forBoolean(trueResult.toBoolean());
                } else {
                    if (trueResult.isTrue() && falseResult.isFalse()) {
                        return conditionalNode.condition();
                    } else if (trueResult.isFalse() && falseResult.isTrue()) {
                        return LogicNegationNode.create(conditionalNode.condition());

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. File a GraalVM issue with -Dgraal.Dump=:2 output: the message already identifies the compare node, constant, and normalize node
  2. Work around by restructuring the comparison at the source level (e.g., use explicit Integer.compare/compareTo instead of chained relational expressions with unary minus patterns that form NormalizeCompare nodes)
  3. If developing custom nodes, override optimizeNormalizeCompare in your CompareNode subclass instead of relying on the throwing default

Example fix

// before: exotic pattern that folds to normalize-compare against a constant
boolean b = (a < b ? -1 : a > b ? 1 : 0) == SOME_CONST;

// after: plain comparison the canonicalizer handles
boolean b = SOME_CONST == Integer.compare(a, b);
Defensive patterns

Strategy: try-catch

Try / catch

try { compile(m); } catch (PermanentBailoutException e) { /* internal canonicalization invariant: report upstream with -Dgraal.Dump=:2 */ fallbackToBaseline(m, e); }

Prevention

When it happens

Trigger: A CompareNode subclass whose canonicalization delegates to the generic optimizeNormalizeCompare (i.e., no constant-folding support for the type combination) encounters normalize(x,y) OP constant during canonicalization. This requires a node/intrinsic producing an unusual normalize-compare usage pattern, typically from generated graphs or new node types.

Common situations: Custom Graal plugins or languages introducing new compare node types; rare shapes in auto-generated (bytecode-generated) code; effectively never in normal application compilation — essentially a bug-class error.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/d30e7ee5b8161d48. Report an issue: GitHub.