openj9 icon indicating copy to clipboard operation
openj9 copied to clipboard

JITServer crash in Compilation::getReloTypeForMethodToBeInlined

Open mpirvu opened this issue 5 months ago • 9 comments

When running AcmeAirEE8 benchmark with AOT enabled the "cold" run (the one with an empty SCC) the JITServer can experience a segfault with the following call stack:

#15 OMR::ResolvedMethodSymbol::getResolvedMethod (this=0x0) at /team/mpirvu/Build/FullJava21/openj9-openjdk-jdk21/omr/compiler/il/OMRResolvedMethodSymbol.hpp:143
#16 J9::Compilation::getReloTypeForMethodToBeInlined (this=this@entry=0x7f08a7000000, guard=guard@entry=0x7f08a70c8ff0, callNode=callNode@entry=0x7f08a71f19a0,
    receiverClass=receiverClass@entry=0xeac000) at /team/mpirvu/Build/FullJava21/openj9-openjdk-jdk21/openj9/runtime/compiler/compile/J9Compilation.cpp:1210
#17 0x00007f09107d8635 in OMR::Compilation::incInlineDepth (this=0x7f08a7000000, method=0x7f08a70fb360, callNode=0x7f08a71f19a0, directCall=<optimized out>, guard=0x7f08a70c8ff0,
    receiverClass=0xeac000, argInfo=0x7f08a70fb710) at /team/mpirvu/Build/FullJava21/openj9-openjdk-jdk21/omr/compiler/compile/OMRCompilation.cpp:1286
#18 0x00007f09105eb58d in TR_InlinerBase::inlineCallTarget (this=this@entry=0x7f09050c43e0, callStack=callStack@entry=0x7f09050c2e50, calltarget=calltarget@entry=0x7f08a70c9030,
    inlinefromgraph=inlinefromgraph@entry=true, argInfo=0x7f08a70fb710, argInfo@entry=0x0, cursorTreeTop=cursorTreeTop@entry=0x7f09050c2e38)
    at /team/mpirvu/Build/FullJava21/openj9-openjdk-jdk21/openj9/runtime/compiler/optimizer/J9Inliner.cpp:470
#19 0x00007f09108d1215 in TR_InlinerBase::inlineFromGraph (this=0x7f09050c43e0, prevCallStack=<optimized out>, calltarget=0x7f08a70a3490, innerPrexInfo=<optimized out>)
    at /team/mpirvu/Build/FullJava21/openj9-openjdk-jdk21/omr/compiler/optimizer/Inliner.cpp:4726
#20 0x00007f09108d4371 in TR_InlinerBase::inlineCallTarget2 (this=this@entry=0x7f09050c43e0, callStack=callStack@entry=0x7f09050c3270, calltarget=calltarget@entry=0x7f08a70a3490,
    cursorTreeTop=cursorTreeTop@entry=0x7f09050c3248, inlinefromgraph=inlinefromgraph@entry=true)
    at /team/mpirvu/Build/FullJava21/openj9-openjdk-jdk21/omr/compiler/optimizer/Inliner.cpp:4989
#21 0x00007f09105eb5d7 in TR_InlinerBase::inlineCallTarget (this=this@entry=0x7f09050c43e0, callStack=callStack@entry=0x7f09050c3270, calltarget=calltarget@entry=0x7f08a70a3490,
    inlinefromgraph=inlinefromgraph@entry=true, argInfo=<optimized out>, argInfo@entry=0x0, cursorTreeTop=cursorTreeTop@entry=0x7f09050c3248)
    at /team/mpirvu/Build/FullJava21/openj9-openjdk-jdk21/openj9/runtime/compiler/optimizer/J9Inliner.cpp:501
#22 0x00007f09105cc43f in TR_MultipleCallTargetInliner::inlineCallTargets (this=0x7f09050c43e0, callerSymbol=<optimized out>, prevCallStack=<optimized out>,
    innerPrexInfo=<optimized out>) at /team/mpirvu/Build/FullJava21/openj9-openjdk-jdk21/openj9/runtime/compiler/optimizer/InlinerTempForJ9.cpp:4099
#23 0x00007f09108d493b in TR_InlinerBase::performInlining (this=this@entry=0x7f09050c43e0, callerSymbol=callerSymbol@entry=0x7f08a70057d0)
    at /team/mpirvu/Build/FullJava21/openj9-openjdk-jdk21/omr/compiler/optimizer/Inliner.cpp:457
#24 0x00007f09105c2e6a in TR_Inliner::perform (this=0x7f08a7010160) at /team/mpirvu/Build/FullJava21/openj9-openjdk-jdk21/openj9/runtime/compiler/optimizer/InlinerTempForJ9.cpp:3373

This is the relevant code:

TR_ExternalRelocationTargetKind
J9::Compilation::getReloTypeForMethodToBeInlined(TR_VirtualGuardSelection *guard, TR::Node *callNode, TR_OpaqueClassBlock *receiverClass)
   {
   TR_ExternalRelocationTargetKind reloKind = OMR::Compilation::getReloTypeForMethodToBeInlined(guard, callNode, receiverClass);

   if (callNode && self()->compileRelocatableCode())
      {
      if (guard && guard->_kind == TR_ProfiledGuard)
         {
         if (guard->_type == TR_MethodTest)
            reloKind = TR_ProfiledMethodGuardRelocation;
         else if (guard->_type == TR_VftTest)
            reloKind = TR_ProfiledClassGuardRelocation;
         }
      else
         {
         TR::MethodSymbol *methodSymbol = callNode->getSymbolReference()->getSymbol()->castToMethodSymbol();

         if (methodSymbol->isSpecial())
            {
            reloKind = TR_InlinedSpecialMethod;
            }
         else if (methodSymbol->isStatic())
            {
            reloKind = TR_InlinedStaticMethod;
            }
         else if (receiverClass
                  && TR::Compiler->cls.isAbstractClass(self(), receiverClass)
                  && methodSymbol->getResolvedMethodSymbol()->getResolvedMethod()->isAbstract())
            {
            reloKind = TR_InlinedAbstractMethod;
            }
         else if (methodSymbol->isVirtual())
            {
            reloKind = TR_InlinedVirtualMethod;
            }
         else if (methodSymbol->isInterface())
            {
            reloKind = TR_InlinedInterfaceMethod;
            }
         }
......

The crash happens under the following conditions:

  • JITServer performs an AOT compilation
  • There is no profiled guard for the callee
  • The receiverClass is abstract
  • The symbolKind for the callee is IsMethod, but not IsResolvedMethod

methodSymbol->getResolvedMethodSymbol() will return NULL because the method is not resolved and the program crashes when dereferencing methodSymbol->getResolvedMethodSymbol()->getResolvedMethod()

mpirvu avatar Jun 12 '25 14:06 mpirvu

I added some code to avoid the crash and be able to produce a compilation log with traceInlining. The method being compiled, org/jboss/weld/injection/InjectionContextImpl.run()V, inlines com/ibm/ws/cdi/impl/weld/injection/WebSphereInjectionServicesImpl.aroundInject(Lorg/jboss/weld/injection/spi/InjectionContext;)V for which the IL is:

...
n112n     BBStart <block_4>                                                                   [0x7f15940812c0] bci=[0,15,-] rc=0 vc=0 vn=- li=- udi=- nc=0
n124n     treetop                                                                             [0x7f1594081680] bci=[0,18,-] rc=0 vc=0 vn=- li=- udi=- nc=1
n123n       new  jitNewObject[#92  helper Method] [flags 0x400 0x0 ]                          [0x7f1594081630] bci=[0,18,-] rc=3 vc=0 vn=- li=- udi=- nc=1
n122n         loadaddr  java/lang/StringBuilder[#425  Static] [flags 0x18307 0x0 ]            [0x7f15940815e0] bci=[0,18,-] rc=1 vc=0 vn=- li=- udi=- nc=0
n127n     treetop                                                                             [0x7f1594081770] bci=[0,22,-] rc=0 vc=0 vn=- li=- udi=- nc=1
n121n       aload  com/ibm/ws/cdi/impl/weld/injection/WebSphereInjectionServicesImpl.tc Lcom/ibm/websphere/ras/TraceComponent;[#422  final Static] [flags 0x20307 0x0 ]  [0x7f1594081590] bci=[0,15,-] rc=2 vc=0 vn=- li=- udi=- nc=0
n126n     NULLCHK on n123n [#32]                                                              [0x7f1594081720] bci=[0,22,-] rc=0 vc=0 vn=- li=- udi=- nc=1
n125n       call  java/lang/StringBuilder.<init>()V[#427  final special Method] [flags 0x20500 0x0 ]  [0x7f15940816d0] bci=[0,22,-] rc=1 vc=0 vn=- li=- udi=- nc=1
n123n         ==>new
n129n     ResolveCHK [#347]                                                                   [0x7f1594081810] bci=[0,25,-] rc=0 vc=0 vn=- li=- udi=- nc=1
n128n       aload  <string>[#428  unresolved Static +1610637392] [flags 0x80000307 0x0 ]      [0x7f15940817c0] bci=[0,25,-] rc=2 vc=0 vn=- li=- udi=- nc=0
n131n     NULLCHK on n123n [#32]                                                              [0x7f15940818b0] bci=[0,28,-] rc=0 vc=0 vn=- li=- udi=- nc=1
n130n       acall  java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;[#431  final virtual Method -608] [flags 0x20500 0x0 ]  [0x7f1594081860] bci=[0,28,-] rc=2 vc=0 vn=- li=- udi=- nc=2
n123n         ==>new
n128n         ==>aload
n135n     ResolveAndNULLCHK on n132n [#32]                                                    [0x7f15940819f0] bci=[0,34,-] rc=0 vc=0 vn=- li=- udi=- nc=1
n134n       acalli  org/jboss/weld/injection/spi/InjectionContext.getAnnotatedType()Ljavax/enterprise/inject/spi/AnnotatedType;[#432  unresolved interface Method] (Interface class) [flags 0x400 0x0 ]  [0x7f15940819a0] bci=[0,34,-] rc=2 vc=0 vn=- li=- udi=- nc=2
n133n         aloadi  <vft-symbol>[#342  Shadow] [flags 0x18607 0x0 ]                         [0x7f1594081950] bci=[0,34,-] rc=1 vc=0 vn=- li=- udi=- nc=1
n179n           aload  injectionContext<parm 1 Lorg/jboss/weld/injection/spi/InjectionContext;>[#420  Parm] [flags 0x40000107 0x0 ]  [0x7f15940827b0] bci=[0,31,-] rc=1 vc=0 vn=- li=- udi=- nc=0
n132n         aload  injectionContext<parm 1 Lorg/jboss/weld/injection/spi/InjectionContext;>[#420  Parm] [flags 0x40000107 0x0 ]  [0x7f1594081900] bci=[0,31,-] rc=1 vc=0 vn=- li=- udi=- nc=0
n137n     NULLCHK on n130n [#32]                                                              [0x7f1594081a90] bci=[0,37,-] rc=0 vc=0 vn=- li=- udi=- nc=1
...

The problematic callee is org/jboss/weld/injection/spi/InjectionContext.getAnnotatedType()Ljavax/enterprise/inject/spi/AnnotatedType; at node n134n. This is unresolved interface method. The Inliner finds a single implementer:

  34: JBinvokeinterface

numberOfExplicitParameters = 0  _pca.getNumPrevConstArgs = 0

Inliner: Compute prex info for call site 0x7f15940d1410

        _CallerResolvedMethod = 00007F15940A83F8        _callNodeTreeTop = 0000000000000000     _parent = 0000000000000000      _callNode = 0000000000000000
        _interfaceMethod = 00007F15940D1320     _receiverClass = 0000000000000000       _vftSlot = -1   _cpIndex = 66
        _initialCalleeMethod = 0000000000000000 _initialCalleeSymbol = 0000000000000000 _bcInfo = 00007F15940D1478      _byteCodeIndex = 34     _isIndirectCall = 1
        _isInterface = 1        numtargets() = 0         failureReason = 0 InlineableTarget
         CALLER signature from method = com/ibm/ws/cdi/impl/weld/injection/WebSphereInjectionServicesImpl.aroundInject(Lorg/jboss/weld/injection/spi/InjectionContext;)V

PREX.inl: Populating prex argInfo for [00007F15940819A0] acalli org/jboss/weld/injection/spi/InjectionContext.getAnnotatedType()Ljavax/enterprise/inject/spi/AnnotatedType;
PREX.inl:    Child 1 [00007F1594081900] n132n aload injectionContext<parm 1 Lorg/jboss/weld/injection/spi/InjectionContext;>
PREX.inl: Done populating prex argInfo for acalli 00007F15940819A0
PREX.inl: Propagating prex argInfo from caller for [00007F15940819A0] acalli org/jboss/weld/injection/spi/InjectionContext.getAnnotatedType()Ljavax/enterprise/inject/spi/AnnotatedType;
Arg 0 is from caller
PREX.inl:    argInfo for callsite 00007F15940D1410 after propagating argInfo from caller
<argInfo address = 00007F15940D1660 numArgs = 1>
<Argument no=0 address=00007F15940A8FE0 classIsFixed=0 classIsPreexistent=1 argIsKnownObject=0 koi=-1 class=0000000000EA4500 className= Lorg/jboss/weld/injection/InjectionContextImpl;/>
</argInfo>
Depth 0: Created Call Site 0x7f15940d1410 for call found at bc index 34. Signature org/jboss/weld/injection/spi/InjectionContext.getAnnotatedType()Ljavax/enterprise/inject/spi/AnnotatedType;  Looking for call targets.
refining _receiverClass 0xea4300 to 0xea4500
MethodFromSingleImplementer
        _method=0x0000000000EA4838
        _thisClass=0x0000000000EA4500
        className=org/jboss/weld/injection/InjectionContextImpl
        _cpIndexOrVftSlot=66
        _callerMethod=0x0000000000C8C348
        _useGetResolvedInterfaceMethod=1
        kind=94
        id=53

Found a Single Interface Implementer with Resolved Method 0x7f15940d1a48 for callsite 0x7f15940d1410
MethodFromClassRecord
        _method=0x0000000000EA4838
        _beholder=0x0000000000EA4500
        className=org/jboss/weld/injection/InjectionContextImpl
        _index=3
        kind=84
        id=53

Creating a call target 0x7f15940d1c70 for callsite 0x7f15940d1410 using a InterfaceGuard and MethodTest .  Signature org/jboss/weld/injection/InjectionContextImpl.getAnnotatedType()Ljavax/enterprise/inject/spi/AnnotatedType;
Call is an Interface with a Single Implementer guard 0x7f15940d1c30

However, later on, when we try to inline, we get into:

TR_ExternalRelocationTargetKind
J9::Compilation::getReloTypeForMethodToBeInlined(TR_VirtualGuardSelection *guard, TR::Node *callNode, TR_OpaqueClassBlock *receiverClass)
   {
   TR_ExternalRelocationTargetKind reloKind = OMR::Compilation::getReloTypeForMethodToBeInlined(guard, callNode, receiverClass);

   if (callNode && self()->compileRelocatableCode())
      {
      if (guard && guard->_kind == TR_ProfiledGuard)
         {
         if (guard->_type == TR_MethodTest)
            reloKind = TR_ProfiledMethodGuardRelocation;
         else if (guard->_type == TR_VftTest)
            reloKind = TR_ProfiledClassGuardRelocation;
         }
      else
         {
         TR::MethodSymbol *methodSymbol = callNode->getSymbolReference()->getSymbol()->castToMethodSymbol();

         if (methodSymbol->isSpecial())
            {
            reloKind = TR_InlinedSpecialMethod;
            }
         else if (methodSymbol->isStatic())
            {
            reloKind = TR_InlinedStaticMethod;
            }
         else if (receiverClass
                  && TR::Compiler->cls.isAbstractClass(self(), receiverClass)
                  && methodSymbol->getResolvedMethodSymbol()->getResolvedMethod()->isAbstract())
            {
            reloKind = TR_InlinedAbstractMethod;
            }
         else if (methodSymbol->isVirtual())
            {
            reloKind = TR_InlinedVirtualMethod;
            }
         else if (methodSymbol->isInterface())
            {
            reloKind = TR_InlinedInterfaceMethod;
            }
         }
......

and execute the check

         else if (receiverClass
                  && TR::Compiler->cls.isAbstractClass(self(), receiverClass)
                  && methodSymbol->getResolvedMethodSymbol()->getResolvedMethod()->isAbstract())

which crashes because methodSymbol->getResolvedMethodSymbol() returns NULL.

mpirvu avatar Jun 13 '25 17:06 mpirvu

@dsouzai Could you please comment on the message above. J9::Compilation::getReloTypeForMethodToBeInlined() is a method that you wrote 5 years back here: https://github.com/eclipse-openj9/openj9/pull/11552. The method accounts for the existence of profiled guards, but in here we have a "single implementer" guard.

mpirvu avatar Jun 13 '25 17:06 mpirvu

Hm, maybe we need to move

         else if (receiverClass
                  && TR::Compiler->cls.isAbstractClass(self(), receiverClass)
                  && methodSymbol->getResolvedMethodSymbol()->getResolvedMethod()->isAbstract())
            {
            reloKind = TR_InlinedAbstractMethod;
            }

as the last condition? The log says that it should be an interface method, but I guess TR::Compiler->cls.isAbstractClass(self(), receiverClass) returns true. I guess it's possible for the receiver to be abstract but we're inlining an interface method. I guess if an abstract class is declared to implement some interface class?

It does seem weird though that methodSymbol->getResolvedMethodSymbol() should be NULL if we've decided that we're going to inline it; that query's definition is

TR::ResolvedMethodSymbol *
OMR::Symbol::getResolvedMethodSymbol()
   {
   return self()->isResolvedMethod() ? (TR::ResolvedMethodSymbol *)this : 0;
   }

so it returns NULL if the methodSymbol is not a resolved method, but intuitively I would've exepcted isResolvedMethod() to be true in this case (because we're inlining it).

@jdmpapin does it make sense that isResolvedMethod() is NULL in this case?

dsouzai avatar Jun 13 '25 17:06 dsouzai

Hm, maybe we need to move ... as the last condition?

Thinking on it, the placement may have been intentional; I think inlined methods of abstract classes can have methodSymbol->isVirtual() be true, so we'd result with the wrong reloKind. So, we may not be able to move it down.

dsouzai avatar Jun 13 '25 17:06 dsouzai

We are asking about the symbol from the callNode: TR::MethodSymbol *methodSymbol = callNode->getSymbolReference()->getSymbol()->castToMethodSymbol(); which I am guessing is the unresolved interface org/jboss/weld/injection/spi/InjectionContext.getAnnotatedType()Ljavax/enterprise/inject/spi/AnnotatedType; rather than the implementer we want to inline org/jboss/weld/injection/InjectionContextImpl.getAnnotatedType()Ljavax/enterprise/inject/spi/AnnotatedType;

mpirvu avatar Jun 13 '25 18:06 mpirvu

The easy solution is to just add && methodSymbol->getResolvedMethodSymbol() to the list of conditions. However, I don't know if that will mask some deeper problem.

If it is valid for the callNode's method symbol to not be resolved, then maybe we need to update getReloTypeForMethodToBeInlined to take in the resolved method of the target which is seen in OMR::Compilation::incInlineDepth:

aotMethodInfo->resolvedMethod = method->getResolvedMethod();

but that will require a coordinated merge :)

dsouzai avatar Jun 13 '25 18:06 dsouzai

Yes, for an interface method, we won't have a "resolved method." Hopefully one day we can stop pretending that interface calls are unresolved... 😞

What's the receiverClass that's been passed in? I guess for this to happen, it would need to be the defining class of the implementing method, so maybe that's what we have when inlining an interface call? But it looks like we're trying to use receiverClass here to detect cases where we're inlining the single implementer of an abstract method, so in that case it would need to be the class named at the call site, or we'd never realize that abstract inlining is happening. So maybe the meaning of receiverClass is not necessarily consistent. Could we detect abstract inlining based on something else instead, e.g. the guard kind or the call node symref?

By the way, what's the relevance of JITServer here? I understand that the bug was observed when running with JITServer, but it doesn't look JITServer-specific at all

jdmpapin avatar Jun 13 '25 18:06 jdmpapin

There is no direct relevance to JITServer, it's just that I could not reproduce it without JITServer.

mpirvu avatar Jun 13 '25 18:06 mpirvu

What's the receiverClass that's been passed in? I guess for this to happen, it would need to be the defining class of the implementing method, so maybe that's what we have when inlining an interface call?

Yes, the receiverClass is the class of the concrete implementer we want to inline: receiverClass=org/jboss/weld/injection/InjectionContextImpl

mpirvu avatar Jun 16 '25 14:06 mpirvu