diff --git a/runtime/codert_vm/arm64nathelp.m4 b/runtime/codert_vm/arm64nathelp.m4 index 2489797f58e..7958aacdee1 100644 --- a/runtime/codert_vm/arm64nathelp.m4 +++ b/runtime/codert_vm/arm64nathelp.m4 @@ -321,8 +321,10 @@ OLD_DUAL_MODE_HELPER_NO_RETURN_VALUE(jitPutFlattenableStaticField,3) OLD_DUAL_MODE_HELPER(jitLoadFlattenableArrayElement,2) OLD_DUAL_MODE_HELPER_NO_RETURN_VALUE(jitStoreFlattenableArrayElement,3) SLOW_PATH_ONLY_HELPER_NO_RETURN_VALUE(jitResolveFlattenableField,3) -FAST_PATH_ONLY_HELPER(jitLookupDynamicInterfaceMethod,3) -OLD_DUAL_MODE_HELPER(jitLookupDynamicPublicInterfaceMethod,3) + +ifdef({ASM_J9VM_OPT_OPENJDK_METHODHANDLE},{ +OLD_DUAL_MODE_HELPER(jitLookupDynamicPublicInterfaceMethod,2) +}) dnl ASM_J9VM_OPT_OPENJDK_METHODHANDLE dnl Trap handlers diff --git a/runtime/codert_vm/armnathelp.m4 b/runtime/codert_vm/armnathelp.m4 index 0c89f385f1a..8158c3f1e68 100644 --- a/runtime/codert_vm/armnathelp.m4 +++ b/runtime/codert_vm/armnathelp.m4 @@ -299,8 +299,10 @@ OLD_DUAL_MODE_HELPER_NO_RETURN_VALUE(jitPutFlattenableStaticField,3) OLD_DUAL_MODE_HELPER(jitLoadFlattenableArrayElement,2) OLD_DUAL_MODE_HELPER_NO_RETURN_VALUE(jitStoreFlattenableArrayElement,3) SLOW_PATH_ONLY_HELPER_NO_RETURN_VALUE(jitResolveFlattenableField,3) -FAST_PATH_ONLY_HELPER(jitLookupDynamicInterfaceMethod,3) -OLD_DUAL_MODE_HELPER(jitLookupDynamicPublicInterfaceMethod,3) + +ifdef({ASM_J9VM_OPT_OPENJDK_METHODHANDLE},{ +OLD_DUAL_MODE_HELPER(jitLookupDynamicPublicInterfaceMethod,2) +}) dnl ASM_J9VM_OPT_OPENJDK_METHODHANDLE dnl Trap handlers diff --git a/runtime/codert_vm/cnathelp.cpp b/runtime/codert_vm/cnathelp.cpp index 20f4c940e1b..2dad6396e96 100644 --- a/runtime/codert_vm/cnathelp.cpp +++ b/runtime/codert_vm/cnathelp.cpp @@ -1623,19 +1623,7 @@ old_fast_jitLookupInterfaceMethod(J9VMThread *currentThread) return slowPath; } -void J9FASTCALL -old_fast_jitLookupDynamicInterfaceMethod(J9VMThread *currentThread) -{ - OLD_JIT_HELPER_PROLOGUE(3); - DECLARE_JIT_CLASS_PARM(receiverClass, 1); - DECLARE_JIT_CLASS_PARM(interfaceClass, 2); - DECLARE_JIT_PARM(UDATA, iTableIndex, 3); - UDATA iTableOffset = sizeof(struct J9ITable) + (iTableIndex * sizeof(UDATA)); - UDATA vTableOffset = convertITableOffsetToVTableOffset(currentThread, receiverClass, interfaceClass, iTableOffset); - Assert_CodertVM_false(0 == vTableOffset); - JIT_RETURN_UDATA(vTableOffset); -} - +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) void* J9FASTCALL old_slow_jitLookupDynamicPublicInterfaceMethod(J9VMThread *currentThread) { @@ -1651,10 +1639,13 @@ void* J9FASTCALL old_fast_jitLookupDynamicPublicInterfaceMethod(J9VMThread *currentThread) { void *slowPath = (void*)old_slow_jitLookupDynamicPublicInterfaceMethod; - OLD_JIT_HELPER_PROLOGUE(3); + OLD_JIT_HELPER_PROLOGUE(2); DECLARE_JIT_CLASS_PARM(receiverClass, 1); - DECLARE_JIT_CLASS_PARM(interfaceClass, 2); - DECLARE_JIT_PARM(UDATA, iTableIndex, 3); + DECLARE_JIT_PARM(j9object_t, memberName, 2); + J9JavaVM *vm = currentThread->javaVM; + J9Method *interfaceMethod = (J9Method *)(UDATA)J9OBJECT_U64_LOAD(currentThread, memberName, vm->vmtargetOffset); + J9Class *interfaceClass = J9_CLASS_FROM_METHOD(interfaceMethod); + UDATA iTableIndex = (UDATA)J9OBJECT_U64_LOAD(currentThread, memberName, vm->vmindexOffset); UDATA iTableOffset = sizeof(struct J9ITable) + (iTableIndex * sizeof(UDATA)); UDATA vTableOffset = convertITableOffsetToVTableOffset(currentThread, receiverClass, interfaceClass, iTableOffset); Assert_CodertVM_false(0 == vTableOffset); @@ -1668,6 +1659,7 @@ old_fast_jitLookupDynamicPublicInterfaceMethod(J9VMThread *currentThread) } return slowPath; } +#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ void J9FASTCALL old_fast_jitMethodIsNative(J9VMThread *currentThread) @@ -3961,9 +3953,10 @@ initPureCFunctionTable(J9JavaVM *vm) jitConfig->old_fast_jitInstanceOf = (void*)old_fast_jitInstanceOf; jitConfig->old_fast_jitLookupInterfaceMethod = (void*)old_fast_jitLookupInterfaceMethod; jitConfig->old_slow_jitLookupInterfaceMethod = (void*)old_slow_jitLookupInterfaceMethod; - jitConfig->old_fast_jitLookupDynamicInterfaceMethod = (void*)old_fast_jitLookupDynamicInterfaceMethod; +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) jitConfig->old_fast_jitLookupDynamicPublicInterfaceMethod = (void*)old_fast_jitLookupDynamicPublicInterfaceMethod; jitConfig->old_slow_jitLookupDynamicPublicInterfaceMethod = (void*)old_slow_jitLookupDynamicPublicInterfaceMethod; +#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ jitConfig->old_fast_jitMethodIsNative = (void*)old_fast_jitMethodIsNative; jitConfig->old_fast_jitMethodIsSync = (void*)old_fast_jitMethodIsSync; jitConfig->old_fast_jitMethodMonitorEntry = (void*)old_fast_jitMethodMonitorEntry; diff --git a/runtime/codert_vm/pnathelp.m4 b/runtime/codert_vm/pnathelp.m4 index 9adcb7c0fe5..99613389a46 100644 --- a/runtime/codert_vm/pnathelp.m4 +++ b/runtime/codert_vm/pnathelp.m4 @@ -369,8 +369,10 @@ OLD_DUAL_MODE_HELPER_NO_RETURN_VALUE(jitPutFlattenableStaticField,3) OLD_DUAL_MODE_HELPER(jitLoadFlattenableArrayElement,2) OLD_DUAL_MODE_HELPER_NO_RETURN_VALUE(jitStoreFlattenableArrayElement,3) SLOW_PATH_ONLY_HELPER_NO_RETURN_VALUE(jitResolveFlattenableField,3) -FAST_PATH_ONLY_HELPER(jitLookupDynamicInterfaceMethod,3) -OLD_DUAL_MODE_HELPER(jitLookupDynamicPublicInterfaceMethod,3) + +ifdef({ASM_J9VM_OPT_OPENJDK_METHODHANDLE},{ +OLD_DUAL_MODE_HELPER(jitLookupDynamicPublicInterfaceMethod,2) +}) dnl ASM_J9VM_OPT_OPENJDK_METHODHANDLE dnl Trap handlers diff --git a/runtime/codert_vm/riscvnathelp.m4 b/runtime/codert_vm/riscvnathelp.m4 index c9042234d0d..1a7483f8f13 100644 --- a/runtime/codert_vm/riscvnathelp.m4 +++ b/runtime/codert_vm/riscvnathelp.m4 @@ -292,8 +292,10 @@ OLD_DUAL_MODE_HELPER(jitGetFlattenableStaticField,2) OLD_DUAL_MODE_HELPER_NO_RETURN_VALUE(jitPutFlattenableStaticField,3) OLD_DUAL_MODE_HELPER(jitLoadFlattenableArrayElement,2) OLD_DUAL_MODE_HELPER_NO_RETURN_VALUE(jitStoreFlattenableArrayElement,3) -FAST_PATH_ONLY_HELPER(jitLookupDynamicInterfaceMethod,3) -OLD_DUAL_MODE_HELPER(jitLookupDynamicPublicInterfaceMethod,3) + +ifdef({ASM_J9VM_OPT_OPENJDK_METHODHANDLE},{ +OLD_DUAL_MODE_HELPER(jitLookupDynamicPublicInterfaceMethod,2) +}) dnl ASM_J9VM_OPT_OPENJDK_METHODHANDLE dnl Trap handlers diff --git a/runtime/codert_vm/xnathelp.m4 b/runtime/codert_vm/xnathelp.m4 index 74c5a415211..f27ee9334a7 100644 --- a/runtime/codert_vm/xnathelp.m4 +++ b/runtime/codert_vm/xnathelp.m4 @@ -419,8 +419,10 @@ OLD_DUAL_MODE_HELPER_NO_RETURN_VALUE(jitPutFlattenableStaticField,3) OLD_DUAL_MODE_HELPER(jitLoadFlattenableArrayElement,2) OLD_DUAL_MODE_HELPER_NO_RETURN_VALUE(jitStoreFlattenableArrayElement,3) SLOW_PATH_ONLY_HELPER_NO_RETURN_VALUE(jitResolveFlattenableField,3) -FAST_PATH_ONLY_HELPER(jitLookupDynamicInterfaceMethod,3) -OLD_DUAL_MODE_HELPER(jitLookupDynamicPublicInterfaceMethod,3) + +ifdef({ASM_J9VM_OPT_OPENJDK_METHODHANDLE},{ +OLD_DUAL_MODE_HELPER(jitLookupDynamicPublicInterfaceMethod,2) +}) dnl ASM_J9VM_OPT_OPENJDK_METHODHANDLE dnl Trap handlers diff --git a/runtime/codert_vm/znathelp.m4 b/runtime/codert_vm/znathelp.m4 index c4385787bd2..e30782ba4b9 100644 --- a/runtime/codert_vm/znathelp.m4 +++ b/runtime/codert_vm/znathelp.m4 @@ -344,8 +344,10 @@ OLD_DUAL_MODE_HELPER_NO_RETURN_VALUE(jitPutFlattenableStaticField,3) OLD_DUAL_MODE_HELPER(jitLoadFlattenableArrayElement,2) OLD_DUAL_MODE_HELPER_NO_RETURN_VALUE(jitStoreFlattenableArrayElement,3) SLOW_PATH_ONLY_HELPER_NO_RETURN_VALUE(jitResolveFlattenableField,3) -FAST_PATH_ONLY_HELPER(jitLookupDynamicInterfaceMethod,3) -OLD_DUAL_MODE_HELPER(jitLookupDynamicPublicInterfaceMethod,3) + +ifdef({ASM_J9VM_OPT_OPENJDK_METHODHANDLE},{ +OLD_DUAL_MODE_HELPER(jitLookupDynamicPublicInterfaceMethod,2) +}) dnl ASM_J9VM_OPT_OPENJDK_METHODHANDLE dnl Trap handlers diff --git a/runtime/compiler/compile/J9SymbolReferenceTable.cpp b/runtime/compiler/compile/J9SymbolReferenceTable.cpp index ff775428b06..1b488c93544 100644 --- a/runtime/compiler/compile/J9SymbolReferenceTable.cpp +++ b/runtime/compiler/compile/J9SymbolReferenceTable.cpp @@ -306,13 +306,6 @@ J9::SymbolReferenceTable::findOrCreateStoreFlattenableArrayElementSymbolRef(TR:: } -TR::SymbolReference * -J9::SymbolReferenceTable::findOrCreateLookupDynamicInterfaceMethodSymbolRef() - { - return findOrCreateRuntimeHelper(TR_jitLookupDynamicInterfaceMethod, false, false, true); - } - - TR::SymbolReference * J9::SymbolReferenceTable::findOrCreateLookupDynamicPublicInterfaceMethodSymbolRef() { @@ -860,6 +853,28 @@ J9::SymbolReferenceTable::findOrFabricateShadowSymbol( return symRef; } +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) +TR::SymbolReference * +J9::SymbolReferenceTable::findOrFabricateMemberNameVmTargetShadow() + { + bool isVolatile = false; + bool isPrivate = false; + bool isFinal = true; + TR::SymbolReference *symRef = self()->findOrFabricateShadowSymbol( + comp()->getMethodSymbol(), + TR::Symbol::Java_lang_invoke_MemberName_vmtarget, + TR::Address, + comp()->fej9()->getVMTargetOffset(), + isVolatile, + isPrivate, + isFinal, + "java/lang/invoke/MemberName.vmtarget J"); + + symRef->getSymbol()->setNotCollected(); + return symRef; + } +#endif + TR::SymbolReference * J9::SymbolReferenceTable::findFlattenedArrayElementFieldShadow( ResolvedFieldShadowKey key, diff --git a/runtime/compiler/compile/J9SymbolReferenceTable.hpp b/runtime/compiler/compile/J9SymbolReferenceTable.hpp index 9c3366c1cdd..55fa406791b 100644 --- a/runtime/compiler/compile/J9SymbolReferenceTable.hpp +++ b/runtime/compiler/compile/J9SymbolReferenceTable.hpp @@ -159,16 +159,13 @@ class SymbolReferenceTable : public OMR::SymbolReferenceTableConnector // This helper walks the iTables to find the VFT offset for an interface // method that is not known until runtime (and therefore does not have an - // IPIC data snippet). The parameters are the receiver class, interface - // class, and iTable index (though in the trees they must appear as - // children in the opposite order), and the return value is the VFT offset. + // IPIC data snippet). The parameters are the receiver class and MemberName + // (though in the trees they must appear as children in the opposite order), + // and the return value is the VFT offset. // // The given receiver class must implement the given interface class. + // IllegalAccessError is thrown if the dispatched callee is not public. // - TR::SymbolReference * findOrCreateLookupDynamicInterfaceMethodSymbolRef(); - - // This helper is a variant of jitLookupDynamicInterfaceMethod that requires - // the dispatched callee to be public, otherwise throwing IllegalAccessError. TR::SymbolReference * findOrCreateLookupDynamicPublicInterfaceMethodSymbolRef(); TR::SymbolReference * findOrCreateShadowSymbol(TR::ResolvedMethodSymbol * owningMethodSymbol, int32_t cpIndex, bool isStore); @@ -201,6 +198,11 @@ class SymbolReferenceTable : public OMR::SymbolReferenceTableConnector */ TR::SymbolReference * findOrFabricateShadowSymbol(TR_OpaqueClassBlock *containingClass, TR::DataType type, uint32_t offset, bool isVolatile, bool isPrivate, bool isFinal, const char *name, const char *signature); +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) + /// Find or fabricate the shadow symref for MemberName.vmtarget + TR::SymbolReference * findOrFabricateMemberNameVmTargetShadow(); +#endif + /** \brief * Returns an array shadow symbol reference fabricated for the field of a flattened array element. * diff --git a/runtime/compiler/control/JITClientCompilationThread.cpp b/runtime/compiler/control/JITClientCompilationThread.cpp index eefe897f0a0..9299e518fca 100644 --- a/runtime/compiler/control/JITClientCompilationThread.cpp +++ b/runtime/compiler/control/JITClientCompilationThread.cpp @@ -1135,10 +1135,12 @@ handleServerMessage(JITServer::ClientStream *client, TR_J9VM *fe, JITServer::Mes client->write(response, fe->isLambdaFormGeneratedMethod(std::get<0>(recv))); } break; - case MessageType::VM_vTableOrITableIndexFromMemberName: + case MessageType::VM_getMemberNameMethodInfo: { auto recv = client->getRecvData(); - client->write(response, fe->vTableOrITableIndexFromMemberName(comp, std::get<0>(recv))); + TR_J9VMBase::MemberNameMethodInfo info = {}; + uintptr_t ok = fe->getMemberNameMethodInfo(comp, std::get<0>(recv), &info); + client->write(response, ok, info.vmtarget, info.vmindex, info.clazz, info.refKind); } break; case MessageType::VM_delegatingMethodHandleTarget: diff --git a/runtime/compiler/env/VMJ9.cpp b/runtime/compiler/env/VMJ9.cpp index 504bbc29b28..241729d036d 100644 --- a/runtime/compiler/env/VMJ9.cpp +++ b/runtime/compiler/env/VMJ9.cpp @@ -4755,49 +4755,44 @@ TR_J9VMBase::targetMethodFromMethodHandle(TR::Compilation* comp, TR::KnownObject return NULL; } -J9JNIMethodID* -TR_J9VMBase::jniMethodIdFromMemberName(uintptr_t memberName) +bool +TR_J9VMBase::getMemberNameMethodInfo( + TR::Compilation* comp, + TR::KnownObjectTable::Index objIndex, + MemberNameMethodInfo *out) { - TR_ASSERT(haveAccess(), "jniMethodIdFromMemberName requires VM access"); - return (J9JNIMethodID *)J9OBJECT_U64_LOAD(vmThread(), memberName, vmThread()->javaVM->vmindexOffset); - } + *out = {}; + + if (objIndex == TR::KnownObjectTable::UNKNOWN) + return false; -J9JNIMethodID* -TR_J9VMBase::jniMethodIdFromMemberName(TR::Compilation* comp, TR::KnownObjectTable::Index objIndex) - { auto knot = comp->getKnownObjectTable(); - if (objIndex != TR::KnownObjectTable::UNKNOWN && - knot && - !knot->isNull(objIndex)) - { - TR::VMAccessCriticalSection jniMethodId(this); - uintptr_t object = knot->getPointer(objIndex); - return jniMethodIdFromMemberName(object); - } - return NULL; - } + if (knot == NULL || knot->isNull(objIndex)) + return false; -uintptr_t -TR_J9VMBase::vTableOrITableIndexFromMemberName(uintptr_t memberName) - { - TR_ASSERT(haveAccess(), "vTableOrITableIndexFromMemberName requires VM access"); - auto methodID = jniMethodIdFromMemberName(memberName); - return methodID->vTableIndex; - } + TR::VMAccessCriticalSection getMemberNameMethodInfo(this); + uintptr_t object = knot->getPointer(objIndex); + const char *mnClassName = "java/lang/invoke/MemberName"; + int32_t mnClassNameLen = (int32_t)strlen(mnClassName); + TR_OpaqueClassBlock *mnClass = + getSystemClassFromClassName(mnClassName, mnClassNameLen); -uintptr_t -TR_J9VMBase::vTableOrITableIndexFromMemberName(TR::Compilation* comp, TR::KnownObjectTable::Index objIndex) - { - auto knot = comp->getKnownObjectTable(); - if (objIndex != TR::KnownObjectTable::UNKNOWN && - knot && - !knot->isNull(objIndex)) - { - TR::VMAccessCriticalSection vTableOrITableIndex(this); - uintptr_t object = knot->getPointer(objIndex); - return vTableOrITableIndexFromMemberName(object); - } - return (uintptr_t)-1; + if (getObjectClass(object) != mnClass) + return false; + + int32_t flags = getInt32Field(object, "flags"); + if ((flags & (MN_IS_METHOD | MN_IS_CONSTRUCTOR)) == 0) + return false; + + auto tgt = J9OBJECT_U64_LOAD(vmThread(), object, vmThread()->javaVM->vmtargetOffset); + auto ix = J9OBJECT_U64_LOAD(vmThread(), object, vmThread()->javaVM->vmindexOffset); + uintptr_t jlClass = getReferenceField(object, "clazz", "Ljava/lang/Class;"); + + out->vmtarget = (TR_OpaqueMethodBlock*)(uintptr_t)tgt; + out->vmindex = (uintptr_t)ix; + out->clazz = getClassFromJavaLangClass(jlClass); + out->refKind = (flags >> MN_REFERENCE_KIND_SHIFT) & MN_REFERENCE_KIND_MASK; + return true; } bool diff --git a/runtime/compiler/env/VMJ9.h b/runtime/compiler/env/VMJ9.h index 855fbde0ff4..aea41cf10fe 100644 --- a/runtime/compiler/env/VMJ9.h +++ b/runtime/compiler/env/VMJ9.h @@ -875,30 +875,28 @@ class TR_J9VMBase : public TR_FrontEnd * VM access is not required */ virtual TR_OpaqueMethodBlock* targetMethodFromMethodHandle(TR::Compilation* comp, TR::KnownObjectTable::Index objIndex); + + /// A summary of the method dispatch information in a MemberName. + struct MemberNameMethodInfo + { + TR_OpaqueMethodBlock *vmtarget; ///< the target J9Method + uintptr_t vmindex; ///< the interpreter vtable offset, itable index, or -1 + TR_OpaqueClassBlock *clazz; ///< the relevant subtype of the defining class of vmtarget + int32_t refKind; ///< one of the MH_REF_* constants (JVMS table 5.4.3.5-A) + }; + /* - * \brief - * Return MemberName.vmindex, a J9JNIMethodID pointer containing vtable/itable offset for the MemberName method - * Caller must acquire VM access - */ - virtual J9JNIMethodID* jniMethodIdFromMemberName(uintptr_t memberName); - /* - * \brief - * Return MemberName.vmindex, a J9JNIMethodID pointer containing vtable/itable offset for the MemberName method - * VM access is not required - */ - virtual J9JNIMethodID* jniMethodIdFromMemberName(TR::Compilation* comp, TR::KnownObjectTable::Index objIndex); - /* - * \brief - * Return vtable or itable index of a method represented by MemberName - * Caller must acquire VM access - */ - virtual uintptr_t vTableOrITableIndexFromMemberName(uintptr_t memberName); - /* - * \brief - * Return vtable or itable index of a method represented by MemberName - * VM access is not required + * \brief Summarize a MemberName representing a method or constructor. + * + * \param comp the compilation object + * \param objIndex the known object index of the MemberName object + * \param[out] out filled on success, zeroed on failure + * \return true on success, false on error (e.g. not a MemberName) */ - virtual uintptr_t vTableOrITableIndexFromMemberName(TR::Compilation* comp, TR::KnownObjectTable::Index objIndex); + virtual bool getMemberNameMethodInfo( + TR::Compilation* comp, + TR::KnownObjectTable::Index objIndex, + MemberNameMethodInfo *out); /** * \brief diff --git a/runtime/compiler/env/VMJ9Server.cpp b/runtime/compiler/env/VMJ9Server.cpp index 6ee4b05a6cd..e58200304d8 100644 --- a/runtime/compiler/env/VMJ9Server.cpp +++ b/runtime/compiler/env/VMJ9Server.cpp @@ -2289,44 +2289,35 @@ TR_J9ServerVM::refineInvokeCacheElementSymRefWithKnownObjectIndex(TR::Compilatio return comp->getSymRefTab()->findOrCreateSymRefWithKnownObject(originalSymRef, idx); } -J9JNIMethodID* -TR_J9ServerVM::jniMethodIdFromMemberName(uintptr_t memberName) - { - TR_ASSERT_FATAL(false, "jniMethodIdFromMemberName must not be called on JITServer"); - return NULL; - } - -J9JNIMethodID* -TR_J9ServerVM::jniMethodIdFromMemberName(TR::Compilation* comp, TR::KnownObjectTable::Index objIndex) - { - // This could be made to work on JITServer, however we would need to receive a copy of J9JNIMethodID - // and put it in some sort of cache, to avoid memory allocation every time this method is called. - // Since the only parameter of J9JNIMethodID currently accessed is vTableIndex, we just use - // vTableOrITableIndexFromMemberName to get the index from the client directly. - TR_ASSERT_FATAL(false, "jniMethodIdFromMemberName must not be called on JITServer"); - return NULL; - } - -uintptr_t -TR_J9ServerVM::vTableOrITableIndexFromMemberName(uintptr_t memberName) +bool +TR_J9ServerVM::getMemberNameMethodInfo( + TR::Compilation* comp, + TR::KnownObjectTable::Index objIndex, + MemberNameMethodInfo *out) { - TR_ASSERT_FATAL(false, "vTableOrITableIndexFromMemberName must not be called on JITServer"); - return 0; - } + *out = {}; -uintptr_t -TR_J9ServerVM::vTableOrITableIndexFromMemberName(TR::Compilation* comp, TR::KnownObjectTable::Index objIndex) - { auto knot = comp->getKnownObjectTable(); - if (objIndex != TR::KnownObjectTable::UNKNOWN && - knot && - !knot->isNull(objIndex)) + if (objIndex == TR::KnownObjectTable::UNKNOWN + || knot == NULL + || knot->isNull(objIndex)) { - JITServer::ServerStream *stream = _compInfoPT->getMethodBeingCompiled()->_stream; - stream->write(JITServer::MessageType::VM_vTableOrITableIndexFromMemberName, objIndex); - return std::get<0>(stream->read()); + return false; + } + + JITServer::ServerStream *stream = _compInfoPT->getMethodBeingCompiled()->_stream; + stream->write(JITServer::MessageType::VM_getMemberNameMethodInfo, objIndex); + auto recv = stream->read(); + bool ok = std::get<0>(recv); + if (ok) + { + out->vmtarget = std::get<1>(recv); + out->vmindex = std::get<2>(recv); + out->clazz = std::get<3>(recv); + out->refKind = std::get<4>(recv); } - return (uintptr_t)-1; + + return ok; } TR::KnownObjectTable::Index diff --git a/runtime/compiler/env/VMJ9Server.hpp b/runtime/compiler/env/VMJ9Server.hpp index f7f832c03fd..27e47a1f87f 100644 --- a/runtime/compiler/env/VMJ9Server.hpp +++ b/runtime/compiler/env/VMJ9Server.hpp @@ -238,10 +238,7 @@ class TR_J9ServerVM: public TR_J9VM virtual TR::SymbolReference* refineInvokeCacheElementSymRefWithKnownObjectIndex(TR::Compilation *comp, TR::SymbolReference *originalSymRef, uintptr_t *invokeCacheArray) override; virtual bool isInvokeCacheEntryAnArray(uintptr_t *invokeCacheArray) override; - virtual J9JNIMethodID* jniMethodIdFromMemberName(uintptr_t memberName) override; - virtual J9JNIMethodID* jniMethodIdFromMemberName(TR::Compilation* comp, TR::KnownObjectTable::Index objIndex) override; - virtual uintptr_t vTableOrITableIndexFromMemberName(uintptr_t memberName) override; - virtual uintptr_t vTableOrITableIndexFromMemberName(TR::Compilation* comp, TR::KnownObjectTable::Index objIndex) override; + virtual bool getMemberNameMethodInfo(TR::Compilation* comp, TR::KnownObjectTable::Index objIndex, MemberNameMethodInfo *out) override; virtual TR::KnownObjectTable::Index delegatingMethodHandleTargetHelper( TR::Compilation *comp, TR::KnownObjectTable::Index dmhIndex, TR_OpaqueClassBlock *cwClass) override; virtual UDATA getVMTargetOffset() override; virtual UDATA getVMIndexOffset() override; diff --git a/runtime/compiler/net/CommunicationStream.hpp b/runtime/compiler/net/CommunicationStream.hpp index bcd9c17f663..3ba27ce0c39 100644 --- a/runtime/compiler/net/CommunicationStream.hpp +++ b/runtime/compiler/net/CommunicationStream.hpp @@ -118,7 +118,7 @@ class CommunicationStream // likely to lose an increment when merging/rebasing/etc. // static const uint8_t MAJOR_NUMBER = 1; - static const uint16_t MINOR_NUMBER = 50; // ID: rTTHu2gMya2I5Wwu3nGy + static const uint16_t MINOR_NUMBER = 51; // ID: cGK/z3DilpnVN85FsPsD static const uint8_t PATCH_NUMBER = 0; static uint32_t CONFIGURATION_FLAGS; diff --git a/runtime/compiler/net/MessageTypes.cpp b/runtime/compiler/net/MessageTypes.cpp index 724c25ab7cc..9862c3f429f 100644 --- a/runtime/compiler/net/MessageTypes.cpp +++ b/runtime/compiler/net/MessageTypes.cpp @@ -179,7 +179,7 @@ const char *messageNames[] = "VM_targetMethodFromInvokeCacheArrayMemberNameObj", "VM_refineInvokeCacheElementSymRefWithKnownObjectIndex", "VM_isLambdaFormGeneratedMethod", - "VM_vTableOrITableIndexFromMemberName", + "VM_getMemberNameMethodInfo", "VM_isMethodHandleExpectedType", "VM_getMemberNameFieldKnotIndexFromMethodHandleKnotIndex", "VM_isStable", diff --git a/runtime/compiler/net/MessageTypes.hpp b/runtime/compiler/net/MessageTypes.hpp index 170effe9614..3ec6e89d9a8 100644 --- a/runtime/compiler/net/MessageTypes.hpp +++ b/runtime/compiler/net/MessageTypes.hpp @@ -188,7 +188,7 @@ enum MessageType : uint16_t VM_targetMethodFromInvokeCacheArrayMemberNameObj, VM_refineInvokeCacheElementSymRefWithKnownObjectIndex, VM_isLambdaFormGeneratedMethod, - VM_vTableOrITableIndexFromMemberName, + VM_getMemberNameMethodInfo, VM_getMemberNameFieldKnotIndexFromMethodHandleKnotIndex, VM_isMethodHandleExpectedType, VM_isStable, diff --git a/runtime/compiler/optimizer/InterpreterEmulator.cpp b/runtime/compiler/optimizer/InterpreterEmulator.cpp index 208758dfb2a..724e8f478b2 100644 --- a/runtime/compiler/optimizer/InterpreterEmulator.cpp +++ b/runtime/compiler/optimizer/InterpreterEmulator.cpp @@ -1031,8 +1031,15 @@ InterpreterEmulator::getReturnValue(TR_ResolvedMethod *callee) } void -InterpreterEmulator::refineResolvedCalleeForInvokestatic(TR_ResolvedMethod *&callee, TR::KnownObjectTable::Index & mcsIndex, TR::KnownObjectTable::Index & mhIndex, bool &isIndirectCall) +InterpreterEmulator::refineResolvedCalleeForInvokestatic( + TR_ResolvedMethod *&callee, + TR::KnownObjectTable::Index & mcsIndex, + TR::KnownObjectTable::Index & mhIndex, + bool &isIndirectCall, + TR_OpaqueClassBlock *&receiverClass) { + receiverClass = NULL; + TR_ASSERT_FATAL(_iteratorWithState, "has to be called when the iterator has state!"); if (!comp()->getOrCreateKnownObjectTable()) return; @@ -1098,36 +1105,26 @@ InterpreterEmulator::refineResolvedCalleeForInvokestatic(TR_ResolvedMethod *&cal { TR::KnownObjectTable::Index memberNameIndex = top()->getKnownObjectIndex(); TR_J9VMBase* fej9 = comp()->fej9(); - auto targetMethod = fej9->targetMethodFromMemberName(comp(), memberNameIndex); - if (!targetMethod) + TR_J9VMBase::MemberNameMethodInfo info = {}; + if (!fej9->getMemberNameMethodInfo(comp(), memberNameIndex, &info)) + return; + + if (info.vmtarget == NULL) return; uint32_t vTableSlot = 0; if (rm == TR::java_lang_invoke_MethodHandle_linkToVirtual) { - uintptr_t slot = fej9->vTableOrITableIndexFromMemberName(comp(), memberNameIndex); - if ((slot & J9_JNI_MID_INTERFACE) == 0) - { - vTableSlot = (uint32_t)slot; - } - else - { - // TODO: Refine to the method identified by the itable slot - // together with the defining (interface) class of the method - // from the MemberName. For such a refinement to matter, - // TR_J9InterfaceCallSite will need to be able to find call - // targets without a CP index. - // - // For the moment, just leave the call unrefined. - // + if (info.refKind != MH_REF_INVOKEVIRTUAL) return; - } + + vTableSlot = info.vmindex; } // Direct or virtual dispatch. A vTableSlot of 0 indicates a direct // call, in which case vTableSlot won't really be used as such. - callee = fej9->createResolvedMethodWithVTableSlot(comp()->trMemory(), vTableSlot, targetMethod, _calltarget->_calleeMethod); - + callee = fej9->createResolvedMethodWithVTableSlot(comp()->trMemory(), vTableSlot, info.vmtarget, _calltarget->_calleeMethod); + receiverClass = info.clazz; isIndirectCall = vTableSlot != 0; TR_ASSERT_FATAL( !isIndirectCall @@ -1602,9 +1599,20 @@ InterpreterEmulator::visitInvokestatic() TR::KnownObjectTable::Index mhIndex = TR::KnownObjectTable::UNKNOWN; TR::KnownObjectTable::Index mcsIndex = TR::KnownObjectTable::UNKNOWN; + TR_OpaqueClassBlock *receiverClass = NULL; bool isIndirectCall = false; if (_iteratorWithState) - refineResolvedCalleeForInvokestatic(_currentCallMethod, mcsIndex, mhIndex, isIndirectCall); + { + refineResolvedCalleeForInvokestatic( + _currentCallMethod, + mcsIndex, + mhIndex, + isIndirectCall, + receiverClass); + } + + if (receiverClass == NULL) + receiverClass = _currentCallMethod->classOfMethod(); bool isInterface = false; TR_CallSite *callsite = NULL; @@ -1619,7 +1627,7 @@ InterpreterEmulator::visitInvokestatic() mcsIndex == TR::KnownObjectTable::UNKNOWN) { callsite = new (comp()->trHeapMemory()) TR_J9MethodHandleCallSite( _calltarget->_calleeMethod, callNodeTreeTop, parent, - callNode, interfaceMethod, _currentCallMethod->classOfMethod(), + callNode, interfaceMethod, receiverClass, -1, cpIndex, _currentCallMethod, resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, comp(), _recursionDepth, allconsts); @@ -1629,7 +1637,7 @@ InterpreterEmulator::visitInvokestatic() mcsIndex != TR::KnownObjectTable::UNKNOWN) { TR_J9MutableCallSite *mcs = new (comp()->trHeapMemory()) TR_J9MutableCallSite( _calltarget->_calleeMethod, callNodeTreeTop, parent, - callNode, interfaceMethod, _currentCallMethod->classOfMethod(), + callNode, interfaceMethod, receiverClass, (int32_t) _currentCallMethod->virtualCallSelector(cpIndex), cpIndex, _currentCallMethod, resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, comp(), _recursionDepth, allconsts); @@ -1645,14 +1653,14 @@ InterpreterEmulator::visitInvokestatic() int32_t noCPIndex = -1; // The method is not referenced via the constant pool callsite = new (comp()->trHeapMemory()) TR_J9VirtualCallSite( _calltarget->_calleeMethod, callNodeTreeTop, parent, callNode, - interfaceMethod, _currentCallMethod->classOfMethod(), (int32_t) _currentCallMethod->virtualCallSelector(cpIndex), noCPIndex, + interfaceMethod, receiverClass, (int32_t) _currentCallMethod->virtualCallSelector(cpIndex), noCPIndex, _currentCallMethod, resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, comp(), _recursionDepth, allconsts); } else { callsite = new (comp()->trHeapMemory()) TR_DirectCallSite(_calltarget->_calleeMethod, callNodeTreeTop, parent, callNode, interfaceMethod, - _currentCallMethod->classOfMethod(), -1, cpIndex, _currentCallMethod, resolvedSymbol, + receiverClass, -1, cpIndex, _currentCallMethod, resolvedSymbol, isIndirectCall, isInterface, *_newBCInfo, comp(), _recursionDepth, allconsts); } diff --git a/runtime/compiler/optimizer/InterpreterEmulator.hpp b/runtime/compiler/optimizer/InterpreterEmulator.hpp index 29eca8efa6c..c221cd678bf 100644 --- a/runtime/compiler/optimizer/InterpreterEmulator.hpp +++ b/runtime/compiler/optimizer/InterpreterEmulator.hpp @@ -344,7 +344,12 @@ class InterpreterEmulator : public TR_ByteCodeIteratorWithStateil.opCodeForIndirectLoad(TR::Address), 1, lambdaFormNode, memberNameSymRef); // load from membername.vmTarget, which is the J9Method - TR::SymbolReference * vmTargetSymRef = comp()->getSymRefTab()->findOrFabricateShadowSymbol(comp()->getMethodSymbol(), - TR::Symbol::Java_lang_invoke_MemberName_vmtarget, - TR::Address, - fej9->getVMTargetOffset(), - false, - false, - true, - "java/lang/invoke/MemberName.vmtarget J"); - vmTargetSymRef->getSymbol()->setNotCollected(); + TR::SymbolReference * vmTargetSymRef = comp()->getSymRefTab()->findOrFabricateMemberNameVmTargetShadow(); TR::Node * vmTargetNode = TR::Node::createWithSymRef(node, TR::aloadi, 1, memberNameNode, vmTargetSymRef); processVMInternalNativeFunction(treetop, node, vmTargetNode, argsList, inlCallNode); } @@ -826,15 +818,7 @@ void J9::RecognizedCallTransformer::process_java_lang_invoke_MethodHandle_linkTo } // load from membername.vmTarget, which is the J9Method TR::Node* mnNode = TR::Node::createLoad(node, argsList->back()); - TR::SymbolReference * vmTargetSymRef = comp()->getSymRefTab()->findOrFabricateShadowSymbol(comp()->getMethodSymbol(), - TR::Symbol::Java_lang_invoke_MemberName_vmtarget, - TR::Address, - fej9->getVMTargetOffset(), - false, - false, - true, - "java/lang/invoke/MemberName.vmtarget J"); - vmTargetSymRef->getSymbol()->setNotCollected(); + TR::SymbolReference * vmTargetSymRef = comp()->getSymRefTab()->findOrFabricateMemberNameVmTargetShadow(); TR::Node * vmTargetNode = TR::Node::createWithSymRef(node, TR::aloadi, 1, mnNode, vmTargetSymRef); vmTargetNode->setIsNonNull(true); argsList->pop_back(); // MemberName is not required when dispatching directly to the jitted method address @@ -904,7 +888,7 @@ void J9::RecognizedCallTransformer::processVMInternalNativeFunction(TR::TreeTop* TR::TreeTop * computedCallTreeTop = TR::TreeTop::create(comp(), TR::Node::create(node, TR::treetop, 1, computedCallNode)); TR::RecognizedMethod rm = node->getSymbol()->castToMethodSymbol()->getMandatoryRecognizedMethod(); TR::Node *nullChkNode = NULL; - if (rm == TR::java_lang_invoke_MethodHandle_linkToSpecial || rm == TR::java_lang_invoke_MethodHandle_linkToVirtual) + if (rm == TR::java_lang_invoke_MethodHandle_linkToSpecial) { TR::Node *passthroughNode = TR::Node::create(node, TR::PassThrough, 1); passthroughNode->setAndIncChild(0, TR::Node::createLoad(node, argsList->front())); @@ -920,256 +904,159 @@ void J9::RecognizedCallTransformer::processVMInternalNativeFunction(TR::TreeTop* void J9::RecognizedCallTransformer::process_java_lang_invoke_MethodHandle_linkToVirtual(TR::TreeTop * treetop, TR::Node * node) { - TR_J9VMBase* fej9 = static_cast(comp()->fe()); - TR::Node * placeholderINLCallNode = node->duplicateTree(false); // this will be eventually removed once the second diamond is created - TR::Node* duplicatedINLCallNode = node->duplicateTree(false); - TR::list* argsList = new (comp()->trStackMemory()) TR::list(getTypedAllocator(comp()->allocator())); - for (int i = 0; i < node->getNumChildren(); i++) - { - TR::Node * currentChild = node->getChild(i); - TR::SymbolReference *newSymbolReference = comp()->getSymRefTab()->createTemporary(comp()->getMethodSymbol(), currentChild->getDataType()); - newSymbolReference->getSymbol()->setNotCollected(); - argsList->push_back(newSymbolReference); - TR::Node * storeNode = TR::Node::createStore(node, newSymbolReference, currentChild); - treetop->insertBefore(TR::TreeTop::create(comp(),storeNode)); - placeholderINLCallNode->setAndIncChild(i, TR::Node::createLoad(node, newSymbolReference)); - duplicatedINLCallNode->setAndIncChild(i, TR::Node::createLoad(node, newSymbolReference)); - currentChild->recursivelyDecReferenceCount(); - currentChild->recursivelyDecReferenceCount(); // ref count has to be decremented twice as there were two duplicates of the parent made - } + TR::Node *receiver = node->getChild(0); + TR::Node *memberNameNode = node->getChild(node->getNumChildren() - 1); - TR::SymbolReference *receiverSymRef = argsList->front(); - TR::SymbolReference *mnSymRef = argsList->back(); - - // -------- construct trees to obtain and check if vTableIndex > 0 -------- - // get JIT Vtable index from membername.vmindex - TR::SymbolReference* vmindexSymRef = comp()->getSymRefTab()->findOrFabricateShadowSymbol(comp()->getMethodSymbol(), - TR::Symbol::Java_lang_invoke_MemberName_vmindex, - TR::Address, - fej9->getVMIndexOffset(), - false, - false, - true, - "java/lang/invoke/MemberName.vmindex J"); - vmindexSymRef->getSymbol()->setNotCollected(); - - // construct trees to check if vtable index is zero (NOT MemberName.vmindex, but MN.vmindex.vtableindex). if zero, then call will be dispatched similar to linkToStatic. - TR::Node * j9JNIMethodIdNode = TR::Node::createWithSymRef(node, TR::aloadi, 1, TR::Node::createLoad(node, mnSymRef), vmindexSymRef); - TR::SymbolReference* vtableIndexSymRef = comp()->getSymRefTab()->findOrCreateJ9JNIMethodIDvTableIndexFieldSymbol(offsetof(struct J9JNIMethodID, vTableIndex)); - vtableIndexSymRef->getSymbol()->setNotCollected(); - TR::Node* vtableIndexNode = TR::Node::createWithSymRef(node, comp()->il.opCodeForIndirectLoad(vtableIndexSymRef->getSymbol()->getDataType()), 1, j9JNIMethodIdNode , vtableIndexSymRef); - TR::SymbolReference* vtableOffsetTempSlotSymRef = comp()->getSymRefTab()->createTemporary(comp()->getMethodSymbol(),vtableIndexNode->getSymbol()->getDataType()); - vtableOffsetTempSlotSymRef->getSymbol()->setNotCollected(); - treetop->insertBefore(TR::TreeTop::create(comp(), TR::Node::createStore(node, vtableOffsetTempSlotSymRef, vtableIndexNode))); - TR::ILOpCodes ifxcmpne = comp()->target().is64Bit()? TR::iflcmpne : TR::ificmpne; - TR::Node *zero = comp()->target().is64Bit()? TR::Node::lconst(node, 0) : TR::Node::iconst(node, 0); + TR::SymbolReference *vftSymRef = comp()->getSymRefTab()->findOrCreateVftSymbolRef(); + TR::Node *vftNode = + TR::Node::createWithSymRef(node, TR::aloadi, 1, receiver, vftSymRef); + + // Null check the receiver + + TR::SymbolReference *nullCheckSymRef = + comp()->getSymRefTab()->findOrCreateNullCheckSymbolRef( + comp()->getMethodSymbol()); + + TR::Node *nullCheck = TR::Node::createWithSymRef( + node, TR::NULLCHK, 1, vftNode, nullCheckSymRef); + + treetop->insertBefore(TR::TreeTop::create(comp(), nullCheck)); + + // Get the VFT offset from MemberName.vmindex + TR::SymbolReference* vmIndexSymRef = comp()->getSymRefTab()->findOrFabricateShadowSymbol( + comp()->getMethodSymbol(), + TR::Symbol::Java_lang_invoke_MemberName_vmindex, + TR::Int64, + comp()->fej9()->getVMIndexOffset(), + false, + false, + true, + "java/lang/invoke/MemberName.vmindex J"); + + TR::Node *vftOffset = + TR::Node::createWithSymRef(node, TR::aloadi, 1, memberNameNode, vmIndexSymRef); + + if (!comp()->target().is64Bit()) + vftOffset = TR::Node::create(node, TR::l2i, 1, vftOffset); - TR::Node* vTableOffsetIsNotZero = TR::Node::createif(ifxcmpne, - TR::Node::createLoad(node, vtableOffsetTempSlotSymRef), - zero, - NULL); - TR::TreeTop* vtableOffsetIsNotZeroTreeTop = TR::TreeTop::create(comp(), vTableOffsetIsNotZero); + makeIntoDispatchVirtualCall(node, vftOffset, vftNode, memberNameNode); + } - // -------- construct trees for path to take when vtable index > 0 -------- +void J9::RecognizedCallTransformer::makeIntoDispatchVirtualCall( + TR::Node *node, TR::Node *vftOffset, TR::Node *vftNode, TR::Node *memberNameNode) + { + // Construct a dummy "resolved method" for JITHelpers.dispatchVirtual()V to + // dispatch through the VFT. + TR_J9VMBase *fej9 = comp()->fej9(); + TR_OpaqueMethodBlock *dispatchVirtual = + fej9->getMethodFromName("com/ibm/jit/JITHelpers", "dispatchVirtual", "()V"); - // construct a dummy "resolved method" for dispatch virtual: - TR_OpaqueMethodBlock *dummyInvoke = fej9->getMethodFromName("com/ibm/jit/JITHelpers", "dispatchVirtual", "()V"); int signatureLength; - char * signature = getSignatureForComputedCall( + char *signature = getSignatureForComputedCall( "JJ", "", comp(), node->getSymbol()->castToMethodSymbol()->getMethod()->signatureChars(), signatureLength); - TR::ResolvedMethodSymbol * owningMethodSymbol = node->getSymbolReference()->getOwningMethodSymbol(comp()); - TR_ResolvedMethod * dummyMethod = fej9->createResolvedMethodWithSignature(comp()->trMemory(),dummyInvoke, NULL, signature, signatureLength, owningMethodSymbol->getResolvedMethod()); - TR::SymbolReference * methodSymRef = comp()->getSymRefTab()->findOrCreateMethodSymbol(owningMethodSymbol->getResolvedMethodIndex(), -1, dummyMethod, TR::MethodSymbol::ComputedStatic); - // construct trees to load target method address (which will either be a thunk or compiled method address) from VFT, but for simplicity let's call it a jitted method entry point - TR::Node * xconstSizeofJ9Class = comp()->target().is64Bit() + TR::ResolvedMethodSymbol *owningMethodSymbol = + node->getSymbolReference()->getOwningMethodSymbol(comp()); + + TR_ResolvedMethod *dispatchVirtualResolvedMethod = + fej9->createResolvedMethodWithSignature( + comp()->trMemory(), + dispatchVirtual, + NULL, + signature, + signatureLength, + owningMethodSymbol->getResolvedMethod()); + + TR::SymbolReference * dispatchVirtualSymRef = + comp()->getSymRefTab()->findOrCreateMethodSymbol( + owningMethodSymbol->getResolvedMethodIndex(), + -1, + dispatchVirtualResolvedMethod, + TR::MethodSymbol::ComputedStatic); + + TR::ILOpCodes indirectCallOp = + node->getSymbol()->castToMethodSymbol()->getMethod()->indirectCallOpCode(); + + TR::Node::recreateWithSymRef(node, indirectCallOp, dispatchVirtualSymRef); + + // 2 extra args prepended (address in vtable entry and vtable slot index), + // and last arg (MemberName object) removed, so net 1 extra child. + TR::Node *dummyExtraChild = NULL; + node->addChildren(&dummyExtraChild, 1); + for (int32_t i = node->getNumChildren() - 1; i >= 2; i--) + node->setChild(i, node->getChild(i - 2)); + + TR::Node *xconstSizeofJ9Class = comp()->target().is64Bit() ? TR::Node::lconst(node, sizeof(J9Class)) : TR::Node::iconst(node, sizeof(J9Class)); TR::ILOpCodes subOp = comp()->target().is64Bit() ? TR::lsub : TR::isub; TR::ILOpCodes axadd = comp()->target().is64Bit() ? TR::aladd : TR::aiadd; - TR::Node* jitVFTOffset = TR::Node::create(subOp, 2, xconstSizeofJ9Class, TR::Node::createLoad(node, vtableOffsetTempSlotSymRef)); - TR::SymbolReference * vftSymRef = comp()->getSymRefTab()->findOrCreateVftSymbolRef(); - TR::Node * vftNode = TR::Node::createWithSymRef(node, TR::aloadi, 1, TR::Node::createLoad(node, receiverSymRef), vftSymRef); - TR::Node * jitVFTSlotPtr = TR::Node::create(axadd, 2, vftNode, jitVFTOffset); - TR::SymbolReference *tSymRef = comp()->getSymRefTab()->findOrCreateGenericIntShadowSymbolReference(0); - tSymRef->getSymbol()->setNotCollected(); - TR::ILOpCodes loadOp = comp()->target().is64Bit() ? TR::lloadi : TR::iloadi; - // arg0 ---> jitted method address (in JIT vtable) - TR::Node * jittedMethodEntryPoint = TR::Node::createWithSymRef(loadOp, 1, 1, jitVFTSlotPtr, tSymRef); - - // arg1 ---> vtable slot index (jitVFTOffset) - - // 2 additional args prepended (address in vtable entry and vtable slot index), and last arg (MemberName object) removed, so net 1 extra child - uint32_t numChildren = node->getNumChildren() + 1; - TR::Node * dispatchVirtualCallNode = TR::Node::createWithSymRef(node, node->getSymbol()->castToMethodSymbol()->getMethod()->indirectCallOpCode(), numChildren, methodSymRef); - - // set children for the dispatchVirtual call node - dispatchVirtualCallNode->setAndIncChild(0, jittedMethodEntryPoint); - dispatchVirtualCallNode->setAndIncChild(1, jitVFTOffset); - argsList->pop_back(); //remove MemberName object - int32_t child_i = 2; - for (auto symRefIt = argsList->begin(); symRefIt != argsList->end(); ++symRefIt) - { - TR::SymbolReference * currentArg = *symRefIt; - dispatchVirtualCallNode->setAndIncChild(child_i, TR::Node::createLoad(node, currentArg)); - child_i++; - } - TR::TreeTop * dispatchVirtualTreeTop = TR::TreeTop::create(comp(), TR::Node::create(node, TR::treetop, 1, dispatchVirtualCallNode)); - TR::TreeTop * placeholderINLCallTreeTop = TR::TreeTop::create(comp(), TR::Node::create(node, TR::treetop, 1, placeholderINLCallNode)); + TR::SymbolReference *genericIntShadow = + comp()->getSymRefTab()->createGenericIntShadowSymbolReference(0); - TR::Node *passthroughNode = TR::Node::create(node, TR::PassThrough, 1); - passthroughNode->setAndIncChild(0, TR::Node::createLoad(node, argsList->front())); - TR::SymbolReference *nullSymRef = comp()->getSymRefTab()->findOrCreateNullCheckSymbolRef(comp()->getMethodSymbol()); - TR::Node* nullChkNode = TR::Node::createWithSymRef(TR::NULLCHK, 1, 1, passthroughNode, nullSymRef); + genericIntShadow->getSymbol()->setNotCollected(); - node->removeAllChildren(); - TR::TransformUtil::createDiamondForCall(this, treetop, vtableOffsetIsNotZeroTreeTop, dispatchVirtualTreeTop, placeholderINLCallTreeTop, false, false); - TR::TreeTop *virtualNullCheckTreeTop = TR::TreeTop::create(comp(), nullChkNode); - dispatchVirtualTreeTop->insertBefore(virtualNullCheckTreeTop); + TR::Node *jitVftOffset = TR::Node::create(subOp, 2, xconstSizeofJ9Class, vftOffset); + TR::Node *jitVftSlotPtr = TR::Node::create(axadd, 2, vftNode, jitVftOffset); - // Insert a conditional at the beginning of the virtual call path to - // determine the VFT offset for interface methods. - TR::Block *virtualNullCheckBlock = virtualNullCheckTreeTop->getEnclosingBlock(); - TR::CFG *cfg = comp()->getFlowGraph(); - TR::Block *itableLookupBlock = virtualNullCheckBlock->split(dispatchVirtualTreeTop, cfg, true); - TR::Block *virtualDispatchBlock = itableLookupBlock->split(dispatchVirtualTreeTop, cfg, true); - - TR::ILOpCodes andOp = comp()->target().is64Bit() ? TR::land : TR::iand; - TR::Node * xconstITableFlagBit = comp()->target().is64Bit() - ? TR::Node::lconst(node, J9_JNI_MID_INTERFACE) - : TR::Node::iconst(node, (int32_t)J9_JNI_MID_INTERFACE); - - TR::ILOpCodes ifxcmpeq = TR::ILOpCode(ifxcmpne).getOpCodeForReverseBranch(); - // The offset is a VFT offset iff it is not an itable index - TR::Node * ifIsVFTOffset = TR::Node::createif( - ifxcmpeq, - TR::Node::create( - node, - andOp, - 2, - TR::Node::createLoad(node, vtableOffsetTempSlotSymRef), - xconstITableFlagBit), - zero->duplicateTree(), // safe because zero is a constant - virtualDispatchBlock->getEntry()); - - virtualNullCheckBlock->append(TR::TreeTop::create(comp(), ifIsVFTOffset)); - cfg->addEdge(virtualNullCheckBlock, virtualDispatchBlock); - - // TODO: For linkToInterface() we should instead use - // findOrCreateLookupDynamicPublicInterfaceMethodSymbolRef(), which will - // throw IllegalAccessError when the dispatched method is non-public. - TR::Node *vftOffsetFromITable = TR::Node::createWithSymRef( + TR::ILOpCodes vftEntryLoadOp = comp()->target().is64Bit() ? TR::lloadi : TR::iloadi; + TR::Node *jittedMethodEntryPoint = + TR::Node::createWithSymRef(vftEntryLoadOp, 1, 1, jitVftSlotPtr, genericIntShadow); + + node->setAndIncChild(0, jittedMethodEntryPoint); + node->setAndIncChild(1, jitVftOffset); + + memberNameNode->decReferenceCount(); // no longer a child of node + } + +void J9::RecognizedCallTransformer::process_java_lang_invoke_MethodHandle_linkToInterface(TR::TreeTop *treetop, TR::Node *node) + { + TR::Node *receiverNode = node->getChild(0); + TR::Node *memberNameNode = node->getChild(node->getNumChildren() - 1); + + // NOTE: There's no need for a null check, since null won't get past the + // type check in DirectMethodHandle$Interface.checkReceiver() anyway. + + // Call a VM helper to do the interface dispatch. It finds the declaring + // interface and the iTable index from the MemberName. The helper throws + // IllegalAccessError when the found method is non-public. It won't throw + // ICCE because the bytecode has already done a type check on the receiver, + // so we'll always find an itable. + + // This produces a VFT offset (to the interpreter VFT slot from the start of + // the J9Class) that we can use to call through the vtable. + + TR::SymbolReference *lookupDynamicInterfaceMethod = + comp()->getSymRefTab()->findOrCreateLookupDynamicPublicInterfaceMethodSymbolRef(); + + TR::Node *vftOffset = TR::Node::createWithSymRef( node, comp()->target().is64Bit() ? TR::lcall : TR::icall, - 3, - comp()->getSymRefTab()->findOrCreateLookupDynamicInterfaceMethodSymbolRef()); - - // The first argument to the itable lookup helper is the receiver class. - // It's safe to duplicate vftNode here because it is just a load of - // from a load of a temp, and the temp has been created - // specifically to hold the receiver reference - there are no other defs. - // - // NOTE: This child is actually last because helper arguments are passed in - // reverse order. - // - vftOffsetFromITable->setAndIncChild(2, vftNode->duplicateTree()); - - // The next argument to the itable lookup helper is the interface class. - const bool vmtargetIsVolatile = false; - const bool vmtargetIsPrivate = false; // could be true? It's a hidden field - const bool vmtargetIsFinal = true; - TR::SymbolReference * vmtargetSymRef = - comp()->getSymRefTab()->findOrFabricateShadowSymbol( - comp()->getMethodSymbol(), - TR::Symbol::Java_lang_invoke_MemberName_vmtarget, - TR::Address, - fej9->getVMTargetOffset(), - vmtargetIsVolatile, - vmtargetIsPrivate, - vmtargetIsFinal, - "java/lang/invoke/MemberName.vmtarget J"); - - vmtargetSymRef->getSymbol()->setNotCollected(); - - TR::Node * vmTargetNode = TR::Node::createWithSymRef( - node, TR::aloadi, 1, TR::Node::createLoad(node, mnSymRef), vmtargetSymRef); - - TR::SymbolReference *cpField = - comp()->getSymRefTab()->findOrCreateJ9MethodConstantPoolFieldSymbolRef( - offsetof(struct J9Method, constantPool)); - - TR::Node *cpAddrIntWithFlags = TR::Node::createWithSymRef( - node, loadOp, 1, vmTargetNode, cpField); - - TR::Node *cpAddrMask = comp()->target().is64Bit() - ? TR::Node::lconst(~(int64_t)J9_STARTPC_STATUS) - : TR::Node::iconst(~(int32_t)J9_STARTPC_STATUS); - - TR::Node *cpAddrInt = TR::Node::create( - node, andOp, 2, cpAddrIntWithFlags, cpAddrMask); - - TR::ILOpCodes x2a = comp()->target().is64Bit() ? TR::l2a : TR::i2a; - TR::Node *cpAddr = TR::Node::create(node, x2a, 1, cpAddrInt); - - TR::Node *ramClassFieldOffset = comp()->target().is64Bit() - ? TR::Node::lconst(offsetof (struct J9ConstantPool, ramClass)) - : TR::Node::iconst(offsetof (struct J9ConstantPool, ramClass)); - - TR::Node *ramClassFieldAddr = TR::Node::create( - node, axadd, 2, cpAddr, ramClassFieldOffset); - - TR::Node *interfaceClassInt = TR::Node::createWithSymRef( - node, loadOp, 1, ramClassFieldAddr, tSymRef); - - TR::Node *interfaceClass = TR::Node::create(node, x2a, 1, interfaceClassInt); - vftOffsetFromITable->setAndIncChild(1, interfaceClass); - - // The final argument to the itable lookup helper is the itable index. - // NOTE: This child is actually first because helper arguments are passed in - // reverse order. - TR::Node *xconstClearITableFlagMask = comp()->target().is64Bit() - ? TR::Node::lconst(node, ~(int64_t)J9_JNI_MID_INTERFACE) - : TR::Node::iconst(node, ~(int32_t)J9_JNI_MID_INTERFACE); - - // Duplicate xconstITableFlagBit to prevent commoning between blocks. The - // duplication is safe because it's a constant. - xconstITableFlagBit = xconstITableFlagBit->duplicateTree(); - TR::Node *itableIndex = TR::Node::create( - andOp, 2, - TR::Node::createLoad(node, vtableOffsetTempSlotSymRef), - xconstClearITableFlagMask); + lookupDynamicInterfaceMethod); - vftOffsetFromITable->setAndIncChild(0, itableIndex); + // The helper takes (receiver J9Class, MemberName reference), but helper + // arguments are passed in reverse order. + vftOffset->setAndIncChild(0, memberNameNode); - itableLookupBlock->append( - TR::TreeTop::create( - comp(), - TR::Node::create(node, TR::treetop, 1, vftOffsetFromITable))); + TR::SymbolReference *vftSymRef = comp()->getSymRefTab()->findOrCreateVftSymbolRef(); + TR::Node *vftNode = + TR::Node::createWithSymRef(node, TR::aloadi, 1, receiverNode, vftSymRef); - itableLookupBlock->append( + vftOffset->setAndIncChild(1, vftNode); + + treetop->insertBefore( TR::TreeTop::create( comp(), - TR::Node::createStore( - node, vtableOffsetTempSlotSymRef, vftOffsetFromITable))); - - // -------- path if vmIndex == 0 -------- - // now let's work with just the placeholder INL call as the main node, as if this is a linkToStatic/Special call. the original treetop was removed. - treetop = placeholderINLCallTreeTop; - node = treetop->getNode()->getFirstChild(); + TR::Node::create(node, TR::treetop, 1, vftOffset))); - // It's safe to duplicate vmTargetNode because it's just a load of vmtarget - // from a load of a temp, and the temp has been created specifically to hold - // the MemberName reference - there are no other defs - vmTargetNode = vmTargetNode->duplicateTree(); - - processVMInternalNativeFunction(treetop, node, vmTargetNode, argsList, duplicatedINLCallNode); + makeIntoDispatchVirtualCall(node, vftOffset, vftNode, memberNameNode); } #endif // J9VM_OPT_OPENJDK_METHODHANDLE @@ -1266,10 +1153,8 @@ bool J9::RecognizedCallTransformer::isInlineable(TR::TreeTop* treetop) else return true; case TR::java_lang_invoke_MethodHandle_linkToVirtual: - if (_processedINLCalls->get(node->getGlobalIndex())) - return false; - else - return true; + case TR::java_lang_invoke_MethodHandle_linkToInterface: + return true; default: return false; } @@ -1396,6 +1281,9 @@ void J9::RecognizedCallTransformer::transform(TR::TreeTop* treetop) case TR::java_lang_invoke_MethodHandle_linkToVirtual: process_java_lang_invoke_MethodHandle_linkToVirtual(treetop, node); break; + case TR::java_lang_invoke_MethodHandle_linkToInterface: + process_java_lang_invoke_MethodHandle_linkToInterface(treetop, node); + break; default: break; } diff --git a/runtime/compiler/optimizer/J9RecognizedCallTransformer.hpp b/runtime/compiler/optimizer/J9RecognizedCallTransformer.hpp index d991f3158cc..6bd94a7fa42 100644 --- a/runtime/compiler/optimizer/J9RecognizedCallTransformer.hpp +++ b/runtime/compiler/optimizer/J9RecognizedCallTransformer.hpp @@ -217,6 +217,47 @@ class RecognizedCallTransformer : public OMR::RecognizedCallTransformer * the call node representing the linkToVirtual call */ void process_java_lang_invoke_MethodHandle_linkToVirtual(TR::TreeTop * treetop, TR::Node * node); + + /** \brief + * Transforms java/lang/MethodHandle.linkToInterface when the MemberName object (last arg) is not a known object. + * + * linkToInterface is a VM INL call that would construct the call frame for the target method invocation. + * This would be the case even if the method to be invoked is compiled, resulting in j2i and i2j transitions. + * This transformation creates an interface dispatch to find the vtable offset and then uses it to do a computed + * virtual call (represented by JITHelpers.dispatchVirtual). + * + * linkToInterface only needs to handle regular interface dispatch. Directly-dispatched methods (e.g. private + * interface instance methods, final methods of Object) will be handled by linkToSpecial(). Non-interface + * virtual methods (e.g. non-final methods of Object) will be handled by linkToVirtual(). + * + * \param treetop + * the TreeTop anchoring the call node + * + * \param node + * the call node representing the linkToInterface call + */ + void process_java_lang_invoke_MethodHandle_linkToInterface(TR::TreeTop * treetop, TR::Node * node); + + /** \brief + * Transforms \p node into a call to \c JITHelpers.dispatchVirtual(), calling the method whose (interpreter) + * vTable offset is the result \p vftOffset. + * + * Additional parameters beyond \p node and \p vftOffset are not strictly necessary, but callers have already + * either identified or created nodes to compute them anyway. + * + * \param node + * the linkToVirtual() or linkToInterface() call node to modify + * + * \param vftOffset + * the (interpreter) VFT offset to use for the call + * + * \param vftNode + * the vTable of the receiver + * + * \param memberNameNode + * the MemberName (last argument) + */ + void makeIntoDispatchVirtualCall(TR::Node *node, TR::Node *vftOffset, TR::Node *vftNode, TR::Node *memberNameNode); #endif private: diff --git a/runtime/compiler/optimizer/J9TransformUtil.cpp b/runtime/compiler/optimizer/J9TransformUtil.cpp index 15cac08c994..a7c6abc1f2d 100644 --- a/runtime/compiler/optimizer/J9TransformUtil.cpp +++ b/runtime/compiler/optimizer/J9TransformUtil.cpp @@ -33,6 +33,7 @@ #include "il/Node_inlines.hpp" #include "infra/Assert.hpp" #include "infra/Cfg.hpp" +#include "infra/String.hpp" #include "il/StaticSymbol.hpp" #include "il/StaticSymbol_inlines.hpp" #include "il/Symbol.hpp" @@ -2322,15 +2323,21 @@ J9::TransformUtil::refineMethodHandleLinkTo(TR::Compilation* comp, TR::TreeTop* auto symRef = node->getSymbolReference(); auto rm = node->getSymbol()->castToMethodSymbol()->getMandatoryRecognizedMethod(); const char *missingResolvedDispatch = NULL; + const char *whichLinkTo = NULL; switch (rm) { case TR::java_lang_invoke_MethodHandle_linkToStatic: + whichLinkTo = "Static"; + // fall through case TR::java_lang_invoke_MethodHandle_linkToSpecial: + if (rm == TR::java_lang_invoke_MethodHandle_linkToSpecial) + whichLinkTo = "Special"; if (!fej9->isResolvedDirectDispatchGuaranteed(comp)) missingResolvedDispatch = "Direct"; break; case TR::java_lang_invoke_MethodHandle_linkToVirtual: + whichLinkTo = "Virtual"; if (!fej9->isResolvedVirtualDispatchGuaranteed(comp)) missingResolvedDispatch = "Virtual"; break; @@ -2339,15 +2346,23 @@ J9::TransformUtil::refineMethodHandleLinkTo(TR::Compilation* comp, TR::TreeTop* TR_ASSERT_FATAL(false, "Unsupported method %s", symRef->getSymbol()->getResolvedMethodSymbol()->getResolvedMethod()->signature(comp->trMemory())); } + char nodeStr[64]; + TR::snprintfNoTrunc( + nodeStr, + sizeof(nodeStr), + "linkTo%s n%un [%p]", + whichLinkTo, + node->getGlobalIndex(), + node); + if (missingResolvedDispatch != NULL) { if (trace) { traceMsg( comp, - "Cannot refine linkToXXX n%un %p without isResolved%sDispatchGuaranteed()\n", - node->getGlobalIndex(), - node, + "Cannot refine %s without isResolved%sDispatchGuaranteed()\n", + nodeStr, missingResolvedDispatch); } return false; @@ -2359,13 +2374,19 @@ J9::TransformUtil::refineMethodHandleLinkTo(TR::Compilation* comp, TR::TreeTop* knot->isNull(mnIndex)) { if (trace) - traceMsg(comp, "MethodName for linkToXXX n%dn %p is unknown or null\n", node->getGlobalIndex(), node); + traceMsg(comp, "%s: MemberName is unknown or null\n", nodeStr); + return false; } - auto targetMethod = fej9->targetMethodFromMemberName(comp, mnIndex); + TR_J9VMBase::MemberNameMethodInfo info = {}; + if (!fej9->getMemberNameMethodInfo(comp, mnIndex, &info) || info.vmtarget == NULL) + { + if (trace) + traceMsg(comp, "%s: Failed to get MemberName method info\n", nodeStr); - TR_ASSERT(targetMethod, "Can't get target method from MethodName obj%d\n", mnIndex); + return false; + } TR::MethodSymbol::Kinds callKind = getTargetMethodCallKind(rm); TR::ILOpCodes callOpCode = getTargetMethodCallOpCode(rm, node->getDataType()); @@ -2375,24 +2396,17 @@ J9::TransformUtil::refineMethodHandleLinkTo(TR::Compilation* comp, TR::TreeTop* int32_t jitVTableOffset = 0; if (rm == TR::java_lang_invoke_MethodHandle_linkToVirtual) { - uintptr_t slot = fej9->vTableOrITableIndexFromMemberName(comp, mnIndex); - if ((slot & J9_JNI_MID_INTERFACE) != 0) + if (info.refKind != MH_REF_INVOKEVIRTUAL) { - // TODO: Refine this to an interface call to the method identified by - // the itable slot and the method's defining (interface) class from - // the MemberName. - // - // Unfortunately, such a call will not be representable until - // interface calls are allowed to be resolved. As it stands, code - // generation requires a CP index for the dispatch. - // - // For the moment, just leave the call unrefined. - // + if (trace) + traceMsg(comp, "%s: wrong MemberName kind %d\n", nodeStr, info.refKind); + return false; } - vTableSlot = (uint32_t)slot; + vTableSlot = (uint32_t)info.vmindex; jitVTableOffset = fej9->vTableSlotToVirtualCallOffset(vTableSlot); + // For private virtual methods and java/lang/Object; type virtual methods, there is no corresponding // entry in the vtable, and for such methods the interpreter vtable index is 0. // The dispatch is not performed through the vtable entry, but directly dispatched to the J9Method @@ -2400,13 +2414,13 @@ J9::TransformUtil::refineMethodHandleLinkTo(TR::Compilation* comp, TR::TreeTop* // jitVTableOffset is obtained using sizeof(J9Class) - interpreter vtable offset, resulting in a positive // value when the interpreter vtable index we get (from MemberName.vmindex.vtableoffset) is set as 0. if (jitVTableOffset > 0) - callKind = TR::MethodSymbol::Static; + callKind = TR::MethodSymbol::Special; } - if (!performTransformation(comp, "O^O Refine linkToXXX n%dn [%p] with known MemberName object\n", node->getGlobalIndex(), node)) + if (!performTransformation(comp, "O^O Refine %s with known MemberName\n", nodeStr)) return false; - auto resolvedMethod = fej9->createResolvedMethodWithVTableSlot(comp->trMemory(), vTableSlot, targetMethod, symRef->getOwningMethod(comp)); + auto resolvedMethod = fej9->createResolvedMethodWithVTableSlot(comp->trMemory(), vTableSlot, info.vmtarget, symRef->getOwningMethod(comp)); newSymRef = comp->getSymRefTab()->findOrCreateMethodSymbol(symRef->getOwningMethodIndex(), -1, resolvedMethod, callKind); if (callKind == TR::MethodSymbol::Virtual) newSymRef->setOffset(jitVTableOffset); diff --git a/runtime/compiler/runtime/Runtime.cpp b/runtime/compiler/runtime/Runtime.cpp index 75bfcfc4722..b74197a20cd 100644 --- a/runtime/compiler/runtime/Runtime.cpp +++ b/runtime/compiler/runtime/Runtime.cpp @@ -662,8 +662,9 @@ JIT_HELPER(_jitResolveConstantDynamic); JIT_HELPER(_nativeStaticHelper); JIT_HELPER(_interpreterStaticSpecialCallGlue); JIT_HELPER(jitLookupInterfaceMethod); -JIT_HELPER(jitLookupDynamicInterfaceMethod); +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) JIT_HELPER(jitLookupDynamicPublicInterfaceMethod); +#endif JIT_HELPER(jitMethodIsNative); JIT_HELPER(jitMethodIsSync); JIT_HELPER(jitPreJNICallOffloadCheck); @@ -1086,8 +1087,9 @@ void initializeCodeRuntimeHelperTable(J9JITConfig *jitConfig, char isSMP) SET(TR_multiANewArray, (void *)jitAMultiNewArray, TR_Helper); SET(TR_aThrow, (void *)jitThrowException, TR_Helper); - SET(TR_jitLookupDynamicInterfaceMethod, (void *)jitLookupDynamicInterfaceMethod, TR_Helper); +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) SET(TR_jitLookupDynamicPublicInterfaceMethod, (void *)jitLookupDynamicPublicInterfaceMethod, TR_Helper); +#endif SET(TR_nullCheck, (void *)jitThrowNullPointerException, TR_Helper); SET(TR_methodTypeCheck, (void *)jitThrowWrongMethodTypeException, TR_Helper); diff --git a/runtime/compiler/runtime/asmprotos.h b/runtime/compiler/runtime/asmprotos.h index fd314e928b5..4ac3efa017a 100644 --- a/runtime/compiler/runtime/asmprotos.h +++ b/runtime/compiler/runtime/asmprotos.h @@ -77,8 +77,9 @@ JIT_HELPER(jitInduceOSRAtCurrentPCAndRecompile); // asm calling-convention help JIT_HELPER(jitInstanceOf); // asm calling-convention helper JIT_HELPER(jitInterpretNewInstanceMethod); // asm calling-convention helper JIT_HELPER(jitLookupInterfaceMethod); // asm calling-convention helper -JIT_HELPER(jitLookupDynamicInterfaceMethod); // asm calling-convention helper +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) JIT_HELPER(jitLookupDynamicPublicInterfaceMethod); // asm calling-convention helper +#endif JIT_HELPER(jitMethodIsNative); // asm calling-convention helper JIT_HELPER(jitMethodIsSync); // asm calling-convention helper JIT_HELPER(jitMethodMonitorEntry); // asm calling-convention helper diff --git a/runtime/jcl/common/java_lang_invoke_MethodHandleNatives.cpp b/runtime/jcl/common/java_lang_invoke_MethodHandleNatives.cpp index 00c034c4741..884011832cf 100644 --- a/runtime/jcl/common/java_lang_invoke_MethodHandleNatives.cpp +++ b/runtime/jcl/common/java_lang_invoke_MethodHandleNatives.cpp @@ -47,9 +47,6 @@ extern "C" { * method when Java assertions are enabled */ -#define MN_REFERENCE_KIND_SHIFT 24 -#define MN_REFERENCE_KIND_MASK 0xF /* (flag >> MN_REFERENCE_KIND_SHIFT) & MN_REFERENCE_KIND_MASK */ - #define MN_SEARCH_SUPERCLASSES 0x00100000 #define MN_SEARCH_INTERFACES 0x00200000 @@ -94,7 +91,7 @@ isPolymorphicMHMethod(J9JavaVM *vm, J9Class *declaringClass, J9UTF8 *methodName) * set vmindex to the fieldID pointer and target to the field offset. * set MN.clazz to declaring class in the fieldID struct. * For j.l.reflect.Method or j.l.reflect.Constructor: - * find JNIMethodID, set vmindex to the methodID pointer and target to the J9Method struct. + * find JNIMethodID, set target to the J9Method and vmindex as appropriate for dispatch. * set MN.clazz to the refObject's declaring class. * * Then for both, compute the MN.flags using access flags and invocation type based on the JNI-id. @@ -152,7 +149,6 @@ initImpl(J9VMThread *currentThread, j9object_t membernameObject, j9object_t refO clazzObject = J9VM_J9CLASS_TO_HEAPCLASS(fieldID->declaringClass); } else if (refClass == J9VMJAVALANGREFLECTMETHOD(vm)) { J9JNIMethodID *methodID = vm->reflectFunctions.idFromMethodObject(currentThread, refObject); - vmindex = (jlong)methodID; target = (jlong)methodID->method; J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(methodID->method); @@ -186,9 +182,11 @@ initImpl(J9VMThread *currentThread, j9object_t membernameObject, j9object_t refO nameObject = J9VMJAVALANGREFLECTMETHOD_NAME(currentThread, refObject); clazzObject = J9VMJAVALANGREFLECTMETHOD_CLAZZ(currentThread, refObject); + J9Class *clazz = J9VM_J9CLASS_FROM_HEAPCLASS(currentThread, clazzObject); + vmindex = vmindexValueForMethodMemberName(methodID, clazz, flags); } else if (refClass == J9VMJAVALANGREFLECTCONSTRUCTOR(vm)) { J9JNIMethodID *methodID = vm->reflectFunctions.idFromConstructorObject(currentThread, refObject); - vmindex = (jlong)methodID; + vmindex = -1; target = (jlong)methodID->method; J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(methodID->method); @@ -668,14 +666,12 @@ Java_java_lang_invoke_MethodHandleNatives_expand(JNIEnv *env, jclass clazz, jobj vmFuncs->setCurrentExceptionUTF(currentThread, J9VMCONSTANTPOOL_JAVALANGILLEGALARGUMENTEXCEPTION, NULL); } } else if (J9_ARE_ANY_BITS_SET(flags, MN_IS_METHOD | MN_IS_CONSTRUCTOR)) { - if (NULL != (void*)vmindex) { - /* For method/constructor MemberName, the vmindex field is required for expand.*/ - J9JNIMethodID *methodID = (J9JNIMethodID*)vmindex; - J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(methodID->method); - - /* Retrieve method info using JNIMethodID, store to MN fields. */ + J9Method *method = (J9Method *)(UDATA)J9OBJECT_U64_LOAD(_currentThread, membernameObject, vm->vmtargetOffset); + if (NULL != method) { + /* Retrieve method info using the J9Method and store to MN fields. */ + J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method); if (NULL == J9VMJAVALANGINVOKEMEMBERNAME_CLAZZ(currentThread, membernameObject)) { - j9object_t newClassObject = J9VM_J9CLASS_TO_HEAPCLASS(J9_CLASS_FROM_METHOD(methodID->method)); + j9object_t newClassObject = J9VM_J9CLASS_TO_HEAPCLASS(J9_CLASS_FROM_METHOD(method)); J9VMJAVALANGINVOKEMEMBERNAME_SET_CLAZZ(currentThread, membernameObject, newClassObject); } if (NULL == J9VMJAVALANGINVOKEMEMBERNAME_NAME(currentThread, membernameObject)) { @@ -912,7 +908,6 @@ Java_java_lang_invoke_MethodHandleNatives_resolve( } } else if (NULL != method) { J9JNIMethodID *methodID = vmFuncs->getJNIMethodID(currentThread, method); - vmindex = (jlong)(UDATA)methodID; target = (jlong)(UDATA)method; J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(methodID->method); @@ -961,6 +956,9 @@ Java_java_lang_invoke_MethodHandleNatives_resolve( } else { Assert_JCL_unreachable(); } + + J9Class *newJ9Clazz = J9VM_J9CLASS_FROM_HEAPCLASS(currentThread, new_clazz); + vmindex = vmindexValueForMethodMemberName(methodID, newJ9Clazz, new_flags); } } if (J9_ARE_ANY_BITS_SET(flags, MN_IS_FIELD)) { J9Class *declaringClass; @@ -1075,7 +1073,7 @@ Java_java_lang_invoke_MethodHandleNatives_resolve( } } - if ((0 != vmindex) && (0 != target)) { + if ((0 != target) && ((0 != vmindex) || J9_ARE_ANY_BITS_SET(flags, MN_IS_METHOD | MN_IS_CONSTRUCTOR))) { /* Refetch reference after GC point */ membernameObject = J9_JNI_UNWRAP_REFERENCE(self); J9VMJAVALANGINVOKEMEMBERNAME_SET_FLAGS(currentThread, membernameObject, new_flags); @@ -1581,8 +1579,9 @@ Java_java_lang_invoke_MethodHandleNatives_getMemberVMInfo(JNIEnv *env, jclass cl j9object_t target = NULL; /* For fields, vmindexOffset (J9JNIFieldID) is initialized using the field offset in - * jnicsup.cpp::getJNIFieldID. For methods, vmindexOffset (J9JNIMethodID) is initialized - * using jnicsup.cpp::initializeMethodID. + * jnicsup.cpp::getJNIFieldID. For methods, vmindexOffset already has the right value + * (vTable offset for MH_REF_INVOKEVIRTUAL, iTable index for MH_REF_INVOKEINTERFACE, + * and -1 for other ref kinds). */ jlong vmindex = (jlong)(UDATA)J9OBJECT_U64_LOAD(currentThread, membernameObject, vm->vmindexOffset); @@ -1591,28 +1590,6 @@ Java_java_lang_invoke_MethodHandleNatives_getMemberVMInfo(JNIEnv *env, jclass cl vmindex = ((J9JNIFieldID*)vmindex)->offset; target = J9VMJAVALANGINVOKEMEMBERNAME_CLAZZ(currentThread, membernameObject); } else { - jint refKind = (flags >> MN_REFERENCE_KIND_SHIFT) & MN_REFERENCE_KIND_MASK; - if ((MH_REF_INVOKEVIRTUAL == refKind) || (MH_REF_INVOKEINTERFACE == refKind)) { - J9JNIMethodID *methodID = (J9JNIMethodID*)vmindex; - if (J9_ARE_ANY_BITS_SET(methodID->vTableIndex, J9_JNI_MID_INTERFACE)) { - /* vmindex points to an iTable index. */ - vmindex = (jlong)(methodID->vTableIndex & ~J9_JNI_MID_INTERFACE); - } else if (0 == methodID->vTableIndex) { - /* initializeMethodID will set J9JNIMethodID->vTableIndex to 0 for private interface - * methods and j.l.Object methods. Reference implementation (RI) expects vmindex to - * be 0 in such cases. - */ - vmindex = 0; - } else { - /* vmindex points to a vTable index. */ - vmindex = (jlong)methodID->vTableIndex; - } - } else { - /* RI expects direct invocation, i.e. !invokevirtual and !invokeinterface ref kinds, - * to have a negative vmindex. - */ - vmindex = -1; - } target = membernameObject; } diff --git a/runtime/jilgen/jilconsts.c b/runtime/jilgen/jilconsts.c index c39b52346fc..222963d01f0 100644 --- a/runtime/jilgen/jilconsts.c +++ b/runtime/jilgen/jilconsts.c @@ -316,6 +316,9 @@ writeConstants(OMRPortLibrary *OMRPORTLIB, IDATA fd) #if defined(J9VM_JIT_FREE_SYSTEM_STACK_POINTER) writeConstant(OMRPORTLIB, fd, "ASM_J9VM_JIT_FREE_SYSTEM_STACK_POINTER", 1) | #endif /* J9VM_JIT_FREE_SYSTEM_STACK_POINTER */ +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) + writeConstant(OMRPORTLIB, fd, "ASM_J9VM_OPT_OPENJDK_METHODHANDLE", 1) | +#endif /* J9VM_OPT_OPENJDK_METHODHANDLE */ #if defined(J9VM_PORT_ZOS_CEEHDLRSUPPORT) writeConstant(OMRPORTLIB, fd, "ASM_J9VM_PORT_ZOS_CEEHDLRSUPPORT", 1) | writeConstant(OMRPORTLIB, fd, "J9TR_ELS_ceehdlrGPRBase", offsetof(J9VMEntryLocalStorage, ceehdlrGPRBase)) | @@ -579,9 +582,10 @@ writeConstants(OMRPortLibrary *OMRPORTLIB, IDATA fd) writeConstant(OMRPORTLIB, fd, "J9TR_JitConfig_old_fast_jitInstanceOf", offsetof(J9JITConfig, old_fast_jitInstanceOf)) | writeConstant(OMRPORTLIB, fd, "J9TR_JitConfig_old_fast_jitLookupInterfaceMethod", offsetof(J9JITConfig, old_fast_jitLookupInterfaceMethod)) | writeConstant(OMRPORTLIB, fd, "J9TR_JitConfig_old_slow_jitLookupInterfaceMethod", offsetof(J9JITConfig, old_slow_jitLookupInterfaceMethod)) | - writeConstant(OMRPORTLIB, fd, "J9TR_JitConfig_old_fast_jitLookupDynamicInterfaceMethod", offsetof(J9JITConfig, old_fast_jitLookupDynamicInterfaceMethod)) | +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) writeConstant(OMRPORTLIB, fd, "J9TR_JitConfig_old_fast_jitLookupDynamicPublicInterfaceMethod", offsetof(J9JITConfig, old_fast_jitLookupDynamicPublicInterfaceMethod)) | writeConstant(OMRPORTLIB, fd, "J9TR_JitConfig_old_slow_jitLookupDynamicPublicInterfaceMethod", offsetof(J9JITConfig, old_slow_jitLookupDynamicPublicInterfaceMethod)) | +#endif /* J9VM_OPT_OPENJDK_METHODHANDLE */ writeConstant(OMRPORTLIB, fd, "J9TR_JitConfig_old_fast_jitMethodIsNative", offsetof(J9JITConfig, old_fast_jitMethodIsNative)) | writeConstant(OMRPORTLIB, fd, "J9TR_JitConfig_old_fast_jitMethodIsSync", offsetof(J9JITConfig, old_fast_jitMethodIsSync)) | writeConstant(OMRPORTLIB, fd, "J9TR_JitConfig_old_fast_jitMethodMonitorEntry", offsetof(J9JITConfig, old_fast_jitMethodMonitorEntry)) | diff --git a/runtime/jvmti/jvmtiClass.c b/runtime/jvmti/jvmtiClass.c index 671c1deac54..a0cea569d6c 100644 --- a/runtime/jvmti/jvmtiClass.c +++ b/runtime/jvmti/jvmtiClass.c @@ -1129,6 +1129,9 @@ redefineClassesCommon(jvmtiEnv* env, J9HashTable * methodPairs = NULL; J9HashTable * classPairs = NULL; J9HashTable * methodEquivalences = NULL; +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) + j9object_t memberNamesToFix = NULL; +#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ #ifdef J9VM_INTERP_NATIVE_SUPPORT J9JVMTIHCRJitEventData jitEventData; #endif @@ -1210,12 +1213,17 @@ redefineClassesCommon(jvmtiEnv* env, rc = determineClassesToRecreate(currentThread, class_count, specifiedClasses, &classPairs, &methodPairs, jitEventDataPtr, !extensionsEnabled); if (rc == JVMTI_ERROR_NONE) { - /* Recreate the RAM classes for all classes */ + /* Identify the MemberNames needing fix-up based on classPairs. */ +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) + memberNamesToFix = prepareToFixMemberNames(currentThread, classPairs); +#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ + /* Recreate the RAM classes for all classes */ rc = recreateRAMClasses(currentThread, classPairs, methodPairs, extensionsUsed, !extensionsEnabled); if (rc != JVMTI_ERROR_NONE) { goto failedWithVMAccess; } + if (!extensionsEnabled) { /* Fast HCR path - where the J9Class is redefined in place. */ @@ -1237,7 +1245,7 @@ redefineClassesCommon(jvmtiEnv* env, #if defined(J9VM_OPT_OPENJDK_METHODHANDLE) /* Fix MemberNames (vmtarget) */ - fixMemberNames(currentThread, classPairs); + fixMemberNames(currentThread, &memberNamesToFix); #endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ /* Fix resolved constant pool references to point to new methods. */ @@ -1256,7 +1264,6 @@ redefineClassesCommon(jvmtiEnv* env, jitClassRedefineEvent(currentThread, &jitEventData, FALSE, FALSE); } else { - /* Clear/suspend all breakpoints in the classes being replaced */ clearBreakpointsInClasses(currentThread, classPairs); @@ -1301,7 +1308,7 @@ redefineClassesCommon(jvmtiEnv* env, #if defined(J9VM_OPT_OPENJDK_METHODHANDLE) /* Fix MemberNames (vmtarget) */ - fixMemberNames(currentThread, classPairs); + fixMemberNames(currentThread, &memberNamesToFix); #endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ /* Restore breakpoints in the implicitly replaced classes */ @@ -1337,6 +1344,11 @@ redefineClassesCommon(jvmtiEnv* env, failedWithVMAccess: +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) + /* Once the MemberNames have been prepared, they need to be fixed even on error. */ + fixMemberNames(currentThread, &memberNamesToFix); +#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ + hashTableFree(classPairs); if (safePoint) { diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index bf9f3838081..3f2d7a427ea 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -2279,6 +2279,9 @@ typedef struct J9ROMMethodHandleRef { #define MN_FLATTENED 0x00400000 #endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ +#define MN_REFERENCE_KIND_SHIFT 24 +#define MN_REFERENCE_KIND_MASK 0xF /* (flag >> MN_REFERENCE_KIND_SHIFT) & MN_REFERENCE_KIND_MASK */ + typedef struct J9ROMMethodRef { U_32 classRefCPIndex; J9SRP nameAndSignature; @@ -3846,9 +3849,10 @@ typedef struct J9JITConfig { void *old_fast_jitInstanceOf; void *old_fast_jitLookupInterfaceMethod; void *old_slow_jitLookupInterfaceMethod; - void *old_fast_jitLookupDynamicInterfaceMethod; +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) void *old_fast_jitLookupDynamicPublicInterfaceMethod; void *old_slow_jitLookupDynamicPublicInterfaceMethod; +#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ void *old_fast_jitMethodIsNative; void *old_fast_jitMethodIsSync; void *old_fast_jitMethodMonitorEntry; diff --git a/runtime/oti/util_api.h b/runtime/oti/util_api.h index 80666844842..de39cc26d1c 100644 --- a/runtime/oti/util_api.h +++ b/runtime/oti/util_api.h @@ -2226,8 +2226,67 @@ void fixJNIRefs (J9VMThread * currentThread, J9HashTable* classHashTable, BOOLEAN fastHCR, UDATA extensionsUsed); #if defined(J9VM_OPT_OPENJDK_METHODHANDLE) +/** + * @brief Identify MemberName objects that are (potentially) affected by class + * redefinition and put them into a state suitable for fix-up. + * + * This must be done before java/lang/Class objects are updated to point to + * replacement RAM classes. + * + * vmtarget is temporarily repurposed as the next pointer for an intrusive + * linked list of all MemberName objects to fix up. This list allows the same + * MemberNames to be processed again by fixMemberNames() without needing to + * iterate over all objects a second time and without needing to identify the + * same MemberNames after java/lang/Class instances and JNI method/field IDs + * have been updated. + * + * For MemberNames representing methods (MN_IS_METHOD, MN_IS_CONSTRUCTOR), + * vmindex is temporarily set to point to the corresponding J9JNIMethodID, + * which will be used in fixMemberNames() to complete the fix-up. + * + * Note that classHashTable is expected to contain an entry for every affected + * class, in particular including classes whose vTable layouts or iTables + * change due to redefinition of a supertype. + * + * Once preparation completes, it's necessary to fixMemberNames() even if class + * redefinition later fails, since vmtarget and vmindex need to be restored to + * their usual meanings. + * + * @param[in] currentThread the J9VMThread of the current thread + * @param[in] classHashTable the hash table of J9JVMTIClassPairs for redefinition + * @return the first MemberName in the list, or NULL if there are none + */ +j9object_t +prepareToFixMemberNames(J9VMThread *currentThread, J9HashTable *classPairs); + +/** + * @brief Update MemberNames based on their JNI field/method IDs. + * + * memberNamesToFix will be set to NULL so that multiple calls with the same + * list are idempotent. + * + * @param[in] currentThread the J9VMThread of the current thread + * @param[in,out] memberNamesToFix the list of MemberNames from prepareToFixMemberNames() + */ void -fixMemberNames(J9VMThread * currentThread, J9HashTable * classHashTable); +fixMemberNames(J9VMThread *currentThread, j9object_t *memberNamesToFix); + +/** + * @brief Determine the value of MemberName.vmindex for a method. + * + * This is the vTable offset for virtual dispatch (MH_REF_INVOKEVIRTUAL), the + * iTable index for interface dispatch (MH_REF_INVOKEINTERFACE), and -1 for + * direct dispatch (MH_REF_INVOKESTATIC, MH_REF_INVOKESPECIAL). + * + * clazz can differ from the defining class of the method when doing virtual + * dispatch of a method inherited from an interface, in which case the defining + * class is the interface but clazz is the inheriting (non-interface) class. + * + * @param[in] clazz the class (that will be) represented by MemberName.clazz + * @return the value that MemberName.vmindex should take on + */ +jlong +vmindexValueForMethodMemberName(J9JNIMethodID *methodID, J9Class *clazz, jint flags); #endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ void diff --git a/runtime/util/hshelp.c b/runtime/util/hshelp.c index 81c93684547..cf1d891cca6 100644 --- a/runtime/util/hshelp.c +++ b/runtime/util/hshelp.c @@ -91,7 +91,7 @@ static UDATA utfsAreIdentical(J9UTF8 * utf1, J9UTF8 * utf2); static UDATA areUTFPairsIdentical(J9UTF8 * leftUtf1, J9UTF8 * leftUtf2, J9UTF8 * rightUtf1, J9UTF8 * rightUtf2); static jvmtiError fixJNIMethodID(J9VMThread *currentThread, J9Method *oldMethod, J9Method *newMethod, BOOLEAN equivalent, UDATA extensionsUsed); #if defined(J9VM_OPT_OPENJDK_METHODHANDLE) -static jvmtiIterationControl fixMemberNamesObjectIteratorCallback(J9JavaVM *vm, J9MM_IterateObjectDescriptor *objectDesc, void *userData); +static jvmtiIterationControl prepareToFixMemberNamesObjectIteratorCallback(J9JavaVM *vm, J9MM_IterateObjectDescriptor *objectDesc, void *userData); #endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ static jvmtiIterationControl fixHeapRefsHeapIteratorCallback(J9JavaVM *vm, J9MM_IterateHeapDescriptor *heapDesc, void *userData); @@ -1703,65 +1703,64 @@ fixJNIRefs(J9VMThread * currentThread, J9HashTable * classPairs, BOOLEAN fastHCR } } - -typedef struct J9ThreadHashPair { +#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) +typedef struct J9PrepareToFixMemberNameIterData { J9VMThread *currentThread; - void *userData; -} J9ThreadHashPair; + J9HashTable *classHashTable; + j9object_t firstAffectedMemberName; +} J9PrepareToFixMemberNameIterData; -#if defined(J9VM_OPT_OPENJDK_METHODHANDLE) -void -fixMemberNames(J9VMThread * currentThread, J9HashTable * classHashTable) +j9object_t +prepareToFixMemberNames(J9VMThread *currentThread, J9HashTable *classHashTable) { + j9object_t firstAffectedMemberName = NULL; if (NULL != classHashTable) { PORT_ACCESS_FROM_JAVAVM(currentThread->javaVM); - J9ThreadHashPair data; + J9PrepareToFixMemberNameIterData data; data.currentThread = currentThread; - data.userData = classHashTable; + data.classHashTable = classHashTable; + data.firstAffectedMemberName = NULL; - /* iterate over all objects fixing up vmtarget refs if object is a MemberName */ - currentThread->javaVM->memoryManagerFunctions->j9mm_iterate_all_objects(currentThread->javaVM, PORTLIB, 0, fixMemberNamesObjectIteratorCallback, &data); + /* Iterate over all objects and find all affected MemberName instances. */ + currentThread->javaVM->memoryManagerFunctions->j9mm_iterate_all_objects(currentThread->javaVM, PORTLIB, 0, prepareToFixMemberNamesObjectIteratorCallback, &data); + firstAffectedMemberName = data.firstAffectedMemberName; } + + return firstAffectedMemberName; } static jvmtiIterationControl -fixMemberNamesObjectIteratorCallback(J9JavaVM *vm, J9MM_IterateObjectDescriptor *objectDesc, void *userData) +prepareToFixMemberNamesObjectIteratorCallback(J9JavaVM *vm, J9MM_IterateObjectDescriptor *objectDesc, void *userData) { - J9ThreadHashPair *data = userData; + J9PrepareToFixMemberNameIterData *data = userData; J9VMThread *currentThread = data->currentThread; - J9HashTable *classHashTable = data->userData; + J9HashTable *classHashTable = data->classHashTable; j9object_t object = objectDesc->object; J9Class *clazz = J9OBJECT_CLAZZ_VM(vm, object); if (clazz == J9VMJAVALANGINVOKEMEMBERNAME_OR_NULL(vm)) { - U_64 vmindex = J9OBJECT_U64_LOAD(currentThread, object, vm->vmindexOffset); - if (0 != vmindex) { + UDATA vmtarget = (UDATA)J9OBJECT_U64_LOAD(currentThread, object, vm->vmtargetOffset); + if (0 != vmtarget) { J9JVMTIClassPair exemplar; J9JVMTIClassPair *result = NULL; - j9object_t membernameClazz = J9VMJAVALANGINVOKEMEMBERNAME_CLAZZ(currentThread, object); - exemplar.replacementClass.ramClass = J9VM_J9CLASS_FROM_HEAPCLASS(currentThread, membernameClazz); + exemplar.originalRAMClass = J9VM_J9CLASS_FROM_HEAPCLASS(currentThread, membernameClazz); result = hashTableFind(classHashTable, &exemplar); if (NULL != result) { jint flags = J9VMJAVALANGINVOKEMEMBERNAME_FLAGS(currentThread, object); - if (J9_ARE_ANY_BITS_SET(flags, MN_IS_FIELD)) { - /* Update vmtarget to vmindex->offset */ - J9JNIFieldID *fieldID = (J9JNIFieldID *)(UDATA)vmindex; - J9ROMFieldShape *romField = fieldID->field; - UDATA offset = fieldID->offset; - - if (J9_ARE_ANY_BITS_SET(romField->modifiers, J9AccStatic)) { - offset |= J9_SUN_STATIC_FIELD_OFFSET_TAG; - if (J9_ARE_ANY_BITS_SET(romField->modifiers, J9AccFinal)) { - offset |= J9_SUN_FINAL_FIELD_OFFSET_TAG; - } + if (J9_ARE_ANY_BITS_SET(flags, MN_IS_METHOD | MN_IS_CONSTRUCTOR | MN_IS_FIELD)) { + if (J9_ARE_ANY_BITS_SET(flags, MN_IS_METHOD | MN_IS_CONSTRUCTOR)) { + /* Set vmindex temporarily to the J9JNIMethodID for vmtarget. The method ID will + * be updated first, and then it will be used to fix the MemberName afterward. + */ + J9JNIMethodID *methodID = currentThread->javaVM->internalVMFunctions->getJNIMethodID(currentThread, (J9Method *)vmtarget); + J9OBJECT_U64_STORE(currentThread, object, vm->vmindexOffset, (U_64)(UDATA)methodID); } - J9OBJECT_U64_STORE(currentThread, object, vm->vmtargetOffset, (U_64)offset); - } else if (J9_ARE_ANY_BITS_SET(flags, MN_IS_METHOD | MN_IS_CONSTRUCTOR)) { - /* Update vmtarget to vmindex->method */ - J9JNIMethodID *methodID = (J9JNIMethodID *)(UDATA)vmindex; - J9OBJECT_U64_STORE(currentThread, object, vm->vmtargetOffset, (U_64)(UDATA)methodID->method); + + /* Temporarily take over vmtarget as the next pointer for a linked list of all affected MemberName instances. */ + J9OBJECT_U64_STORE(currentThread, object, vm->vmtargetOffset, (U_64)(UDATA)data->firstAffectedMemberName); + data->firstAffectedMemberName = object; } } } @@ -1769,6 +1768,113 @@ fixMemberNamesObjectIteratorCallback(J9JavaVM *vm, J9MM_IterateObjectDescriptor return JVMTI_ITERATION_CONTINUE; } + +void +fixMemberNames(J9VMThread *currentThread, j9object_t *memberNamesToFix) +{ + J9JavaVM *vm = currentThread->javaVM; + j9object_t object = *memberNamesToFix; + + *memberNamesToFix = NULL; /* For idempotency. */ + + while (NULL != object) { + j9object_t nextObject = (j9object_t)(UDATA)J9OBJECT_U64_LOAD(currentThread, object, vm->vmtargetOffset); + jint flags = J9VMJAVALANGINVOKEMEMBERNAME_FLAGS(currentThread, object); + U_64 vmindex = J9OBJECT_U64_LOAD(currentThread, object, vm->vmindexOffset); + + Assert_hshelp_false(0 == vmindex); /* Must be a valid J9JNIFieldID or J9JNIMethodID pointer. */ + + if (J9_ARE_ANY_BITS_SET(flags, MN_IS_FIELD)) { + /* Update vmtarget to vmindex->offset. */ + J9JNIFieldID *fieldID = (J9JNIFieldID *)(UDATA)vmindex; + J9ROMFieldShape *romField = fieldID->field; + UDATA offset = fieldID->offset; + + if (J9_ARE_ANY_BITS_SET(romField->modifiers, J9AccStatic)) { + offset |= J9_SUN_STATIC_FIELD_OFFSET_TAG; + if (J9_ARE_ANY_BITS_SET(romField->modifiers, J9AccFinal)) { + offset |= J9_SUN_FINAL_FIELD_OFFSET_TAG; + } + } + + J9OBJECT_U64_STORE(currentThread, object, vm->vmtargetOffset, (U_64)offset); + } else if (J9_ARE_ANY_BITS_SET(flags, MN_IS_METHOD | MN_IS_CONSTRUCTOR)) { + /* Update vmtarget to vmindex->method and set vmindex as appropriate for dispatch. */ + J9JNIMethodID *methodID = (J9JNIMethodID *)(UDATA)vmindex; + j9object_t clazzObj = J9VMJAVALANGINVOKEMEMBERNAME_CLAZZ(currentThread, object); + J9Class *clazz = J9VM_J9CLASS_FROM_HEAPCLASS(currentThread, clazzObj); + jlong vmindex = vmindexValueForMethodMemberName(methodID, clazz, flags); + J9OBJECT_U64_STORE(currentThread, object, vm->vmtargetOffset, (U_64)(UDATA)methodID->method); + J9OBJECT_U64_STORE(currentThread, object, vm->vmindexOffset, (U_64)vmindex); + } else { + /* The MemberName must represent a field, method, or constructor. */ + Assert_hshelp_true(FALSE); + } + + object = nextObject; + } +} + +jlong +vmindexValueForMethodMemberName(J9JNIMethodID *methodID, J9Class *clazz, jint flags) +{ + jint refKind = (flags >> MN_REFERENCE_KIND_SHIFT) & MN_REFERENCE_KIND_MASK; + jlong result = (jlong)-2; /* Invalid result (must be replaced). */ + + Assert_hshelp_true(J9_ARE_ANY_BITS_SET(flags, MN_IS_METHOD | MN_IS_CONSTRUCTOR)); + + switch (refKind) { + case MH_REF_INVOKESTATIC: + case MH_REF_INVOKESPECIAL: + case MH_REF_NEWINVOKESPECIAL: + result = (jlong)-1; + break; + + case MH_REF_INVOKEINTERFACE: + Assert_hshelp_true(J9_ARE_ALL_BITS_SET(methodID->vTableIndex, J9_JNI_MID_INTERFACE)); + Assert_hshelp_true(J9_ARE_ALL_BITS_SET(clazz->romClass->modifiers, J9AccInterface)); + result = (jlong)(methodID->vTableIndex & ~(UDATA)J9_JNI_MID_INTERFACE); + break; + + case MH_REF_INVOKEVIRTUAL: + Assert_hshelp_true(J9_ARE_NO_BITS_SET(clazz->romClass->modifiers, J9AccInterface)); + if (J9_ARE_NO_BITS_SET(methodID->vTableIndex, J9_JNI_MID_INTERFACE)) { + result = (jlong)methodID->vTableIndex; + } else { + /* Find the vTable offset for this method in clazz. */ + J9Class *interfaceClass = J9_CLASS_FROM_METHOD(methodID->method); + UDATA iTableIndex = methodID->vTableIndex & ~(UDATA)J9_JNI_MID_INTERFACE; + UDATA vTableOffset = 0; + J9ITable *iTable = clazz->lastITable; + + if (interfaceClass == iTable->interfaceClass) { + goto foundITable; + } + + iTable = (J9ITable *)clazz->iTable; + while (NULL != iTable) { + if (interfaceClass == iTable->interfaceClass) { + clazz->lastITable = iTable; +foundITable: + vTableOffset = ((UDATA *)(iTable + 1))[iTableIndex]; + break; + } + + iTable = iTable->next; + } + + result = (jlong)vTableOffset; + } + + break; + + default: + Assert_hshelp_true(FALSE); + } + + Assert_hshelp_true((jlong)-1 <= result); + return result; +} #endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ diff --git a/runtime/vm/BytecodeInterpreter.hpp b/runtime/vm/BytecodeInterpreter.hpp index 74106afb846..a8613276027 100644 --- a/runtime/vm/BytecodeInterpreter.hpp +++ b/runtime/vm/BytecodeInterpreter.hpp @@ -9184,10 +9184,10 @@ class INTERPRETER_CLASS return THROW_NPE; } - J9JNIMethodID *methodID = (J9JNIMethodID *)(UDATA)J9OBJECT_U64_LOAD(_currentThread, memberNameObject, _vm->vmindexOffset); - J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(methodID->method); + J9Method *method = (J9Method *)(UDATA)J9OBJECT_U64_LOAD(_currentThread, memberNameObject, _vm->vmtargetOffset); + J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method); UDATA methodArgCount = 0; - bool isInvokeBasic = (J9_BCLOOP_SEND_TARGET_METHODHANDLE_INVOKEBASIC == J9_BCLOOP_DECODE_SEND_TARGET(methodID->method->methodRunAddress)); + bool isInvokeBasic = (J9_BCLOOP_SEND_TARGET_METHODHANDLE_INVOKEBASIC == J9_BCLOOP_DECODE_SEND_TARGET(method->methodRunAddress)); /* In MethodHandle.loop API it may generate a invokeBasic NamedFunction (see LambdaForm$NamedFunction(MethodType basicInvokerType)) * that is linked by linkToVirtual. As invokeBasic is signature polymorphic, the romMethod->argCount may not match the actual @@ -9211,43 +9211,17 @@ class INTERPRETER_CLASS return THROW_NPE; } - /* vmindexOffset (J9JNIMethodID) is initialized using jnicsup.cpp::initializeMethodID. - * initializeMethodID will set J9JNIMethodID->vTableIndex to 0 for private interface - * methods and j.l.Object methods. When J9JNIMethodID->vTableIndex is 0, then - * vmtargetOffset (J9Method) is the _sendMethod, and it will point to the private - * interface method or j.l.Object method. When J9JNIMethodID->vTableIndex is not 0, - * then it is either a vTable offset or an iTable index. + /* The vTable offset has been stored in memberNameObject.vmindex. + * + * Directly-dispatched instance methods don't reach here because they will go through linkToSpecial() instead. + * + * An interface method can reach here, but only when it was found via some non-interface class C that inherits it. + * In that case, MemberName resolution has already done the iTable walk to get the corresponding vTable offset in + * C and stored it in vmindex. The receiver is always an instance of C (or a subclass). */ - UDATA vTableOffset = methodID->vTableIndex; - if (0 == vTableOffset) { - _sendMethod = (J9Method *)(UDATA)J9OBJECT_U64_LOAD(_currentThread, memberNameObject, _vm->vmtargetOffset); - } else { - J9Class *receiverClass = J9OBJECT_CLAZZ(_currentThread, receiverObject); - - if (J9_ARE_ANY_BITS_SET(vTableOffset, J9_JNI_MID_INTERFACE)) { - /* Treat as iTable index for the method if J9_JNI_MID_INTERFACE is set. */ - UDATA iTableIndex = vTableOffset & ~(UDATA)J9_JNI_MID_INTERFACE; - J9Class *interfaceClass = J9_CLASS_FROM_METHOD(methodID->method); - /* Get the latest version of the class for the iTable search. */ - interfaceClass = VM_VMHelpers::currentClass(interfaceClass); - vTableOffset = 0; - J9ITable * iTable = receiverClass->lastITable; - if (interfaceClass == iTable->interfaceClass) { - goto foundITable; - } - iTable = (J9ITable*)receiverClass->iTable; - while (NULL != iTable) { - if (interfaceClass == iTable->interfaceClass) { - receiverClass->lastITable = iTable; -foundITable: - vTableOffset = ((UDATA*)(iTable + 1))[iTableIndex]; - break; - } - iTable = iTable->next; - } - } - _sendMethod = *(J9Method **)(((UDATA)receiverClass) + vTableOffset); - } + UDATA vTableOffset = (UDATA)J9OBJECT_U64_LOAD(_currentThread, memberNameObject, _vm->vmindexOffset); + J9Class *receiverClass = J9OBJECT_CLAZZ(_currentThread, receiverObject); + _sendMethod = *(J9Method **)(((UDATA)receiverClass) + vTableOffset); /* The invokeBasic INL uses the methodArgCount from ramCP to locate the receiver object, * so when dispatched using linkToVirtual, it will still access the ramCP of the linkToVirtual call @@ -9282,13 +9256,15 @@ class INTERPRETER_CLASS { VM_BytecodeAction rc = GOTO_RUN_METHOD; bool fromJIT = J9_ARE_ANY_BITS_SET(jitStackFrameFlags(REGISTER_ARGS, 0), J9_SSF_JIT_NATIVE_TRANSITION_FRAME); - J9JNIMethodID *methodID = NULL; J9ROMMethod *romMethod = NULL; UDATA methodArgCount = 0; j9object_t receiverObject = NULL; J9Class *receiverClass = NULL; J9Method *method = NULL; UDATA vTableOffset = 0; + UDATA iTableIndex = 0; + J9Class *interfaceClass = NULL; + J9ITable *iTable = NULL; /* Pop memberNameObject from the stack. */ j9object_t memberNameObject = *(j9object_t *)_sp++; @@ -9302,8 +9278,8 @@ class INTERPRETER_CLASS goto done; } - methodID = (J9JNIMethodID *)(UDATA)J9OBJECT_U64_LOAD(_currentThread, memberNameObject, _vm->vmindexOffset); - romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(methodID->method); + method = (J9Method *)(UDATA)J9OBJECT_U64_LOAD(_currentThread, memberNameObject, _vm->vmtargetOffset); + romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method); methodArgCount = romMethod->argCount; receiverObject = ((j9object_t *)_sp)[methodArgCount - 1]; @@ -9318,46 +9294,43 @@ class INTERPRETER_CLASS } receiverClass = J9OBJECT_CLAZZ(_currentThread, receiverObject); - method = (J9Method *)(UDATA)J9OBJECT_U64_LOAD(_currentThread, memberNameObject, _vm->vmtargetOffset); - vTableOffset = methodID->vTableIndex; - - /* vmindexOffset (J9JNIMethodID) is initialized using jnicsup.cpp::initializeMethodID. - * initializeMethodID will set J9JNIMethodID->vTableIndex to 0 for private interface - * methods and j.l.Object methods. When J9JNIMethodID->vTableIndex is 0, then - * vmtargetOffset (J9Method) is the _sendMethod, and it will point to the private - * interface method or j.l.Object method. When J9JNIMethodID->vTableIndex is not 0, - * then it is either a vTable offset or an iTable index. + + /* Here MemberName.vmindex is always an iTable index. If an instance method is supposed + * to be dispatched directly, then it will go through linkToSpecial() instead. If it's + * supposed to go through non-interface virtual dispatch, like e.g. a non-final method + * of Object, it will go through linkToVirtual() instead. */ - if (0 == vTableOffset) { - /* Private interface method or j.l.Object method. */ - _sendMethod = method; - } else { - /* Treat as vTable offset for the method if J9_JNI_MID_INTERFACE is not set. */ - if (J9_ARE_ANY_BITS_SET(vTableOffset, J9_JNI_MID_INTERFACE)) { - /* Treat as iTable index for the method if J9_JNI_MID_INTERFACE is set. */ - UDATA iTableIndex = vTableOffset & ~(UDATA)J9_JNI_MID_INTERFACE; - J9Class *interfaceClass = J9_CLASS_FROM_METHOD(method); - /* Get the latest version of the class for the iTable search. */ - interfaceClass = VM_VMHelpers::currentClass(interfaceClass); - vTableOffset = 0; - J9ITable * iTable = receiverClass->lastITable; - if (interfaceClass == iTable->interfaceClass) { - goto foundITable; - } - iTable = (J9ITable*)receiverClass->iTable; - while (NULL != iTable) { - if (interfaceClass == iTable->interfaceClass) { - receiverClass->lastITable = iTable; + iTableIndex = (UDATA)J9OBJECT_U64_LOAD(_currentThread, memberNameObject, _vm->vmindexOffset); + interfaceClass = J9_CLASS_FROM_METHOD(method); + vTableOffset = 0; + iTable = receiverClass->lastITable; + if (interfaceClass == iTable->interfaceClass) { + goto foundITable; + } + iTable = (J9ITable *)receiverClass->iTable; + while (NULL != iTable) { + if (interfaceClass == iTable->interfaceClass) { + receiverClass->lastITable = iTable; foundITable: - vTableOffset = ((UDATA*)(iTable + 1))[iTableIndex]; - break; - } - iTable = iTable->next; - } + vTableOffset = ((UDATA *)(iTable + 1))[iTableIndex]; + break; } - _sendMethod = *(J9Method **)(((UDATA)receiverClass) + vTableOffset); + iTable = iTable->next; } + /* The bytecode guarantees with an explicit type test that the receiver is an instance + * of the expected interface. + * + * Nevertheless, this assertion can fail if interfaceClass is obsolete and therefore has + * no corresponding iTable. This situation can only arise if interfaceClass has been + * redefined in such a way as to remove method from it, in which case iTableIndex no + * longer meaningfully corresponds to method, and a crash is highly likely were dispatch + * to proceed as usual. Better to crash eagerly by failing the assertion here. + */ + Assert_VM_false(0 == vTableOffset); + + _sendMethod = *(J9Method **)(((UDATA)receiverClass) + vTableOffset); + romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(_sendMethod); if (J9_ARE_NO_BITS_SET(romMethod->modifiers, J9AccPublic | J9AccPrivate)) { if (fromJIT) {