JDK-8346420 : C2: IfNode::fold_compares_helper() wrongly folds two CmpI nodes to a single CmpU node
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 9
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2024-12-17
  • Updated: 2025-12-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 27
27Unresolved
Related Reports
Relates :  
Relates :  
Description
I extracted a simple reproducer Test.java where a condition is wrongly evaluated leading to executing the wrong path in an if/else.

Interpreter works:
$ java -Xint Test.java

Compiler wrongly throws RuntimeException:
$ java -XX:CompileOnly=Test::test -Xcomp Test.java

Exception in thread "main" java.lang.RuntimeException
	at Test.test(Test.java:33)
	at Test.main(Test.java:7)

-----------------
Original report:

ADDITIONAL SYSTEM INFORMATION :
Issue was reproduced on Linux x86, MacOS arm, Windows 10. Reproducible on Java 18-23.
e.g.:
openjdk version "21.0.5" 2024-10-15 LTS
OpenJDK Runtime Environment Temurin-21.0.5+11 (build 21.0.5+11-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.5+11 (build 21.0.5+11-LTS, mixed mode, sharing)


A DESCRIPTION OF THE PROBLEM :
ByteBuddy sporadically runs into an "impossible" exception in net.bytebuddy.dynamic.scaffold.TypeWriter.Default.ValidatingClassVisitor.visitField. This code path can happen just with switch statement skipping all branches including default. In general this condition is rare, but running org.example.App with -Xcomp reliably reproduces this condition on Java 18-23.

Exception in thread "main" java.lang.IllegalStateException: Field POOL_NORMAL defines an incompatible default value 0 at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ValidatingClassVisitor.visitField(TypeWriter.java:2535) at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor.onVisitField(TypeWriter.java:5164) at net.bytebuddy.utility.visitor.MetadataAwareClassVisitor.visitField(MetadataAwareClassVisitor.java:278) at net.bytebuddy.jar.asm.ClassVisitor.visitField(ClassVisitor.java:356) at net.bytebuddy.jar.asm.commons.ClassRemapper.visitField(ClassRemapper.java:169) at net.bytebuddy.jar.asm.ClassReader.readField(ClassReader.java:1138) at net.bytebuddy.jar.asm.ClassReader.accept(ClassReader.java:740) at net.bytebuddy.utility.AsmClassReader$Default.accept(AsmClassReader.java:132) at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:4039) at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:2246) at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter.make(DynamicType.java:4085) at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3769) at org.example.App.main(App.java:20)

REGRESSION : Last worked in version 17.0.13

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Provided in external source.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Switch statement executing default branch
ACTUAL -
Switch statement skipping default branch and application running into an impossible condition.

---------- BEGIN SOURCE ----------
package org.example;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.*;
import net.bytebuddy.dynamic.*;
public class App {

    private static final int POOL_NORMAL = 0;
    private static final int POOL_SUSPENDED = 1;
    private static final int POOL_SHUTDOWN = 2;

    public volatile int poolState;

    public static void main(String[] args) {
        System.out.println("Running on:"+System.getProperty("java.version"));
        DynamicType.Builder<App> builder = new ByteBuddy().with(TypeValidation.ENABLED)
                .redefine(App.class)
                .name(App.class.getName() + "_Redefine");
        for (int i = 0; i < 100; i++) {
            builder.make();
        }
    }
}
---------- END SOURCE ----------

FREQUENCY : rarely 
Comments
Draft: https://github.com/openjdk/jdk/pull/28819
15-12-2025

As documented in the Test.java reproducer C2 converts the if expression from if (one < minimum || one > maximum) { throw new RuntimeException(); } to if (one - minimum <u (maximum - minimum) + one) { } else { throw new RuntimeException(); } in IGVN run 6. This is the first optimization (or any transformation after RemoveUseless) after parsing that modifies the graph in any way and is performed in IfNode::fold_compares_helper, line 969 and following. This transformed expression is already not equivalent to the original test anymore. This can be checked by running Integer.compareUnsigned(1 - Integer.MIN_VALUE, (Integer.MAX_VALUE - Integer.MIN_VALUE) + 1) which returns 1, meaning greater. The transformed expression does indeed work if either maximum or minimum are not Integer.MAX_INT or Integer.MIN_INT, respectively. Furthermore, let's note that performing the same comparison with longs instead of ints, yields the correct result, thereby confirming that the issue is caused by an overflow. jshell> Long.compareUnsigned(1L - Integer.MIN_VALUE, ((long)Integer.MAX_VALUE - (long)Integer.MIN_VALUE) + 1L) $49 ==> -1 This gives rise to two ways of addressing this issue therefore: - Find a static test that confirms no overflow will occur. Given the limited type information usually available, such a test would be very conservative and probably disable this optimization most of the time. - Switch the optimization to work with longs instead of ints. This would be a straightforward and effective way of preventing overflows.
22-01-2025

ILW = Wrong execution of C2 compiled code, only with edge case values/rare, disable compilation of affected method = HLM = P3
08-01-2025

Looks like a regression from JDK-8081823.
08-01-2025