JDK-8361211 : C2: Final graph reshaping generates unencodeable klass constants
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 24,26
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2025-07-01
  • Updated: 2025-12-10
  • Resolved: 2025-08-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 25 JDK 26
25.0.2Fixed 26 b10Fixed
Related Reports
Blocks :  
Causes :  
Relates :  
Relates :  
Relates :  
Description
I am seeing the CTW failures with JDK-8360557, which manifest with the following assert:

#  Internal Error (/home/shade/trunks/jdk/src/hotspot/share/oops/compressedKlass.inline.hpp:80), pid=3869033, tid=3869052
#  assert(is_encodable(addr)) failed: Address 0x000070470c568fa0 is not encodable (Klass range: [0x000000002c001000 - 0x000000006d000000), (1090514944 bytes), klass alignment: 8)

Current CompileTask:
C2:2968 2272   !b  4       org.apache.cassandra.auth.Auth::setupDefaultSuperuser (74 bytes)

Stack: [0x000070473c1f9000,0x000070473c2f9000],  sp=0x000070473c2f47c0,  free space=1005k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0x15e1e1a]  CompressedKlassPointers::encode_not_null(Klass*)+0xba  (compressedKlass.inline.hpp:80)
V  [libjvm.so+0x15ba5f7]  MacroAssembler::cmp_narrow_klass(Register, Klass*)+0xb7  (compressedKlass.inline.hpp:73)
V  [libjvm.so+0x17ba912]  PhaseOutput::scratch_emit_size(Node const*)+0x4a2  (output.cpp:3388)
V  [libjvm.so+0x17b2852]  PhaseOutput::shorten_branches(unsigned int*)+0x382  (output.cpp:540)
V  [libjvm.so+0x17c5458]  PhaseOutput::Output()+0xad8  (output.cpp:340)
V  [libjvm.so+0xb68c90]  Compile::Code_Gen()+0xa20  (compile.cpp:3109)
V  [libjvm.so+0xb6df93]  Compile::Compile(ciEnv*, ciMethod*, int, Options, DirectiveSet*)+0x2023  (compile.cpp:891)
V  [libjvm.so+0x99ee90]  C2Compiler::compile_method(ciEnv*, ciMethod*, int, bool, DirectiveSet*)+0x450  (c2compiler.cpp:141)
V  [libjvm.so+0xb7d432]  CompileBroker::invoke_compiler_on_method(CompileTask*)+0xb22  (compileBroker.cpp:2323)
V  [libjvm.so+0xb7e458]  CompileBroker::compiler_thread_loop()+0x5a8  (compileBroker.cpp:1967)
V  [libjvm.so+0x10d3d4f]  JavaThread::thread_main_inner()+0x12f  (javaThread.cpp:773)
V  [libjvm.so+0x1cdde3a]  Thread::call_run()+0xba  (thread.cpp:243)
V  [libjvm.so+0x17983a8]  thread_native_entry(Thread*)+0x128  (os_linux.cpp:868)
C  [libc.so.6+0x9caa4]

It is fairly reproducible on one of the internal JARs, and only with specific StressSeed-s. So there must be some high-level optimization in play.

Adding the assert like this:
 
+TypeNarrowKlass::TypeNarrowKlass(const TypePtr* ptrtype): TypeNarrowPtr(NarrowKlass, ptrtype) {
+  if (ptrtype->ptr() == TypePtr::Constant) {
+    CompressedKlassPointers::check_encodable((void*)ptrtype->get_con());
+  }
+}

Starts to fail here:

Stack: [0x000071208d300000,0x000071208d400000],  sp=0x000071208d3fac50,  free space=1003k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0x1d0eb32]  TypeNarrowKlass::TypeNarrowKlass(TypePtr const*) [clone .part.0]+0xa2  (compressedKlass.inline.hpp:80)
V  [libjvm.so+0x1d193ae]  TypeNarrowKlass::make(TypePtr const*)+0xae  (type.cpp:5322)
V  [libjvm.so+0xb594dd]  Compile::final_graph_reshaping_main_switch(Node*, Final_Reshape_Counts&, unsigned int, Unique_Node_List&)+0x1e9d  (compile.cpp:3589)
V  [libjvm.so+0xb59adf]  Compile::final_graph_reshaping_impl(Node*, Final_Reshape_Counts&, Unique_Node_List&) [clone .part.0]+0x15f  (compile.cpp:3204)
V  [libjvm.so+0xb5a47b]  Compile::final_graph_reshaping_walk(Node_Stack&, Node*, Final_Reshape_Counts&, Unique_Node_List&)+0x1fb  (compile.cpp:3153)
V  [libjvm.so+0xb6426d]  Compile::final_graph_reshaping()+0x66d  (compile.cpp:4019)
V  [libjvm.so+0xb6b0d1]  Compile::Optimize()+0x1051  (compile.cpp:2568)
V  [libjvm.so+0xb6de93]  Compile::Compile(ciEnv*, ciMethod*, int, Options, DirectiveSet*)+0x1f23  (compile.cpp:858)
V  [libjvm.so+0x99ee90]  C2Compiler::compile_method(ciEnv*, ciMethod*, int, bool, DirectiveSet*)+0x450  (c2compiler.cpp:141)
V  [libjvm.so+0xb7d432]  CompileBroker::invoke_compiler_on_method(CompileTask*)+0xb22  (compileBroker.cpp:2323)
V  [libjvm.so+0xb7e458]  CompileBroker::compiler_thread_loop()+0x5a8  (compileBroker.cpp:1967)
V  [libjvm.so+0x10d3d4f]  JavaThread::thread_main_inner()+0x12f  (javaThread.cpp:773)
V  [libjvm.so+0x1cdde3a]  Thread::call_run()+0xba  (thread.cpp:243)
V  [libjvm.so+0x17983a8]  thread_native_entry(Thread*)+0x128  (os_linux.cpp:868)

In other words, here:
https://github.com/openjdk/jdk/blob/38f59f84c98dfd974eec0c05541b2138b149def7/src/hotspot/share/opto/compile.cpp#L3588-L3590

        } else if (t->isa_klassptr()) {
          new_in2 = ConNode::make(t->make_narrowklass());
        }

That code seems to assume that if we are holding the klass pointer, it would always be encodeable as narrow klass pointer. But this is not true at least since JDK-8338526. 

This issue looks like a missing case similar to JDK-8343206. I think Tobias' analysis at the time was that we never reach this code with the klass that is not abstract/interface. But this CTW failure serves as a direct counter-example to that. In fact, I can throw in an assert like this:

        } else if (t->isa_klassptr()) {
          ciKlass* klass = t->is_klassptr()->exact_klass();
          assert(!klass->is_abstract() && !klass->is_interface(),
                 "Hey, is it really fine? abstract: %s; interface: %s",
                 BOOL_TO_STR(klass->is_abstract()), BOOL_TO_STR(klass->is_interface()));
          new_in2 = ConNode::make(t->make_narrowklass());
        }

...and see it failing:

#  Internal Error (/home/shade/trunks/jdk/src/hotspot/share/opto/compile.cpp:3590), pid=3920890, tid=3920942
#  assert(!klass->is_abstract() && !klass->is_interface()) failed: Hey, is it really fine? abstract: true; interface: false

Comments
The jar for verification is not provided.
10-12-2025

[jdk25u-fix-request] Approval Request from Aleksey Shipilëv Fixes a corner case with abstract/interface class encoding in C2. Seen in extended CTW tests. While the optimization would be disabled in mainline and JDK 25 eventually, this fix stands on its own, and gives us an additional safety now. The fix was in mainline for 3 weeks without bugtail. Applies cleanly; all tests pass. Risk is on medium-lower side: while it touches C2, it cleanly bails from incorrect transformation.
25-08-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk25u/pull/128 Date: 2025-08-25 10:24:29 +0000
25-08-2025

I was waiting to see what we are about to do with the whole optimization in 25u, I see it is tracked by JDK-8365823 now. But there is still a marginal value in checking the encodeable range for klasses in final graph reshaping. So I'll do the backport to 25u as well.
25-08-2025

I think it should be.
22-08-2025

[~shade] I wonder if this should be backported to JDK 25u ?
20-08-2025

Changeset: e304d379 Branch: master Author: Aleksey Shipilev <shade@openjdk.org> Date: 2025-08-06 08:32:25 +0000 URL: https://git.openjdk.org/jdk/commit/e304d37996b075b8b2b44b5762d7d242169add49
06-08-2025

Raising priority of this to P2, similar to JDK-8343218. ILW = Assert during C2 compilation because klass is not encodeable as narrow, with CTW and stress options, no workaround but disable compilation or compressed klass pointers = HLH = P2
04-08-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/26559 Date: 2025-07-30 16:20:43 +0000
30-07-2025

(moved the comment to JDK-8343218)
30-07-2025

I am going to plug the leak in final graph reshaping here. But we should really consider reopening JDK-8343218 and disabling the abstract/interface encoding optimization until C2 is properly patched up.
30-07-2025

ILW = Assert during C2 compilation because klass is not encodeable as narrow, with CTW and stress options, no workaround but disable compilation or compressed klass pointers = HLM = P3
04-07-2025

I have a POC fix that seems to resolve the failure: ``` diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 4c5f382ceee..13bdefb7eeb 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -3586,7 +3586,10 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f } else if (t->isa_oopptr()) { new_in2 = ConNode::make(t->make_narrowoop()); } else if (t->isa_klassptr()) { - new_in2 = ConNode::make(t->make_narrowklass()); + ciKlass* klass = t->is_klassptr()->exact_klass(); + if (klass->is_in_encoding_range()) { + new_in2 = ConNode::make(t->make_narrowklass()); + } } } if (new_in2 != nullptr) { @@ -3623,7 +3626,10 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f } else if (t->isa_oopptr()) { n->subsume_by(ConNode::make(t->make_narrowoop()), this); } else if (t->isa_klassptr()) { - n->subsume_by(ConNode::make(t->make_narrowklass()), this); + ciKlass* klass = t->is_klassptr()->exact_klass(); + if (klass->is_in_encoding_range()) { + n->subsume_by(ConNode::make(t->make_narrowklass()), this); + } } } if (in1->outcnt() == 0) { ```
01-07-2025