Skip to content

Commit c55c2e5

Browse files
authored
Merge pull request #21742 from jdmpapin/linkToVirtual-invokeBasic-0.51
(0.51) Add table of invokeBasic() call sites to J9JITExceptionTable
2 parents b0ba118 + 318b66f commit c55c2e5

File tree

16 files changed

+399
-19
lines changed

16 files changed

+399
-19
lines changed

doc/compiler/runtime/J9JITExceptionTable.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ The following diagram provides the high level overview. The
5656
(7) | GPU Info | // If GPU
5757
|-----------------------------------------------------|
5858
(8) | Bytecode PC to Instruction Address Map | // If Hardware Profiling
59+
|-----------------------------------------------------|
60+
(9) | invokeBasic() Call Site Info | // If Applicable Call Sites Exist
5961
+-----------------------------------------------------+
6062
```
6163

@@ -434,4 +436,11 @@ set of data for each mapping
434436
via the `bodyInfo` field of the `J9JITExceptionTable` struct. This
435437
structure contains data such as flags used to describe the body,
436438
as well as data used to implement features such as
437-
Guarded Counting Recompilation (GCR).
439+
Guarded Counting Recompilation (GCR).
440+
441+
## (9) `invokeBasic()` Call Info
442+
The `invokeBasic()` Call Info section can be accessed via the
443+
`invokeBasicCallInfo` field of the `J9JITExceptionTable` struct. This section
444+
consists of a single instance of the variable-size `J9JITInvokeBasicCallInfo`
445+
struct, which contains a table of call sites that are capable of calling the
446+
interpreter's implementation of `MethodHandle.invokeBasic()`.

runtime/codert_vm/cnathelp.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3383,12 +3383,26 @@ old_slow_icallVMprJavaSendPatchupVirtual(J9VMThread *currentThread)
33833383
J9Class *clazz = J9OBJECT_CLAZZ(currentThread, receiver);
33843384
UDATA interpVTableOffset = sizeof(J9Class) - jitVTableOffset;
33853385
J9Method *method = *(J9Method**)((UDATA)clazz + interpVTableOffset);
3386-
J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method);
3387-
J9ROMNameAndSignature *nas = &romMethod->nameAndSignature;
3388-
UDATA const thunk = (UDATA)jitConfig->thunkLookUpNameAndSig(jitConfig, nas);
3389-
UDATA const patchup = (UDATA)jitConfig->patchupVirtual;
3390-
UDATA *jitVTableSlot = (UDATA*)((UDATA)clazz + jitVTableOffset);
3391-
VM_AtomicSupport::lockCompareExchange(jitVTableSlot, patchup, thunk);
3386+
UDATA thunk = 0;
3387+
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
3388+
if (J9_BCLOOP_SEND_TARGET_METHODHANDLE_INVOKEBASIC == J9_BCLOOP_DECODE_SEND_TARGET(method->methodRunAddress)) {
3389+
/* Because invokeBasic() is signature-polymorphic, the signature from the ROM method is irrelevant.
3390+
* We need the J2I thunk that corresponds to the signature that the JIT was using at the call site.
3391+
* Since this varies depending on the call site, we can't replace the VFT entry with the J2I thunk.
3392+
*/
3393+
J9JITInvokeBasicCallSite *site = jitGetInvokeBasicCallSiteFromPC(currentThread, (UDATA)jitReturnAddress);
3394+
thunk = (UDATA)site->j2iThunk;
3395+
}
3396+
else
3397+
#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */
3398+
{
3399+
J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method);
3400+
J9ROMNameAndSignature *nas = &romMethod->nameAndSignature;
3401+
thunk = (UDATA)jitConfig->thunkLookUpNameAndSig(jitConfig, nas);
3402+
UDATA const patchup = (UDATA)jitConfig->patchupVirtual;
3403+
UDATA *jitVTableSlot = (UDATA*)((UDATA)clazz + jitVTableOffset);
3404+
VM_AtomicSupport::lockCompareExchange(jitVTableSlot, patchup, thunk);
3405+
}
33923406
currentThread->tempSlot = thunk;
33933407
}
33943408

runtime/compiler/aarch64/codegen/ARM64PrivateLinkage.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,14 @@ void J9::ARM64::PrivateLinkage::buildDirectCall(TR::Node *callNode,
14301430
new (trHeapMemory()) TR::SymbolReference(comp()->getSymRefTab(), label),
14311431
snippet);
14321432

1433+
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
1434+
auto rm = callSymbol->getMandatoryRecognizedMethod();
1435+
if (rm == TR::java_lang_invoke_MethodHandle_invokeBasic)
1436+
{
1437+
cg()->addInvokeBasicCallSite(callNode, gcPoint);
1438+
}
1439+
#endif
1440+
14331441
// Nop is necessary due to confusion when resolving shared slots at a transition
14341442
if (callSymRef->isOSRInductionHelper())
14351443
cg()->generateNop(callNode);
@@ -2090,6 +2098,15 @@ void J9::ARM64::PrivateLinkage::buildVirtualDispatch(TR::Node *callNode,
20902098
TR::Instruction *gcPoint = generateRegBranchInstruction(cg(), TR::InstOpCode::blr, callNode, vftReg, dependencies);
20912099
gcPoint->ARM64NeedsGCMap(cg(), regMapForGC);
20922100

2101+
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
2102+
// JITHelpers.dispatchVirtual() can target MethodHandle.invokeBasic().
2103+
auto rm = methodSymbol->getMandatoryRecognizedMethod();
2104+
if (rm == TR::com_ibm_jit_JITHelpers_dispatchVirtual)
2105+
{
2106+
cg()->addInvokeBasicCallSite(callNode, gcPoint);
2107+
}
2108+
#endif
2109+
20932110
return;
20942111
}
20952112

runtime/compiler/codegen/J9CodeGenerator.cpp

Lines changed: 117 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ J9::CodeGenerator::CodeGenerator(TR::Compilation *comp) :
8181
_jniCallSites(getTypedAllocator<TR_Pair<TR_ResolvedMethod,TR::Instruction> *>(comp->allocator())),
8282
_monitorMapping(std::less<ncount_t>(), MonitorMapAllocator(comp->trMemory()->heapMemoryRegion())),
8383
_dummyTempStorageRefNode(NULL)
84+
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
85+
, _invokeBasicCallSites(comp->region())
86+
#endif
8487
{
8588
/**
8689
* Do not add CodeGenerator initialization logic here.
@@ -797,15 +800,16 @@ J9::CodeGenerator::lowerTreeIfNeeded(
797800
{
798801
TR::RecognizedMethod rm = node->getSymbol()->castToMethodSymbol()->getMandatoryRecognizedMethod();
799802

800-
if(rm == TR::java_lang_invoke_MethodHandle_invokeBasic ||
801-
rm == TR::java_lang_invoke_MethodHandle_linkToStatic ||
803+
// There's no need to set tempSlot for invokeBasic(). The number of stack
804+
// slots for the arguments will be available from the JIT body metadata.
805+
806+
if(rm == TR::java_lang_invoke_MethodHandle_linkToStatic ||
802807
rm == TR::java_lang_invoke_MethodHandle_linkToSpecial ||
803808
rm == TR::java_lang_invoke_MethodHandle_linkToVirtual ||
804809
rm == TR::java_lang_invoke_MethodHandle_linkToInterface)
805810
{
806-
// invokeBasic and linkTo* are signature-polymorphic, so the VM needs to know the number of argument slots
807-
// for the INL call in order to locate the start of the arguments on the stack. The arg slot count is stored
808-
// in vmThread.tempSlot.
811+
// linkTo* is signature-polymorphic, so the VM needs to know the number of argument slots for the INL call in order to
812+
// locate the start of the arguments on the stack. The arg slot count is stored in vmThread.tempSlot.
809813
//
810814
// Furthermore, for unresolved invokedynamic and invokehandle bytecodes, we create a dummy TR_ResolvedMethod call to
811815
// linkToStatic. The appendix object in the invoke cache array entry could be NULL, which we cannot determine at compile
@@ -5289,3 +5293,111 @@ J9::CodeGenerator::stressJitDispatchJ9MethodJ2I()
52895293
static const bool stress = feGetEnv("TR_stressJitDispatchJ9MethodJ2I") != NULL;
52905294
return stress;
52915295
}
5296+
5297+
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
5298+
void
5299+
J9::CodeGenerator::addInvokeBasicCallSiteImpl(
5300+
TR::Node *callNode, TR::Instruction *instr, uint8_t *retAddr)
5301+
{
5302+
TR_ASSERT_FATAL_WITH_NODE(
5303+
callNode,
5304+
(instr != NULL) != (retAddr != NULL),
5305+
"expected exactly one of TR::Instruction or return address");
5306+
5307+
if (comp()->getOption(TR_TraceCG))
5308+
{
5309+
traceMsg(comp(), "Call instruction ");
5310+
if (instr != NULL)
5311+
{
5312+
traceMsg(comp(), "%p", instr);
5313+
}
5314+
else
5315+
{
5316+
traceMsg(comp(), "with return address %p", retAddr);
5317+
}
5318+
5319+
traceMsg(
5320+
comp(),
5321+
" for n%un [%p] may target VM MethodHandle.invokeBasic\n",
5322+
callNode->getGlobalIndex(),
5323+
callNode);
5324+
}
5325+
5326+
TR_J9VMBase *fej9 = comp()->fej9();
5327+
TR::Node *j2iCallNode = callNode;
5328+
TR::MethodSymbol *methodSymbol = callNode->getSymbol()->castToMethodSymbol();
5329+
TR::RecognizedMethod rm = methodSymbol->getMandatoryRecognizedMethod();
5330+
switch (rm)
5331+
{
5332+
case TR::java_lang_invoke_MethodHandle_invokeBasic:
5333+
break; // ok
5334+
5335+
case TR::com_ibm_jit_JITHelpers_dispatchVirtual:
5336+
j2iCallNode =
5337+
fej9->getEquivalentVirtualCallNodeForDispatchVirtual(callNode, comp());
5338+
break;
5339+
5340+
default:
5341+
TR_ASSERT_FATAL_WITH_NODE(
5342+
callNode,
5343+
false,
5344+
"expected MethodHandle.invokeBasic or JITHelpers.dispatchVirtual");
5345+
break;
5346+
}
5347+
5348+
int32_t numChildren = j2iCallNode->getNumChildren();
5349+
int32_t firstArgIndex = j2iCallNode->getFirstArgumentIndex();
5350+
uint32_t numArgSlots32 = 0;
5351+
for (int32_t i = firstArgIndex; i < numChildren; i++)
5352+
{
5353+
TR::Node *child = j2iCallNode->getChild(i);
5354+
5355+
// PassThrough nodes will appear to have type TR::NoType. The actual type
5356+
// of the resulting value is the same as the type of the child.
5357+
while (child->getOpCodeValue() == TR::PassThrough)
5358+
{
5359+
child = child->getChild(0);
5360+
}
5361+
5362+
TR::DataTypes dt = child->getDataType().getDataType();
5363+
numArgSlots32 += 1 + (uint32_t)(dt == TR::Int64 || dt == TR::Double);
5364+
}
5365+
5366+
if (comp()->getOption(TR_TraceCG))
5367+
{
5368+
traceMsg(comp(), " arg slots: %u\n", numArgSlots32);
5369+
}
5370+
5371+
TR_ASSERT_FATAL_WITH_NODE(
5372+
callNode,
5373+
numArgSlots32 <= UINT8_MAX,
5374+
"too many argument slots (%u)",
5375+
numArgSlots32);
5376+
5377+
uint8_t numArgSlots = (uint8_t)numArgSlots32;
5378+
5379+
void *j2iThunk = NULL;
5380+
if (rm == TR::com_ibm_jit_JITHelpers_dispatchVirtual)
5381+
{
5382+
TR::Method *m = methodSymbol->getMethod();
5383+
char *sig = fej9->getJ2IThunkSignatureForDispatchVirtual(
5384+
m->signatureChars(), m->signatureLength(), comp());
5385+
5386+
int32_t sigLen = strlen(sig);
5387+
j2iThunk = fej9->getJ2IThunk(sig, sigLen, comp());
5388+
TR_ASSERT_FATAL_WITH_NODE(callNode, j2iThunk != NULL, "missing J2I thunk");
5389+
5390+
if (comp()->getOption(TR_TraceCG))
5391+
{
5392+
traceMsg(comp(), " J2I thunk: %p\n", j2iThunk);
5393+
}
5394+
}
5395+
5396+
InvokeBasicCallSite site = {};
5397+
site._instr = instr;
5398+
site._retAddr = retAddr;
5399+
site._numArgSlots = numArgSlots;
5400+
site._j2iThunk = j2iThunk;
5401+
_invokeBasicCallSites.push_back(site);
5402+
}
5403+
#endif // defined(J9VM_OPT_OPENJDK_METHODHANDLE)

runtime/compiler/codegen/J9CodeGenerator.hpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ namespace J9 { typedef J9::CodeGenerator CodeGeneratorConnector; }
4040
#include "env/jittypes.h"
4141
#include "infra/List.hpp"
4242
#include "infra/HashTab.hpp"
43+
#include "infra/TRlist.hpp"
4344
#include "codegen/RecognizedMethods.hpp"
4445
#if defined(J9VM_OPT_JITSERVER)
4546
#include "control/CompilationRuntime.hpp"
@@ -708,8 +709,40 @@ void addMonClass(TR::Node* monNode, TR_OpaqueClassBlock* clazz);
708709
/// Determine whether to stress the J2I path for \c jitDispatchJ9Method.
709710
bool stressJitDispatchJ9MethodJ2I();
710711

712+
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
713+
void addInvokeBasicCallSite(TR::Node *callNode, TR::Instruction *instr)
714+
{
715+
addInvokeBasicCallSiteImpl(callNode, instr, NULL);
716+
}
717+
718+
void addInvokeBasicCallSite(TR::Node *callNode, uint8_t *retAddr)
719+
{
720+
addInvokeBasicCallSiteImpl(callNode, NULL, retAddr);
721+
}
722+
723+
struct InvokeBasicCallSite
724+
{
725+
TR::Instruction *_instr; // for call instruction from an evaluator
726+
void *_retAddr; // for call instruction from a snippet
727+
uint8_t _numArgSlots;
728+
void *_j2iThunk; // for JITHelpers.dispatchVirtual()
729+
};
730+
731+
typedef TR::list<InvokeBasicCallSite, TR::Region&> InvokeBasicCallSiteList;
732+
733+
const InvokeBasicCallSiteList &invokeBasicCallSites()
734+
{
735+
return _invokeBasicCallSites;
736+
}
737+
#endif
738+
711739
private:
712740

741+
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
742+
void addInvokeBasicCallSiteImpl(
743+
TR::Node *callNode, TR::Instruction *instr, uint8_t *retAddr);
744+
#endif
745+
713746
enum // Flags
714747
{
715748
HasFixedFrameC_CallingConvention = 0x00000001,
@@ -734,6 +767,10 @@ void addMonClass(TR::Node* monNode, TR_OpaqueClassBlock* clazz);
734767
};
735768

736769
flags32_t _j9Flags;
770+
771+
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
772+
InvokeBasicCallSiteList _invokeBasicCallSites;
773+
#endif
737774
};
738775
}
739776

runtime/compiler/p/codegen/CallSnippet.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,14 @@ uint8_t *TR::PPCCallSnippet::emitSnippetBody()
373373

374374
cursor += PPC_INSTRUCTION_LENGTH;
375375

376+
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
377+
auto rm = methodSymbol->getMandatoryRecognizedMethod();
378+
if (rm == TR::java_lang_invoke_MethodHandle_invokeBasic)
379+
{
380+
cg()->addInvokeBasicCallSite(callNode, cursor);
381+
}
382+
#endif
383+
376384
if (isNativeStatic)
377385
{
378386
// Rather than placing the return address as data after the 'bl', place a 'b' back to main line code

runtime/compiler/p/codegen/PPCPrivateLinkage.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2320,6 +2320,15 @@ void J9::Power::PrivateLinkage::buildVirtualDispatch(TR::Node
23202320
TR::Instruction *gcPoint = generateInstruction(cg(), TR::InstOpCode::bctrl, callNode);
23212321
generateDepLabelInstruction(cg(), TR::InstOpCode::label, callNode, doneLabel, dependencies);
23222322

2323+
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
2324+
// JITHelpers.dispatchVirtual() can target MethodHandle.invokeBasic().
2325+
auto rm = methodSymbol->getMandatoryRecognizedMethod();
2326+
if (rm == TR::com_ibm_jit_JITHelpers_dispatchVirtual)
2327+
{
2328+
cg()->addInvokeBasicCallSite(callNode, gcPoint);
2329+
}
2330+
#endif
2331+
23232332
gcPoint->PPCNeedsGCMap(regMapForGC);
23242333
return;
23252334
}

0 commit comments

Comments
 (0)