Skip to content

Commit

Permalink
Merge pull request #20373 from dsouzai/aotmhResolved
Browse files Browse the repository at this point in the history
Add SVM AOT support for resolved invokeHandle/invokeDynamic dispatch
  • Loading branch information
jdmpapin authored Nov 12, 2024
2 parents d7f653d + 9a7438f commit 3638df4
Show file tree
Hide file tree
Showing 26 changed files with 1,103 additions and 64 deletions.
4 changes: 4 additions & 0 deletions doc/compiler/aot/RelocationRecords.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,7 @@ exact type of the API class for each relocation kind can be found in
|`TR_CatchBlockCounter`|Relocates the address of the catch block counter in the `TR_PersistentMethodInfo` of the method being compiled.|
|`TR_StartPC`|Relocates the startPC of the method being compiled. Only implemented and used on Power.|
|`TR_MethodEnterExitHookAddress`|Relocates the address of the method enter or exit hook.|
|`TR_ValidateDynamicMethodFromCallsiteIndex`|Validates the target method of an `invokeDynamic` invocation.|
|`TR_ValidateHandleMethodFromCPIndex`|Validates the target method of an `invokeHandle` invocation.|
|`TR_CallsiteTableEntryAddress`|Relocates the callsite table entry address.|
|`TR_MethodTypeTableEntryAddress`|Relocates the method type table entry address.|
8 changes: 7 additions & 1 deletion runtime/bcutil/ROMClassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,14 @@ ROMClassBuilder::handleAnonClassName(J9CfrClassFile *classfile, ROMClassCreation
* Performance can be much worse (compared to shared cache turned off).
*/
if (isLambdaFormClassName(originalStringBytes, originalStringLength, NULL/*deterministicPrefixLength*/)) {
context->addFindClassFlags(J9_FINDCLASS_FLAG_DO_NOT_SHARE);
context->addFindClassFlags(J9_FINDCLASS_FLAG_LAMBDAFORM);
#if defined(J9VM_OPT_SHARED_CLASSES)
if ((NULL != _javaVM) && (NULL != _javaVM->sharedClassConfig)) {
if (J9_ARE_NO_BITS_SET(_javaVM->sharedClassConfig->runtimeFlags2, J9SHR_RUNTIMEFLAG2_SHARE_LAMBDAFORM)) {
context->addFindClassFlags(J9_FINDCLASS_FLAG_DO_NOT_SHARE);
}
}
#endif /* defined(J9VM_OPT_SHARED_CLASSES) */
}

#if JAVA_SPEC_VERSION >= 15
Expand Down
133 changes: 133 additions & 0 deletions runtime/compiler/codegen/J9AheadOfTimeCompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,67 @@ J9::AheadOfTimeCompile::initializeCommonAOTRelocationHeader(TR::IteratedExternal
}
break;

case TR_ValidateDynamicMethodFromCallsiteIndex:
{
auto *dmciRecord = reinterpret_cast<TR_RelocationRecordValidateDynamicMethodFromCallsiteIndex *>(reloRecord);

TR::DynamicMethodFromCallsiteIndexRecord *svmRecord = reinterpret_cast<TR::DynamicMethodFromCallsiteIndexRecord *>(relocation->getTargetAddress());

dmciRecord->setMethodID(reloTarget, symValManager->getSymbolIDFromValue(svmRecord->_method));
dmciRecord->setCallerID(reloTarget, symValManager->getSymbolIDFromValue(svmRecord->_caller));
dmciRecord->setCallsiteIndex(reloTarget, svmRecord->_callsiteIndex);
dmciRecord->setAppendixObjectNull(reloTarget, svmRecord->_appendixObjectNull);
dmciRecord->setDefiningClassID(reloTarget, symValManager->getSymbolIDFromValue(svmRecord->_definingClass));
dmciRecord->setMethodIndex(reloTarget, fej9->getMethodIndexInClass(svmRecord->_definingClass, svmRecord->_method));
}
break;

case TR_ValidateHandleMethodFromCPIndex:
{
auto *hmciRecord = reinterpret_cast<TR_RelocationRecordValidateHandleMethodFromCPIndex *>(reloRecord);

TR::HandleMethodFromCPIndex *svmRecord = reinterpret_cast<TR::HandleMethodFromCPIndex *>(relocation->getTargetAddress());

hmciRecord->setMethodID(reloTarget, symValManager->getSymbolIDFromValue(svmRecord->_method));
hmciRecord->setCallerID(reloTarget, symValManager->getSymbolIDFromValue(svmRecord->_caller));
hmciRecord->setCpIndex(reloTarget, svmRecord->_cpIndex);
hmciRecord->setAppendixObjectNull(reloTarget, svmRecord->_appendixObjectNull);
hmciRecord->setDefiningClassID(reloTarget, symValManager->getSymbolIDFromValue(svmRecord->_definingClass));
hmciRecord->setMethodIndex(reloTarget, fej9->getMethodIndexInClass(svmRecord->_definingClass, svmRecord->_method));
}
break;

case TR_CallsiteTableEntryAddress:
{
auto *cteaRecord = reinterpret_cast<TR_RelocationRecordCallsiteTableEntryAddress *>(reloRecord);

TR::SymbolReference *symRef = reinterpret_cast<TR::SymbolReference *>(relocation->getTargetAddress());
uint8_t flags = static_cast<uint8_t>(reinterpret_cast<uintptr_t>(relocation->getTargetAddress2()));

TR_OpaqueMethodBlock *method = symRef->getOwningMethod(comp)->getNonPersistentIdentifier();

cteaRecord->setReloFlags(reloTarget, flags);
cteaRecord->setMethodID(reloTarget, symValManager->getSymbolIDFromValue(method));
cteaRecord->setCallsiteIndex(reloTarget, symRef->getSymbol()->getStaticSymbol()->getCallSiteIndex());
}
break;


case TR_MethodTypeTableEntryAddress:
{
auto *mteaRecord = reinterpret_cast<TR_RelocationRecordMethodTypeTableEntryAddress *>(reloRecord);

TR::SymbolReference *symRef = reinterpret_cast<TR::SymbolReference *>(relocation->getTargetAddress());
uint8_t flags = static_cast<uint8_t>(reinterpret_cast<uintptr_t>(relocation->getTargetAddress2()));

TR_OpaqueMethodBlock *method = symRef->getOwningMethod(comp)->getNonPersistentIdentifier();

mteaRecord->setReloFlags(reloTarget, flags);
mteaRecord->setMethodID(reloTarget, symValManager->getSymbolIDFromValue(method));
mteaRecord->setCpIndex(reloTarget, symRef->getSymbol()->getStaticSymbol()->getMethodTypeIndex());
}
break;

default:
TR_ASSERT(false, "Unknown relo type %d!\n", kind);
comp->failCompilation<J9::AOTRelocationRecordGenerationFailure>("Unknown relo type %d!\n", kind);
Expand Down Expand Up @@ -2292,6 +2353,78 @@ J9::AheadOfTimeCompile::dumpRelocationHeaderData(uint8_t *cursor, bool isVerbose
}
break;

case TR_ValidateDynamicMethodFromCallsiteIndex:
{
auto *dmciRecord = reinterpret_cast<TR_RelocationRecordValidateDynamicMethodFromCallsiteIndex *>(reloRecord);

self()->traceRelocationOffsets(startOfOffsets, offsetSize, endOfCurrentRecord, orderedPair);
if (isVerbose)
{
traceMsg(
self()->comp(),
"\n Validate Dynamic Method From Callsite Index: methodID=%d, callerID=%d, callsiteIndex=%d, appendixObjectNull=%s, definingClassID=%d, methodIndex=%d ",
(uint32_t)dmciRecord->methodID(reloTarget),
(uint32_t)dmciRecord->callerID(reloTarget),
dmciRecord->callsiteIndex(reloTarget),
dmciRecord->appendixObjectNull(reloTarget) ? "true" : "false",
(uint32_t)dmciRecord->definingClassID(reloTarget),
dmciRecord->methodIndex(reloTarget));
}
}
break;

case TR_ValidateHandleMethodFromCPIndex:
{
auto *hmciRecord = reinterpret_cast<TR_RelocationRecordValidateHandleMethodFromCPIndex *>(reloRecord);

self()->traceRelocationOffsets(startOfOffsets, offsetSize, endOfCurrentRecord, orderedPair);
if (isVerbose)
{
traceMsg(
self()->comp(),
"\n Validate Handle Method From CP Index: methodID=%d, callerID=%d, cpIndex=%d, appendixObjectNull=%s, definingClassID=%d, methodIndex=%d ",
(uint32_t)hmciRecord->methodID(reloTarget),
(uint32_t)hmciRecord->callerID(reloTarget),
hmciRecord->cpIndex(reloTarget),
hmciRecord->appendixObjectNull(reloTarget) ? "true" : "false",
(uint32_t)hmciRecord->definingClassID(reloTarget),
hmciRecord->methodIndex(reloTarget));
}
}
break;

case TR_CallsiteTableEntryAddress:
{
auto *cteaRecord = reinterpret_cast<TR_RelocationRecordCallsiteTableEntryAddress *>(reloRecord);

self()->traceRelocationOffsets(startOfOffsets, offsetSize, endOfCurrentRecord, orderedPair);
if (isVerbose)
{
traceMsg(
self()->comp(),
"\n Callsite Table Entry Address: methodID=%d, callsiteIndex=%d ",
(uint32_t)cteaRecord->methodID(reloTarget),
cteaRecord->callsiteIndex(reloTarget));
}
}
break;

case TR_MethodTypeTableEntryAddress:
{
auto *mteaRecord = reinterpret_cast<TR_RelocationRecordMethodTypeTableEntryAddress *>(reloRecord);

self()->traceRelocationOffsets(startOfOffsets, offsetSize, endOfCurrentRecord, orderedPair);
if (isVerbose)
{
traceMsg(
self()->comp(),
"\n Method Type Table Entry Address: methodID=%d, cpIndex=%d ",
(uint32_t)mteaRecord->methodID(reloTarget),
mteaRecord->cpIndex(reloTarget));
}
}
break;

default:
TR_ASSERT_FATAL(false, "dumpRelocationHeaderData: unknown relo kind %d\n", kind);
}
Expand Down
17 changes: 17 additions & 0 deletions runtime/compiler/control/JITClientCompilationThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,11 @@ handleServerMessage(JITServer::ClientStream *client, TR_J9VM *fe, JITServer::Mes
// These offsets are initialized later on
vmInfo._vmtargetOffset = 0;
vmInfo._vmindexOffset = 0;
auto sharedCacheConfig = fe->sharedCache() ? fe->sharedCache()->sharedCacheConfig() : NULL;
if (sharedCacheConfig)
vmInfo._shareLambdaForm = J9_ARE_ALL_BITS_SET(sharedCacheConfig->runtimeFlags2, J9SHR_RUNTIMEFLAG2_SHARE_LAMBDAFORM);
else
vmInfo._shareLambdaForm = false;
#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */
}

Expand Down Expand Up @@ -1915,6 +1920,18 @@ handleServerMessage(JITServer::ClientStream *client, TR_J9VM *fe, JITServer::Mes
client->write(response, mirror->isFieldFlattened(comp, cpIndex, isStatic));
}
break;
case MessageType::ResolvedMethod_getTargetMethodFromMemberName:
{
auto recv = client->getRecvData<TR_ResolvedJ9Method *, uintptr_t *>();
TR_ResolvedJ9Method *owningMethod = std::get<0>(recv);
uintptr_t * invokeCacheArray = std::get<1>(recv);

bool isInvokeCacheAppendixNull;
TR_OpaqueMethodBlock *targetMethod = owningMethod->getTargetMethodFromMemberName(invokeCacheArray, &isInvokeCacheAppendixNull);

client->write(response, targetMethod, isInvokeCacheAppendixNull);
}
break;
case MessageType::ResolvedRelocatableMethod_createResolvedRelocatableJ9Method:
{
auto recv = client->getRecvData<TR_ResolvedJ9Method *, J9Method *, int32_t, uint32_t>();
Expand Down
3 changes: 2 additions & 1 deletion runtime/compiler/env/J9SharedCache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ class TR_J9SharedCache : public TR_SharedCache

virtual J9SharedClassCacheDescriptor *getCacheDescriptorList();

J9SharedClassConfig *sharedCacheConfig() { return _sharedCacheConfig; }

protected:
static bool disclaim(const uint8_t *start, const uint8_t *end, UDATA pageSize, bool trace);

Expand Down Expand Up @@ -503,7 +505,6 @@ class TR_J9SharedCache : public TR_SharedCache

J9JITConfig *jitConfig() { return _jitConfig; }
J9JavaVM *javaVM() { return _javaVM; }
J9SharedClassConfig *sharedCacheConfig() { return _sharedCacheConfig; }

TR_AOTStats *aotStats() { return _aotStats; }

Expand Down
90 changes: 74 additions & 16 deletions runtime/compiler/env/j9method.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,30 @@ TR_ResolvedRelocatableJ9Method::isUnresolvedMethodHandle(I_32 cpIndex)
return true;
}

bool
TR_ResolvedRelocatableJ9Method::isUnresolvedCallSiteTableEntry(int32_t callSiteIndex)
{
bool unresolved = true;
J9JavaVM * javaVM = fej9()->_jitConfig->javaVM;
if (J9_ARE_ALL_BITS_SET(javaVM->sharedClassConfig->runtimeFlags2, J9SHR_RUNTIMEFLAG2_SHARE_LAMBDAFORM))
{
unresolved = TR_ResolvedJ9Method::isUnresolvedCallSiteTableEntry(callSiteIndex);
}
return unresolved;
}

bool
TR_ResolvedRelocatableJ9Method::isUnresolvedMethodTypeTableEntry(int32_t cpIndex)
{
bool unresolved = true;
J9JavaVM * javaVM = fej9()->_jitConfig->javaVM;
if (J9_ARE_ALL_BITS_SET(javaVM->sharedClassConfig->runtimeFlags2, J9SHR_RUNTIMEFLAG2_SHARE_LAMBDAFORM))
{
unresolved = TR_ResolvedJ9Method::isUnresolvedMethodTypeTableEntry(cpIndex);
}
return unresolved;
}

TR_ResolvedMethod *
TR_ResolvedRelocatableJ9Method::getResolvedPossiblyPrivateVirtualMethod(
TR::Compilation *comp,
Expand Down Expand Up @@ -6970,6 +6994,20 @@ TR_ResolvedJ9Method::handleUnresolvedVirtualMethodInCP(int32_t cpIndex, bool * u
{
}

TR_OpaqueMethodBlock *
TR_ResolvedJ9Method::getTargetMethodFromMemberName(uintptr_t * invokeCacheArray, bool * isInvokeCacheAppendixNull)
{
TR::VMAccessCriticalSection getTargetMethodCS(fej9());
TR_OpaqueMethodBlock *targetJ9MethodBlock = fej9()->targetMethodFromMemberName((uintptr_t) fej9()->getReferenceElement(*invokeCacheArray, JSR292_invokeCacheArrayMemberNameIndex));
// if the callSite table entry / method type table entry is resolved,
// we can check if the appendix object is null,
// in which case the appendix object must not be pushed to stack
auto appendixObject = fej9()->getReferenceElement(*invokeCacheArray, JSR292_invokeCacheArrayAppendixIndex);
if (isInvokeCacheAppendixNull && !appendixObject)
*isInvokeCacheAppendixNull = true;
return targetJ9MethodBlock;
}

TR_ResolvedMethod *
TR_ResolvedJ9Method::getResolvedDynamicMethod(TR::Compilation * comp, I_32 callSiteIndex, bool * unresolvedInCP, bool * isInvokeCacheAppendixNull)
{
Expand Down Expand Up @@ -7006,13 +7044,12 @@ TR_ResolvedJ9Method::getResolvedDynamicMethod(TR::Compilation * comp, I_32 callS
J9UTF8 *signature = J9ROMNAMEANDSIGNATURE_SIGNATURE(nameAndSig);

#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
if (isInvokeCacheAppendixNull)
*isInvokeCacheAppendixNull = false;
bool invokeCacheAppendixNull = false;

if (!isUnresolvedEntry)
{
TR_OpaqueMethodBlock * targetJ9MethodBlock = NULL;
uintptr_t * invokeCacheArray = (uintptr_t *) callSiteTableEntryAddress(callSiteIndex);

// invokedynamic resolution can either result in a valid entry in the corresponding CallSite table slot if successful,
// or an exception object otherwise. The CallSite table entry is a two-element array containing the MemberName
// and appendix objects necessary for constructing a resolved invokedynamic adapter method call.
Expand All @@ -7023,14 +7060,21 @@ TR_ResolvedJ9Method::getResolvedDynamicMethod(TR::Compilation * comp, I_32 callS
comp->failCompilation<TR::CompilationException>("Invalid CallSite table entry for invokedynamic");
}

TR_OpaqueMethodBlock * targetJ9MethodBlock = getTargetMethodFromMemberName(invokeCacheArray, &invokeCacheAppendixNull);

if (comp->compileRelocatableCode())
{
TR::VMAccessCriticalSection getResolvedDynamicMethod(fej9());
targetJ9MethodBlock = fej9()->targetMethodFromMemberName((uintptr_t) fej9()->getReferenceElement(*invokeCacheArray, JSR292_invokeCacheArrayMemberNameIndex)); // this will not work in AOT or JITServer
// if the callSite table entry is resolved, we can check if the appendix object is null,
// in which case the appendix object must not be pushed to stack
uintptr_t appendixObject = (uintptr_t) fej9()->getReferenceElement(*invokeCacheArray, JSR292_invokeCacheArrayAppendixIndex);
if (isInvokeCacheAppendixNull && !appendixObject) *isInvokeCacheAppendixNull = true;
bool valid =
comp->getSymbolValidationManager()->addDynamicMethodFromCallsiteIndex(
targetJ9MethodBlock,
getNonPersistentIdentifier(),
callSiteIndex,
invokeCacheAppendixNull);

if (!valid)
comp->failCompilation<J9::AOTHasInvokeHandle>("Failed to add validation record for resolved dynamic method %p", targetJ9MethodBlock);
}

result = fej9()->createResolvedMethod(comp->trMemory(), targetJ9MethodBlock, this);
}
else
Expand All @@ -7044,6 +7088,9 @@ TR_ResolvedJ9Method::getResolvedDynamicMethod(TR::Compilation * comp, I_32 callS
char * linkToStaticSignature = _fe->getSignatureForLinkToStaticForInvokeDynamic(comp, signature, signatureLength);
result = _fe->createResolvedMethodWithSignature(comp->trMemory(), dummyInvoke, NULL, linkToStaticSignature, signatureLength, this);
}

if (isInvokeCacheAppendixNull)
*isInvokeCacheAppendixNull = invokeCacheAppendixNull;
#else
TR_OpaqueMethodBlock *dummyInvokeExact = _fe->getMethodFromName("java/lang/invoke/MethodHandle", "invokeExact", JSR292_invokeExactSig);
result = _fe->createResolvedMethodWithSignature(comp->trMemory(), dummyInvokeExact, NULL, utf8Data(signature), J9UTF8_LENGTH(signature), this);
Expand Down Expand Up @@ -7081,19 +7128,27 @@ TR_ResolvedJ9Method::getResolvedHandleMethod(TR::Compilation * comp, I_32 cpInde
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
J9UTF8 *signature = J9ROMNAMEANDSIGNATURE_SIGNATURE(nameAndSig);

if (isInvokeCacheAppendixNull)
*isInvokeCacheAppendixNull = false;
bool invokeCacheAppendixNull = false;

if (!isUnresolvedEntry)
{
uintptr_t * invokeCacheArray = (uintptr_t *) methodTypeTableEntryAddress(cpIndex);
TR_OpaqueMethodBlock * targetJ9MethodBlock = NULL;

TR_OpaqueMethodBlock * targetJ9MethodBlock = getTargetMethodFromMemberName(invokeCacheArray, &invokeCacheAppendixNull);

if (comp->compileRelocatableCode())
{
TR::VMAccessCriticalSection getResolvedHandleMethod(fej9());
targetJ9MethodBlock = fej9()->targetMethodFromMemberName((uintptr_t) fej9()->getReferenceElement(*invokeCacheArray, JSR292_invokeCacheArrayMemberNameIndex)); // this will not work in AOT or JITServer
uintptr_t appendixObject = (uintptr_t) fej9()->getReferenceElement(*invokeCacheArray, JSR292_invokeCacheArrayAppendixIndex);
if (isInvokeCacheAppendixNull && !appendixObject) *isInvokeCacheAppendixNull = true;
bool valid =
comp->getSymbolValidationManager()->addHandleMethodFromCPIndex(
targetJ9MethodBlock,
getNonPersistentIdentifier(),
cpIndex,
invokeCacheAppendixNull);

if (!valid)
comp->failCompilation<J9::AOTHasInvokeHandle>("Failed to add validation record for resolved handle method %p", targetJ9MethodBlock);
}

result = fej9()->createResolvedMethod(comp->trMemory(), targetJ9MethodBlock, this);
}
else
Expand All @@ -7107,6 +7162,9 @@ TR_ResolvedJ9Method::getResolvedHandleMethod(TR::Compilation * comp, I_32 cpInde
char * linkToStaticSignature = _fe->getSignatureForLinkToStaticForInvokeHandle(comp, signature, signatureLength);
result = _fe->createResolvedMethodWithSignature(comp->trMemory(), dummyInvoke, NULL, linkToStaticSignature, signatureLength, this);
}

if (isInvokeCacheAppendixNull)
*isInvokeCacheAppendixNull = invokeCacheAppendixNull;
#else

TR_OpaqueMethodBlock *dummyInvokeExact = _fe->getMethodFromName("java/lang/invoke/MethodHandle", "invokeExact", JSR292_invokeExactSig);
Expand Down
Loading

0 comments on commit 3638df4

Please sign in to comment.