Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SVM AOT support for resolved invokeHandle/invokeDynamic dispatch #20373

Merged
merged 8 commits into from
Nov 12, 2024
Merged
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