JDK-8339303 : C2: dead node after failing to match cloned address expression
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 21,23,24
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • CPU: x86_64
  • Submitted: 2024-08-05
  • Updated: 2024-11-11
  • Resolved: 2024-11-06
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 24
24 b23Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
Similar to JDK-8202952, the failure is caused by a leftover dead node from failing to subsume into a memory operand a shared address expression that is wrongly marked as clonable by x86's implementation of `Matcher::pd_clone_address_expressions`. In this case, the shared address expression consists of two chained `AddP` nodes with a small constant offset each (see before-matching.pdf), a pattern that is wrongly recognized as subsumable into a complex addressing mode by x86's `Matcher::pd_clone_address_expressions`. Accordingly, C2's matcher builds the state tree depicted in match.pdf, where the address expression AddP(AddP ..), ..) is duplicated and matched by two identical Mach instructions (`109 leaPCompressedOopOffset` and `111 leaPCompressedOopOffset`). Similarly to the case in JDK-8202952, the `Expand` method of `108 subI_mem_rReg` selects one of them (`109 leaPCompressedOopOffset`) and leaves the other one (`111 leaPCompressedOopOffset`) dead but referenced from `110 loadConN`'s output set. When GCM visits `110 loadConN`, it assumes that all its outputs have been already assigned a basic block, and causes a segmentation fault when fetching the (unassigned) basic block of `111 leaPCompressedOopOffset`.

A potential solution is to not mark the above pattern (two chained `AddP` nodes with a small constant offset each, see before-matching.pdf) as clonable in x86's implementation of `Matcher::pd_clone_address_expressions`. Note that the pattern should be optimized by `AddPNode::Ideal` into a single `AddP` node with the constant sum of the offsets (in this case it is not, due to JDK-8343067), but it is desirable to not rely on the application of specific optimizations for correctness.

ORIGINAL REPORT:
-------------------------

ADDITIONAL SYSTEM INFORMATION :
# JRE version: Java(TM) SE Runtime Environment (21.0.4+8) (build 21.0.4+8-LTS-274)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (21.0.4+8-LTS-274, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64)

A DESCRIPTION OF THE PROBLEM :
We attempted to use R8 to generate more concise class files. During our attempt, we encountered an issue: when running the R8-processed class files with jdk-21.0.4-oracle, the JVM crashes. The specific crash information is detailed in the hs_err file. We believe this could be an issue with the JDK because it does not occur when using OpenJ9 or JDK-17.

R8 is a code shrinker and obfuscation tool provided by Google for Android. It optimizes Java bytecode into a smaller and more efficient format, reducing the size of applications and improving performance. R8 replaces ProGuard, integrating code shrinking and obfuscation features, and improves compilation efficiency through tight integration with the D8 compiler.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. javac Test.java
2. jar cvf classes.jar ./*.class
3. java -cp r8.jar com.android.tools.r8.R8 --pg-conf rules.pro --release --classfile --output ./r8 ./classes.jar
4. java -cp ./r8 Test

```

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
no output
ACTUAL -
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x0000712d9ee4251d, pid=467817, tid=467831
#
# JRE version: Java(TM) SE Runtime Environment (21.0.4+8) (build 21.0.4+8-LTS-274)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (21.0.4+8-LTS-274, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
# Problematic frame:
# V  [libjvm.so+0x84251d]  Node_Backward_Iterator::next()+0x11d
#
# Core dump will be written. Default location: Core dumps may be processed with "/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E" (or dumping to /data/user/proj/core.467817)
#
# An error report file with more information is saved as:
# /data/user/proj/hs_err_pid467817.log
#
# Compiler replay data is saved as:
# /data/user/proj/replay_pid467817.log
#
# If you would like to submit a bug report, please visit:
#   https://bugreport.java.com/bugreport/crash.jsp
#

Current CompileTask:
C2:     41    6 % !   4       Test::main @ 60 (171 bytes)

Stack: [0x0000712d46600000,0x0000712d46700000],  sp=0x0000712d466fbf80,  free space=1007k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0x84251d]  Node_Backward_Iterator::next()+0x11d
V  [libjvm.so+0x844999]  PhaseCFG::schedule_late(VectorSet&, Node_Stack&)+0x79
V  [libjvm.so+0x845161]  PhaseCFG::global_code_motion()+0x241
V  [libjvm.so+0x84695a]  PhaseCFG::do_global_code_motion()+0x4a
V  [libjvm.so+0x643e45]  Compile::Code_Gen()+0x285
V  [libjvm.so+0x648024]  Compile::Compile(ciEnv*, ciMethod*, int, Options, DirectiveSet*)+0x1584
V  [libjvm.so+0x574d5d]  C2Compiler::compile_method(ciEnv*, ciMethod*, int, bool, DirectiveSet*)+0x17d
V  [libjvm.so+0x64d7e7]  CompileBroker::invoke_compiler_on_method(CompileTask*)+0xa97
V  [libjvm.so+0x6508d8]  CompileBroker::compiler_thread_loop()+0x6a8
V  [libjvm.so+0x8fd9b8]  JavaThread::thread_main_inner() [clone .part.0]+0xb8
V  [libjvm.so+0xea7848]  Thread::call_run()+0xa8
V  [libjvm.so+0xcca7da]  thread_native_entry(Thread*)+0xda

timeout: the monitored command dumped core


---------- BEGIN SOURCE ----------
class Test {
  static int a = 56;
  long b;
  int c;
  int w[];
  static int d[] = new int[a];

  void e() {
    int h[][] = new int[a][a];
    long k[] = new long[a];
    for (int i = 0; i < h.length; ++i)
      for (int j = 0; j < h.length; ++j)
        ;
    long m = 0;
    for (int i = 0; i < k.length; ++i)
      m = k[i];
  }

  void r() {
    // short t = 4;
    int i;
    int p = 5;
    long q;
    for (i = 0; i < 216; i++)
      e();
    for (q = 1; q < 3; q++)
      d[(int) q] -= p;
    try {
      w[i] = i;
    } catch (Throwable throwable) {
    }
    w = d;
  }

  public static void main(String[] u) {
    Test v = new Test();
    for (int i = 0; i < 20; ++i)
      v.r();
  }
}
---------- END SOURCE ----------


Comments
Changeset: 83f3d42d Branch: master Author: Roberto CastaƱeda Lozano <rcastanedalo@openjdk.org> Date: 2024-11-06 09:17:21 +0000 URL: https://git.openjdk.org/jdk/commit/83f3d42d6bcefac80449987f4d951f8280eeee3a
06-11-2024

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/21829 Date: 2024-11-01 13:53:33 +0000
04-11-2024

Thanks for the heads-up Roberto. Great job extracting a "standalone" test! Updated ILW = = Crash during C2 compilation in GCM, reproducible with simple test but edge case, no workaround but disable compilation of affected method = HMM = P2
04-11-2024

[~thartmann] in case this affects the priority level, the issue can be reproduced using bytecode produced directly by javac, see https://github.com/robcasloz/jdk/blob/JDK-8339303-bwd-iterator-crash/test/hotspot/jtreg/compiler/c2/TestMatcherTwoImmOffsets.java. The only particular condition required is a specific compressed Oops mode (using address shifting, i.e. -Xmx > 2g).
01-11-2024

Roberto, please have a look once you get a chance.
17-10-2024

ILW = Crash during C2 compilation in GCM, single test using shrinked/obfuscated bytecode not directly produced by javac, no workaround but disable compilation of affected method = HLM = P3
07-10-2024

We crash here: 0x000074e56bbcbaa7 in Node_Backward_Iterator::next (this=this@entry=0x74e5544fb900) at /oracle/valhalla/open/src/hotspot/share/opto/gcm.cpp:927 927 uint use_rpo = _cfg.get_block_for_node(use)->_rpo; _cfg._node_to_block_mapping[154] is NULL. Maybe the problem is that the node does not seem to have any uses: (rr) p use->dump(2) 18 StartOSR === 18 1 [[ 18 17 19 20 21 45 48 0 112 199 153 287 286 278 61 60 ]] #{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address, 5:rawptr:BotPTR} !jvms: Test::main @ bci:60 (line 11) o626 ConN === o0 [[ o627 o1158 o1159 ]] #narrowoop: int[int:56] (java/lang/Cloneable,java/io/Serializable)<ciTypeArray length=56 type=<ciTypeArrayKlass name=[I loaded=true ident=1417 address=0x00005f790e1db5f8> ident=1430 address=0x00005f790e3663c0> * 153 loadConN === 18 [[ 156 154 152 171 ]] narrowoop: int[int:56] (java/lang/Cloneable,java/io/Serializable)<ciTypeArray length=56 type=<ciTypeArrayKlass name=[I loaded=true ident=1417 address=0x00005f790e1db5f8> ident=1430 address=0x00005f790e3663c0> * o1158 DecodeN === _ o626 [[ o925 o926 o926 154 ]] #int[int:56] (java/lang/Cloneable,java/io/Serializable)<ciTypeArray length=56 type=<ciTypeArrayKlass name=[I loaded=true ident=1417 address=0x00005f790e1db5f8> ident=1430 address=0x00005f790e3663c0> * 154 leaPCompressedOopOffset === _ o1158 153 [[ ]] int[int:56] (java/lang/Cloneable,java/io/Serializable)<ciTypeArray length=56 type=<ciTypeArrayKlass name=[I loaded=true ident=1417 address=0x00005f790e1db5f8> ident=1430 address=0x00005f790e3663c0>+4 * The IR before code GCM looks sane on a first glance.
07-10-2024

First reproduces after JDK-8303466 in JDK 21 b20. Might be related to JDK-8308504.
07-10-2024

Thanks, I can reproduce the issue now and mainline (JDK 24) is affected as well.
07-10-2024

Additional Information from submitter: ====================================== These are the class files we used during our testing process, and the specific methods to generate them are as follows: <LINK> 1. Compile to generate the classfile: javac -source 8 -target 8 -cp . Test.java 2. Package the classfile into a jar: jar cvf classes.jar ./*.class 3. Use R8 to convert the jar into a dex file: java -cp r8.jar com.android.tools.r8.R8 --pg-conf proguard-rules.pro --release --output ./ ./classes.jar 4. Use R8 to simplify the jar and convert it back to a classfile: java -cp r8.jar com.android.tools.r8.R8 --pg-conf proguard-rules.pro --release --classfile --output ./r8 ./classes.jar Among them, there were no errors when running the dex file generated in step 3 on host-art, nor when running the classfile generated in step 1 on the JVM. However, when running the classfile generated in step 4 on the JVM, the previously reported issue occurred.
04-10-2024

Resolving as Incomplete until we wait for the class file.
05-09-2024

It would be better if the classfile produced by R8 was provided - or even better an asm/jasm equivalent so we can actually see the result of the obfuscation.
02-09-2024