JDK-8356989 : Unexpected null in C2 compiled code
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 21,25
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2025-05-09
  • Updated: 2025-05-26
  • Resolved: 2025-05-26
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 25
25 masterFixed
Related Reports
Causes :  
Description
ADDITIONAL SYSTEM INFORMATION :
macOS version:

```java
Software:

    System Software Overview:

      System Version: macOS 13.2 (22D49)
      Kernel Version: Darwin 22.3.0
      Boot Volume: Macintosh HD
      Boot Mode: Normal
      Computer Name: MacBook Pro
      User Name: MacBook
      Secure Virtual Memory: Enabled
      System Integrity Protection: Enabled
      Time since boot: 1 day, 2 hours, 19 minutes

Hardware:

    Hardware Overview:

      Model Name: MacBook Pro
      Model Identifier: MacBookPro18,1
      Model Number: MK193CH/A
      Chip: Apple M1 Pro
      Total Number of Cores: 10 (8 performance and 2 efficiency)
      Memory: 16 GB
      System Firmware Version: 8419.80.7
      OS Loader Version: 8419.80.7
      Activation Lock Status: Disabled
```

OpenJDK version:

```jsx
openjdk version "21.0.7" 2025-04-15 LTS
OpenJDK Runtime Environment Temurin-21.0.7+6 (build 21.0.7+6-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.7+6 (build 21.0.7+6-LTS, mixed mode, sharing)
```

A DESCRIPTION OF THE PROBLEM :
### Description:

The provided test case triggers a `java.lang.NullPointerException` when executed on **OpenJDK 21**, while the same code does not exhibit this behavior on alternative JVM implementations, such as OpenJ9. Interestingly, the exception disappears if any other line in the test program is removed, suggesting that this issue may be related to JIT compiler optimizations.

```java
public class Test {

    public static int[] arr = new int[500];
    public static void main(String[] args) {
        try {
            int a = 7, b = 14;
            for (int i = 16; i < 338; ++i) {
                for (long l = 4; l < 78; ++l) {
                    Double.doubleToLongBits(checkSum(new double[400]));
                    java.util.List<java.lang.String> sequencedList = new java.util.ArrayList<>(java.util.List.of("A", "B", "C"));
                    sequencedList.addFirst("Z");
                    sequencedList.getLast().length();
                }
                try {
                    a = (a % Test.arr[i]);
                } catch (ArithmeticException a_e) {
                }
                b = 123;
                do {
                } while (b-- > 0);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static double checkSum(double[] a) {
        double sum = 0;
        for (int j = 0; j < a.length; j++) {
            sum += (a[j] / (j + 1) + a[j] % (j + 1));
        }
        return sum;
    }
}
```

### Observed Behavior:

When executing the program using **OpenJDK 21**, the following exception is thrown:

```
java.lang.NullPointerException: Cannot invoke "String.length()" because the return value of "java.util.List.getLast()" is null
        at Test.main(Test.java:12)
```

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile the `Test.java` file using `javac`.
2. Run the `Test` class  using OpenJDK 21.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The program should execute normally and terminate without throwing any exceptions.
ACTUAL -
The program throws a NullPointerException due to an unexpected null return from List.getLast(), which should not occur under normal conditions.

---------- BEGIN SOURCE ----------
public class Test {

    public static int[] arr = new int[500];
    public static void main(String[] args) {
        try {
            int a = 7, b = 14;
            for (int i = 16; i < 338; ++i) {
                for (long l = 4; l < 78; ++l) {
                    Double.doubleToLongBits(checkSum(new double[400]));
                    java.util.List<java.lang.String> sequencedList = new java.util.ArrayList<>(java.util.List.of("A", "B", "C"));
                    sequencedList.addFirst("Z");
                    sequencedList.getLast().length();
                }
                try {
                    a = (a % Test.arr[i]);
                } catch (ArithmeticException a_e) {
                }
                b = 123;
                do {
                } while (b-- > 0);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static double checkSum(double[] a) {
        double sum = 0;
        for (int j = 0; j < a.length; j++) {
            sum += (a[j] / (j + 1) + a[j] % (j + 1));
        }
        return sum;
    }
}
---------- END SOURCE ----------


Comments
Changeset: ed4cd2ac Branch: master Author: Roland Westrelin <roland@openjdk.org> Date: 2025-05-26 08:33:37 +0000 URL: https://git.openjdk.org/jdk/commit/ed4cd2acd2d8bb92c296c5a860c76cffaff53add
26-05-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/25389 Date: 2025-05-22 09:22:08 +0000
22-05-2025

Reproduces with attached test case: $ java -XX:-TieredCompilation -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileOnly=TestArrayCopyInitializesNonEscapingArray::test1 TestArrayCopyInitializesNonEscapingArray Exception in thread "main" java.lang.RuntimeException: Can't be null at TestArrayCopyInitializesNonEscapingArray.test1(TestArrayCopyInitializesNonEscapingArray.java:21) at TestArrayCopyInitializesNonEscapingArray.main(TestArrayCopyInitializesNonEscapingArray.java:8) Key to reproducing this is to have a single non escaping allocation as source and destination of an ArrayCopy
20-05-2025

ILW = Incorrect result (unexpected null) in C2 compiled code, easy to reproduce, disable compilation or EA of affected method = HMM = P2
15-05-2025

I attached a simplified version of the test: java -Xbatch -XX:-TieredCompilation -XX:CompileCommand=quiet -XX:CompileCommand=compileonly,Test::test Test.java Exception in thread "main" java.lang.RuntimeException: Unexpected null at Test.test(Test.java:10) at Test.main(Test.java:16) It seems to be a regression from JDK-8297933 in JDK 21 b05.
15-05-2025

Reproduces with latest JDK 25 as well: java -Xbatch -XX:-TieredCompilation Test.java java.lang.NullPointerException: Cannot invoke "String.length()" because the return value of "java.util.List.getLast()" is null at Test.main(Test.java:12) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) at java.base/java.lang.reflect.Method.invoke(Method.java:565) at jdk.compiler/com.sun.tools.javac.launcher.SourceLauncher.execute(SourceLauncher.java:254) at jdk.compiler/com.sun.tools.javac.launcher.SourceLauncher.run(SourceLauncher.java:138) at jdk.compiler/com.sun.tools.javac.launcher.SourceLauncher.main(SourceLauncher.java:76)
15-05-2025

To reproduce with JDK 25, use Test2.java java -Xbatch -XX:-TieredCompilation -XX:CompileCommand=quiet -XX:CompileCommand=compileonly,Test2::test Test2.java Some incorrect folding seems to happen in ConnectionGraph::optimize_ptr_compare (workaround: -XX:-OptimizePtrCompare).
15-05-2025

Got a consistent error: java.lang.NullPointerException: Cannot invoke "String.length()" because the return value of "java.util.List.getLast()" is null at Test.main(Test.java:12) When testing with: OpenJDK 64-Bit Server VM (build 21.0.1+12-29, mixed mode, sharing) Java HotSpot(TM) 64-Bit Server VM (build 21.0.5+9-LTS-239, mixed mode, sharing) Java HotSpot(TM) 64-Bit Server VM (build 22.0.1+8-16, mixed mode, sharing) OpenJDK 64-Bit Server VM (build 22.0.2+9-70, mixed mode, sharing) Java HotSpot(TM) 64-Bit Server VM (build 23.0.1+11-39, mixed mode, sharing) ILW = Tests that are blocking further testing, Consistent failure in a test, No viable workaround = HHH => P1 Moved to JDK
14-05-2025