RFR: 8355354: C2 crashed: assert(_callee == nullptr || _callee == m) failed: repeated inline attempt with different callee

Vladimir Ivanov vlivanov at openjdk.org
Thu Aug 21 18:42:55 UTC 2025


On Thu, 21 Aug 2025 00:22:53 GMT, Dean Long <dlong at openjdk.org> wrote:

>> # Issue
>> The CTW test `applications/ctw/modules/java_xml.java` crashes when trying to repeat late inlining of a virtual method (after IGVN passes through the method's call node again). The failure originates [here](https://github.com/openjdk/jdk/blob/e2ae50d877b13b121912e2496af4b5209b315a05/src/hotspot/share/opto/callGenerator.cpp#L473) because `_callee != m`. Apparently when running IGVN a second time after a first late inline failure and [setting the callee in the call generator](https://github.com/openjdk/jdk/blob/e2ae50d877b13b121912e2496af4b5209b315a05/src/hotspot/share/opto/callnode.cpp#L1240) we notice that the previous callee is not the same as the current one.
>> In this specific instance it seems that the issue happens when CTW is compiling Apache Xalan.
>> 
>> # Cause
>> The root of the issue has to do with repeated late inlining, class hierarchy analysis and dynamic class loading.
>> 
>> For this particular issue the two differing methods are `org.apache.xalan.xsltc.compiler.LocationPathPattern::translate` first and `org.apache.xalan.xsltc.compiler.AncestorPattern::translate` the second time. `LocationPathPattern` is an abstract class but has a concrete `translate` method. `AncestorPattern` is a concrete class that extends another abstract class `RelativePathPattern` that extends `LocationPathPattern`. `AncestorPattern` overrides the translate method.
>> What seems to be happening is the following: we compile a virtual call `RelativePathPattern::translate` and at compile time. Only the abstract classes `RelativePathPattern` <: `LocationPathPattern` are loaded. CHA then finds out that the call must always call `LocationPathPattern::translate` because the method is not overwritten anywhere else. However, there is still no non-abstract class in the entire class hierarchy, i.e. as soon as `AncestorPattern` is loaded, this class is then the only non-abstract class in the class hierarchy and therefore the receiver type must be `AncestorPattern`.
>> 
>> More in general, when late inlining is repeated and classes are loaded dynamically, it is possible that the resolved method between a late inlining attempt and the next one is not the same.
>> 
>> # Fix
>> 
>> This looks like a very edge-case. If CHA is affected by class loading the original recorded dependency becomes invalid. So, we change the assert to **check for invalid dependencies if the current callee and the previous one don't match**.
>> 
>> # Testing
>> 
>> This issue is very very, very intermittent and d...
>
> src/hotspot/share/opto/callGenerator.cpp line 487:
> 
>> 485:         "repeated inline attempt with different callee");
>> 486:     }
>> 487: #endif
> 
> I'm wondering if there might be other reasons that the callee might change, like JVMTI class redefinition.  Also, it sounds like the CHA case is really rare, and we check dependencies at the end anyway, so the easiest fix for class redefinition and CHA would be to ignore the new callee and keep the old one here.

I second that. And it aligns with our effort to make CI queries report stable results.  

(That's what I proposed to Damon privately: "Another alternative is to cache and reuse cg->callee_method() when it becomes non-null. And turn repeated CHA requests (Compile::optimize_inlining) into verification logic.")

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/26441#discussion_r2291857763


More information about the hotspot-compiler-dev mailing list