JDK-8296412 : Special case infinite loops with unmerged backedges in IdealLoopTree::check_safepts
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 17,19,20,21
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2022-11-04
  • Updated: 2022-12-19
  • Resolved: 2022-12-19
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
21 masterFixed
Related Reports
Relates :  
Relates :  
Description
Working hypothesis:
Maybe backedge of irreducible loop is missing a SafePoint.

During fuzzing work of JDK-8280126, I found an S.jasm that triggers the following assert:

#  Internal Error (/home/emanuel/Documents/fork2-jdk/open/src/hotspot/share/opto/loopnode.cpp:3610), pid=159269, tid=159282
#  assert(is_member(nlpt)) failed: nested loop
#
# JRE version: Java(TM) SE Runtime Environment (20.0) (slowdebug build 20-internal-2022-10-06-1045569.emanuel...)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (slowdebug 20-internal-2022-10-06-1045569.emanuel..., compiled mode, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
# Problematic frame:
# V  [libjvm.so+0xf5cb86]  IdealLoopTree::check_safepts(VectorSet&, Node_List&)+0x200

Current CompileTask:
C2:    742   26    b        S::test (27 bytes)

Stack: [0x00007f4a65dfe000,0x00007f4a65eff000],  sp=0x00007f4a65ef9090,  free space=1004k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0xf5cb86]  IdealLoopTree::check_safepts(VectorSet&, Node_List&)+0x200  (loopnode.cpp:3610)
V  [libjvm.so+0xf5c9f7]  IdealLoopTree::check_safepts(VectorSet&, Node_List&)+0x71  (loopnode.cpp:3584)
V  [libjvm.so+0xf5c9cf]  IdealLoopTree::check_safepts(VectorSet&, Node_List&)+0x49  (loopnode.cpp:3583)
V  [libjvm.so+0xf5ff1f]  PhaseIdealLoop::build_and_optimize()+0x8bf  (loopnode.cpp:4358)
V  [libjvm.so+0x8a6343]  PhaseIdealLoop::PhaseIdealLoop(PhaseIterGVN&, LoopOptsMode)+0x105  (loopnode.hpp:1087)
V  [libjvm.so+0x8a653e]  PhaseIdealLoop::optimize(PhaseIterGVN&, LoopOptsMode)+0x46  (loopnode.hpp:1166)
V  [libjvm.so+0x899177]  Compile::Optimize()+0xa69  (compile.cpp:2365)
V  [libjvm.so+0x892104]  Compile::Compile(ciEnv*, ciMethod*, int, Options, DirectiveSet*)+0x1404  (compile.cpp:830)
V  [libjvm.so+0x780a9b]  C2Compiler::compile_method(ciEnv*, ciMethod*, int, bool, DirectiveSet*)+0x179  (c2compiler.cpp:113)
V  [libjvm.so+0x8b0df8]  CompileBroker::invoke_compiler_on_method(CompileTask*)+0x916  (compileBroker.cpp:2240)
V  [libjvm.so+0x8afa61]  CompileBroker::compiler_thread_loop()+0x3ed  (compileBroker.cpp:1916)
V  [libjvm.so+0x8d01aa]  CompilerThread::thread_entry(JavaThread*, JavaThread*)+0x72  (compilerThread.cpp:58)
V  [libjvm.so+0xc5e0cc]  JavaThread::thread_main_inner()+0x144  (javaThread.cpp:699)
V  [libjvm.so+0xc5df84]  JavaThread::run()+0x182  (javaThread.cpp:684)
V  [libjvm.so+0x13306e7]  Thread::call_run()+0x195  (thread.cpp:224)
V  [libjvm.so+0x10ddf15]  thread_native_entry(Thread*)+0x19b  (os_linux.cpp:710)

Reproduce it:
java -jar ~/Documents/asmtools-7.0-build/release/lib/asmtools.jar jasm S.jasm
java -Xcomp -XX:CompileCommand=compileonly,S::test -XX:-TieredCompilation -XX:PerMethodTrapLimit=0 -XX:-RenumberLiveNodes S

Note that the flags
 -XX:PerMethodTrapLimit=0 -XX:-RenumberLiveNodes
are not required, but simplify the graph by removing traps (no predicates inserted), and the node idx are stable as they are not renumbered during remove useless.

While it is well possible that this is only reproducilbe with irreducible loops, it does not seem to have to do with dead irreducible loops, so it is a separate bug from JDK-8280126.

Analysis:
(please look at attached png's for node idx)
In IdealLoopTree::check_safepts.
this:
  Loop: N37/N57
    Loop: N37/N56  sfpts={ 44 }
We start walking from n = tail() up the n = idom(n):
57  IfFalse
55  If
We realize that 55 If belongs to:
Loop: N37/N56
Hence, we jump to head of that loop, so set n:
37  Region
(note: this is the loop head of "this" loop, we should abort the idom walk)
We take n = idom(n), and get:
25  IfTrue
(this node is outside the loop, but the assumption of the code is that we are still inside the loop)
This node's loop (nlpt) is:
Loop: N0/N0  has_sfpt
  Loop: N103/N102  sfpts={ 82 70 }
  Loop: N37/N57
    Loop: N37/N56  sfpts={ 44 }
(this is the root loop)
Now we hit the assert, we check that "nlpt" is a member of "this".

Why does this usually work?
Usually, when we have a nested loop, where the loop is shared, we seem to always have a SafePoint on the backedge of the outer loop. So when we exit the inner loop, we expect to go through a SafePoint before we take the backedge to the loop head.
This seems not to be given, and may well have to do with the fact that we have an irreducible loop here (37 and 38 Region).

What could be solutions?
I have not really understood the issue. But some first thoughts include:
1) Checking when we jump to the loop-head of the inner loop, if this is also the loop head of the outer loop. But this would be accepting that backedges of outer-loops do not have safepoints.
2) Insert the safepoint during parsing.
Comments
Changeset: da38d43f Author: Emanuel Peter <epeter@openjdk.org> Date: 2022-12-19 12:21:50 +0000 URL: https://git.openjdk.org/jdk/commit/da38d43fcc640ea9852db6c7c23817dcef7080d5
19-12-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/11706 Date: 2022-12-16 09:57:35 +0000
16-12-2022

Current understanding: Issue is not irreducibility (at least not primarily). Rather, we have an issue if a loop-entry (or just loop-head) comes after (a part of) its loop body. During parsing, we only insert SafePoints when we jump from a higher to lower bci. That ensures every backedge has a SafePoint (at least initially). But if the head is after the body, there is no SafePoint on the backedge, but the SafePoint is somewhere in the loop-body. This is not expected in the logic of IdealLoopTree::check_safepts. There is an issue for irreducible loops: at least one loop-entry cannot be the first block of the loop. If all other loop-entries were to lose their entry, we now have a loop-head that is not in the first block, hence no SafePoints on backedges.
13-12-2022

ILW = assert in debug build; debug only, jasm test; no workaround = MMH = P3
05-11-2022