JDK-8327423 : C2 remove_main_post_loops: check if main-loop belongs to pre-loop, not just assert
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 21,22,23
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2024-03-06
  • Updated: 2024-06-13
  • Resolved: 2024-03-14
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 21 JDK 23
21.0.5-oracleFixed 23 b15Fixed
Related Reports
Relates :  
Relates :  
Description
# A fatal error has been detected by the Java Runtime Environment:
#
#  Internal Error (workspace/open/src/hotspot/share/opto/loopTransform.cpp:3304), pid=58340, tid=40195
#  assert(locate_pre_from_main(main_head) == cl) failed: bad main loop
#
# JRE version: Java(TM) SE Runtime Environment (23.0+13) (fastdebug build 23-ea+13-927)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (fastdebug 23-ea+13-927, compiled mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, bsd-aarch64)
# No core dump will be written. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again

Current thread (0x00000001200ca410):  JavaThread "C2 CompilerThread0" daemon [_thread_in_native, id=24835, stack(0x000000016f170000,0x000000016f373000) (2060K)]

Current CompileTask:
C2:302   73    b        Test::mainTest (540 bytes)

Stack: [0x000000016f170000,0x000000016f373000],  sp=0x000000016f36e970,  free space=2042k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.dylib+0x112b6bc]  VMError::report_and_die(int, char const*, char const*, char*, Thread*, unsigned char*, void*, void*, char const*, int, unsigned long)+0x564  (loopTransform.cpp:3304)
V  [libjvm.dylib+0x112bedc]  VMError::report_and_die(Thread*, unsigned int, unsigned char*, void*, void*)+0x0
V  [libjvm.dylib+0x55bee4]  print_error_for_unit_test(char const*, char const*, char*)+0x0
V  [libjvm.dylib+0xc72bb0]  IdealLoopTree::remove_main_post_loops(CountedLoopNode*, PhaseIdealLoop*)+0x120
V  [libjvm.dylib+0xc7300c]  IdealLoopTree::do_remove_empty_loop(PhaseIdealLoop*)+0x134
V  [libjvm.dylib+0xc74510]  IdealLoopTree::iteration_split_impl(PhaseIdealLoop*, Node_List&)+0x84
V  [libjvm.dylib+0xc74ccc]  IdealLoopTree::iteration_split(PhaseIdealLoop*, Node_List&)+0xec
V  [libjvm.dylib+0xc74d98]  IdealLoopTree::iteration_split(PhaseIdealLoop*, Node_List&)+0x1b8
V  [libjvm.dylib+0xc74d98]  IdealLoopTree::iteration_split(PhaseIdealLoop*, Node_List&)+0x1b8
V  [libjvm.dylib+0xc950e0]  PhaseIdealLoop::build_and_optimize()+0xaa8
V  [libjvm.dylib+0x4d5158]  PhaseIdealLoop::PhaseIdealLoop(PhaseIterGVN&, LoopOptsMode)+0x184
V  [libjvm.dylib+0x4c7b30]  PhaseIdealLoop::optimize(PhaseIterGVN&, LoopOptsMode)+0x6c
V  [libjvm.dylib+0x4c7fa8]  Compile::optimize_loops(PhaseIterGVN&, LoopOptsMode)+0x68
V  [libjvm.dylib+0x4c13a0]  Compile::Optimize()+0x98c
V  [libjvm.dylib+0x4bf810]  Compile::Compile(ciEnv*, ciMethod*, int, Options, DirectiveSet*)+0x1234
V  [libjvm.dylib+0x388fd0]  C2Compiler::compile_method(ciEnv*, ciMethod*, int, bool, DirectiveSet*)+0x1e0
V  [libjvm.dylib+0x4ddc50]  CompileBroker::invoke_compiler_on_method(CompileTask*)+0x854
V  [libjvm.dylib+0x4dd094]  CompileBroker::compiler_thread_loop()+0x348
V  [libjvm.dylib+0x8b37d8]  JavaThread::thread_main_inner()+0x1dc
V  [libjvm.dylib+0x106f57c]  Thread::call_run()+0xf4
V  [libjvm.dylib+0xe325a0]  thread_native_entry(Thread*)+0x138
C  [libsystem_pthread.dylib+0x7240]  _pthread_start+0x94

Comments
[jdk21u-fix-request] Approval Request from Martin Should get backported for parity with 21.0.25-oracle. It is recognized as clean backport and tier 1-4 have passed.
13-06-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jdk21u-dev/pull/701 Date: 2024-06-12 10:48:06 +0000
12-06-2024

Changeset: fadc4b19 Author: Emanuel Peter <epeter@openjdk.org> Date: 2024-03-14 07:12:16 +0000 URL: https://git.openjdk.org/jdk/commit/fadc4b197e927cfa1814fe6cb65ee04b3bd4b0c2
14-03-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/18200 Date: 2024-03-11 15:56:38 +0000
11-03-2024

The assert was added in JDK-8085832, by [~roland]. And in JDK-8297724, he made more empty loops be removed, so that probably just enabled this exact case here. Since the assert is from JDK-8085832, this could be a JDK9 regression. But so far we have only a reproducer that works since JDK-8297724, i.e. JDK21.
08-03-2024

I further simplified the regression test: public class Reduced { static int sink; public static void main(String[] strArr) { test(false); } static void test(boolean flag) { int x = 8; for (int j = 0; j < 100; j++) { for (int k = 0; k < 100; k++) { if (flag) { x += k; sink = 42; } } if (flag) { break; } } } } Analysis: Through a series of unswitching, pre-main-post, and empty-loop-removal, we get to the CFG of "image1_CFG_before_assert.png". (rr) p _ltree_root->dump() Loop: N0/N0 has_sfpt Loop: N425/N431 limit_check profile_predicated predicated counted [0,int),+1 (4 iters) pre sfpts={ 429 } Loop: N298/N301 profile_predicated predicated counted [0,int),+1 (4 iters) pre Loop: N200/N179 counted [int,100),+1 (2147483648 iters) main sfpts={ 171 } Loop: N398/N404 counted [int,100),+1 (4 iters) post sfpts={ 402 } This is basically: 415 pre orange 298 pre PURPLE 200 main orange 398 post orange Then, we attempt to remove "298", because it is empty. As [~chagedorn] has described above: "I've had a quick look and it looks like when trying to remove a pre-loop without a main loop, we find an unrelated main loop by using the IdealLoopTree::_next->_head info. Afterward, we find its pre-loop which is a different pre-loop and the assertion fails." It seems that we assume in the code, that we can check the "_next->_head", and if: 1) it is a main-loop and 2) that main-loop still has a pre-loop then the current pre-loop "cl" must be the pre-loop of that found main-loop "locate_pre_from_main(main_head)". But I don't think this is generally guaranteed by "PhaseIdealLoop::build_loop_tree". I think the loop-tree is correct here, and this is how it was arrived at: "415 CountedLoop" (pre orange) is visited, and its body traversed. "427 If" is traversed. Now the path splits. If we first took the "428 IfFalse" path, then we would visit "200 CountedLoop" (main orange), and "398 CountedLoop" (post orange) first. But we instead take "432 IfTrue" first, and hence visit "298 CountedLoop" (pre PURPLE) first. So depending on what turn we take at this "427 If", we either get the order: 415 pre orange 298 pre PURPLE 200 main orange 398 post orange (the one we get, and assert with) OR 415 pre orange 200 main orange 398 post orange 298 pre PURPLE (assert woud not trigger, since we would have "_next == nullptr" and return) I think we have to convert the assert into a bailout-condition, i.e. simply return from "remove_main_post_loops" without removing any main/post loops.
08-03-2024

Also fails on my machine: emanuel@emanuel-oracle:/oracle-work/jdk-fork8/build/linux-x64-slowdebug/jdk/bin$ ../../../linux-x64-debug/jdk/bin/java -Xcomp -XX:CompileOnly=Reduced::* Reduced.java CompileCommand: compileonly Reduced.* bool compileonly = true # # A fatal error has been detected by the Java Runtime Environment: # # Internal Error (/oracle-work/jdk-fork8/open/src/hotspot/share/opto/loopTransform.cpp:3304), pid=193149, tid=193163 # assert(locate_pre_from_main(main_head) == cl) failed: bad main loop # # JRE version: Java(TM) SE Runtime Environment (23.0) (fastdebug build 23-internal-2024-03-05-1531402.emanuel...) # Java VM: Java HotSpot(TM) 64-Bit Server VM (fastdebug 23-internal-2024-03-05-1531402.emanuel..., compiled mode, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64) # Problematic frame: # V [libjvm.so+0x12843a8] IdealLoopTree::remove_main_post_loops(CountedLoopNode*, PhaseIdealLoop*)+0x268 # # 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 /oracle-work/jdk-fork8/build/linux-x64-slowdebug/jdk/bin/core.193149) # # An error report file with more information is saved as: # /oracle-work/jdk-fork8/build/linux-x64-slowdebug/jdk/bin/hs_err_pid193149.log # # Compiler replay data is saved as: # /oracle-work/jdk-fork8/build/linux-x64-slowdebug/jdk/bin/replay_pid193149.log # # If you would like to submit a bug report, please visit: # https://bugreport.java.com/bugreport/crash.jsp #
07-03-2024

ILW = Assertion when trying to remove empty loop, single fuzzer test, possibly disable compilation of affected method = HLM = P3
06-03-2024

Attached reduced version Reduced.java: $ java -Xcomp -XX:CompileOnly=Reduced::* Reduced.java
06-03-2024

Starts to fail after JDK-8297724 but I think that could have just revealed an existing issue. I've had a quick look and it looks like when trying to remove a pre-loop without a main loop, we find an unrelated main loop by using the IdealLoopTree::_next->_head info. Afterward, we find its pre-loop which is a different pre-loop and the assertion fails.
06-03-2024