openj9
openj9 copied to clipboard
JITServer crash in Compilation::getReloTypeForMethodToBeInlined
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
receiverClassis abstract - The symbolKind for the callee is
IsMethod, but notIsResolvedMethod
methodSymbol->getResolvedMethodSymbol() will return NULL because the method is not resolved and the program crashes when dereferencing methodSymbol->getResolvedMethodSymbol()->getResolvedMethod()
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.
@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.
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?
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.
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;
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 :)
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
There is no direct relevance to JITServer, it's just that I could not reproduce it without JITServer.
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