Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions runtime/compiler/codegen/J9RecognizedMethodsEnum.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -622,13 +622,14 @@ FirstJ9Method = LastOMRMethod + 1,
java_lang_invoke_InsertHandle_numSuffixArgs, java_lang_invoke_InsertHandle_numValuesToInsert,
java_lang_invoke_InterfaceHandle_invokeExact, java_lang_invoke_Invokers_checkCustomized,
java_lang_invoke_Invokers_checkExactType, java_lang_invoke_Invokers_getCallSiteTarget,
java_lang_invoke_Invokers_checkVarHandleGenericType, java_lang_invoke_MethodHandle_doCustomizationLogic,
java_lang_invoke_MethodHandle_asType, java_lang_invoke_MethodHandle_asType_instance,
java_lang_invoke_MethodHandle_invoke, java_lang_invoke_MethodHandle_invokeExact,
java_lang_invoke_MethodHandle_invokeBasic, java_lang_invoke_MethodHandle_invokeExactTargetAddress,
java_lang_invoke_MethodHandle_linkToStatic, java_lang_invoke_MethodHandle_linkToSpecial,
java_lang_invoke_MethodHandle_linkToVirtual, java_lang_invoke_MethodHandle_linkToInterface,
java_lang_invoke_MethodHandle_linkToNative, java_lang_invoke_MethodHandleImpl_ArrayAccessor_getElementI,
java_lang_invoke_Invokers_checkGenericType, java_lang_invoke_Invokers_checkVarHandleGenericType,
java_lang_invoke_MethodHandle_doCustomizationLogic, java_lang_invoke_MethodHandle_asType,
java_lang_invoke_MethodHandle_asType_instance, java_lang_invoke_MethodHandle_invoke,
java_lang_invoke_MethodHandle_invokeExact, java_lang_invoke_MethodHandle_invokeBasic,
java_lang_invoke_MethodHandle_invokeExactTargetAddress, java_lang_invoke_MethodHandle_linkToStatic,
java_lang_invoke_MethodHandle_linkToSpecial, java_lang_invoke_MethodHandle_linkToVirtual,
java_lang_invoke_MethodHandle_linkToInterface, java_lang_invoke_MethodHandle_linkToNative,
java_lang_invoke_MethodHandleImpl_ArrayAccessor_getElementI,
java_lang_invoke_MethodHandleImpl_ArrayAccessor_getElementJ,
java_lang_invoke_MethodHandleImpl_ArrayAccessor_getElementF,
java_lang_invoke_MethodHandleImpl_ArrayAccessor_getElementD,
Expand Down
12 changes: 12 additions & 0 deletions runtime/compiler/control/JITClientCompilationThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,18 @@ static bool handleResponse(JITServer::MessageType response, JITServer::ClientStr
client->write(response, result, knot->getPointerLocation(mhIndex),
knot->getPointerLocation(expectedTypeIndex));
} break;
case MessageType::VM_getConvertedMethodHandle: {
auto recv = client->getRecvData<TR::KnownObjectTable::Index, TR::KnownObjectTable::Index>();
TR::KnownObjectTable::Index mhIndex = std::get<0>(recv);
TR::KnownObjectTable::Index desiredTypeIndex = std::get<1>(recv);

TR::KnownObjectTable::Index convertedMHIndex
= fe->getConvertedMethodHandle(comp, mhIndex, desiredTypeIndex);
uintptr_t *mhObj = NULL;
if (convertedMHIndex != TR::KnownObjectTable::UNKNOWN)
mhObj = knot->getPointerLocation(convertedMHIndex);
client->write(response, convertedMHIndex, mhObj);
} break;
case MessageType::VM_getMethodHandleTableEntryIndex: {
auto recv = client->getRecvData<TR::KnownObjectTable::Index, TR::KnownObjectTable::Index>();
TR::KnownObjectTable::Index mhIndex
Expand Down
34 changes: 34 additions & 0 deletions runtime/compiler/env/VMJ9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4909,6 +4909,40 @@ bool TR_J9VMBase::isMethodHandleExpectedType(TR::Compilation *comp, TR::KnownObj
return mtObject == etObject;
}

OMR::KnownObjectTable::Index TR_J9VMBase::getConvertedMethodHandle(TR::Compilation *comp,
TR::KnownObjectTable::Index mhIndex, TR::KnownObjectTable::Index desiredTypeIndex)
{
/* Individual type compatibility checks on each type in the MT are not needed. Since we only fold
* when we have a populated cache, and a call to asType would make <desiredType> and the asType result's
* MT match, we only need to check for MT pointer equality.
*/
TR::KnownObjectTable *knot = comp->getKnownObjectTable();
if (!knot)
return TR::KnownObjectTable::UNKNOWN;

TR::VMAccessCriticalSection vmAccess(this);

/* We shouldn't need to check for anonymous classes or for same class loaders for the Hard Cache case.
* The java.lang.invoke infrastructure only sets this cache when it is safe to do so.
*
* if we find that the normal asTypeCache doesn't get many hits, we should look into adding SoftCache checks.
*/
uintptr_t mhObject = knot->getPointer(mhIndex);
uintptr_t cachedMT = 0;
uintptr_t cachedMH = getReferenceField(mhObject, "asTypeCache", "Ljava/lang/invoke/MethodHandle;");
if (cachedMH != 0) {
cachedMT = getReferenceField(cachedMH, "type", "Ljava/lang/invoke/MethodType;");
}

uintptr_t desiredMTObject = knot->getPointer(desiredTypeIndex);
// there is only one MT instance per unique signature, so this check is valid
if (desiredMTObject == cachedMT) {
logprintf(comp->getOption(TR_TraceOptDetails), comp->log(), "(Hard) cache match\n");
return knot->getOrCreateIndex(cachedMH);
}
return TR::KnownObjectTable::UNKNOWN;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VMAccess ends here, with the function returning a raw pointer (if successful). The callers of this function then takes the resulting raw pointer, and then calls knot->getOrCreateIndex(result of this function), which is not GC safe. The VM access should be held all the way till the knot index is obtained, and the easiest way to do that would be to modify this function to return the knot index instead of the raw pointer, so that VM access is held throughout.


/**
* \brief
* Check if two java/lang/String objects are equal. Equivalent to java/lang/String.equals.
Expand Down
12 changes: 12 additions & 0 deletions runtime/compiler/env/VMJ9.h
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,18 @@ class TR_J9VMBase : public TR_FrontEnd {
virtual bool isMethodHandleExpectedType(TR::Compilation *comp, TR::KnownObjectTable::Index mhIndex,
TR::KnownObjectTable::Index expectedTypeIndex);

/**
* \brief
* Gets the KOI of the adapted MethodHandle
*
* \param comp the compilation object
* \param mhIndex known object index of the java/lang/invoke/MethodHandle object
* \param desiredTypeIndex known object index of java/lang/invoke/MethodType object
* \return the KOI index of the adapted method handle, which will be UNKNOWN if the MethodTypes do not match
*/
virtual OMR::KnownObjectTable::Index getConvertedMethodHandle(TR::Compilation *comp,
TR::KnownObjectTable::Index mhIndex, TR::KnownObjectTable::Index desiredTypeIndex);

/*
* \brief
* tell whether it's possible to dereference a field given the field symbol reference at compile time
Expand Down
15 changes: 15 additions & 0 deletions runtime/compiler/env/VMJ9Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2389,6 +2389,21 @@ bool TR_J9ServerVM::isMethodHandleExpectedType(TR::Compilation *comp, TR::KnownO
return std::get<0>(recv);
}

OMR::KnownObjectTable::Index TR_J9ServerVM::getConvertedMethodHandle(TR::Compilation *comp,
TR::KnownObjectTable::Index mhIndex, TR::KnownObjectTable::Index desiredTypeIndex)
{
TR::KnownObjectTable *knot = comp->getKnownObjectTable();
if (!knot)
return TR::KnownObjectTable::UNKNOWN;
JITServer::ServerStream *stream = _compInfoPT->getMethodBeingCompiled()->_stream;
stream->write(JITServer::MessageType::VM_getConvertedMethodHandle, mhIndex, desiredTypeIndex);
auto recv = stream->read<TR::KnownObjectTable::Index, uintptr_t *>();

TR::KnownObjectTable::Index convertedMHIndex = std::get<0>(recv);
knot->updateKnownObjectTableAtServer(convertedMHIndex, std::get<1>(recv));
return convertedMHIndex;
}

bool TR_J9ServerVM::isLambdaFormGeneratedMethod(TR_OpaqueMethodBlock *method)
{
JITServer::ServerStream *stream = _compInfoPT->getMethodBeingCompiled()->_stream;
Expand Down
2 changes: 2 additions & 0 deletions runtime/compiler/env/VMJ9Server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,8 @@ class TR_J9ServerVM : public TR_J9VM {
TR::KnownObjectTable::Index mhIndex, const char *fieldName) override;
virtual bool isMethodHandleExpectedType(TR::Compilation *comp, TR::KnownObjectTable::Index mhIndex,
TR::KnownObjectTable::Index expectedTypeIndex) override;
virtual OMR::KnownObjectTable::Index getConvertedMethodHandle(TR::Compilation *comp,
TR::KnownObjectTable::Index mhIndex, TR::KnownObjectTable::Index desiredTypeIndex) override;
virtual bool inSnapshotMode() override;
virtual bool isSnapshotModeEnabled() override;
virtual bool isPortableRestoreModeEnabled() override;
Expand Down
2 changes: 2 additions & 0 deletions runtime/compiler/env/j9method.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3641,6 +3641,8 @@ void TR_ResolvedJ9Method::construct()
{ TR::java_lang_invoke_Invokers_checkCustomized, 15, "checkCustomized", (int16_t)-1, "*" },
{ TR::java_lang_invoke_Invokers_checkExactType, 14, "checkExactType", (int16_t)-1, "*" },
{ TR::java_lang_invoke_Invokers_getCallSiteTarget, 17, "getCallSiteTarget", (int16_t)-1, "*" },
{ x(TR::java_lang_invoke_Invokers_checkGenericType, "checkGenericType",
"(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;") },
{ x(TR::java_lang_invoke_Invokers_checkVarHandleGenericType, "checkVarHandleGenericType",
"(Ljava/lang/invoke/VarHandle;Ljava/lang/invoke/VarHandle$AccessDescriptor;)Ljava/lang/invoke/"
"MethodHandle;") },
Expand Down
4 changes: 2 additions & 2 deletions runtime/compiler/net/ClientStream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class ClientStream : public CommunicationStream {

Note that the discovery of the incompatibility is done in the first message
that the client sends to the server. This function is actually called to
determine if we need to try the compatibilty check again. The idea is that
determine if we need to try the compatibility check again. The idea is that
the incompatible server could be killed and another one (compatible) could be
instantiated. If enough time has passed since the server was found to be
incompatible, then the client should try again. "Enough time" is defined as a
Expand Down Expand Up @@ -188,7 +188,7 @@ class ClientStream : public CommunicationStream {
static int _incompatibilityCount;
static uint64_t _incompatibleStartTime; // Time when version incomptibility has been detected
static const uint64_t
RETRY_COMPATIBILITY_INTERVAL_MS; // (ms) When we should perform again a version compatibilty check
RETRY_COMPATIBILITY_INTERVAL_MS; // (ms) When we should perform again a version compatibility check
static const int INCOMPATIBILITY_COUNT_LIMIT;

static SSL_CTX *_sslCtx;
Expand Down
2 changes: 1 addition & 1 deletion runtime/compiler/net/CommunicationStream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,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 = 101; // ID: fd7QYgAKTKgrQL6Fxs+7
static const uint16_t MINOR_NUMBER = 102; // ID: /HgmjAVrlw2DV8qPrQ3o
static const uint8_t PATCH_NUMBER = 0;
static uint32_t CONFIGURATION_FLAGS;

Expand Down
3 changes: 2 additions & 1 deletion runtime/compiler/net/MessageTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,9 @@ const char *messageNames[] = {
"VM_targetMethodFromInvokeCacheArrayMemberNameObj",
"VM_isLambdaFormGeneratedMethod",
"VM_getMemberNameMethodInfo",
"VM_isMethodHandleExpectedType",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

"VM_getMemberNameFieldKnotIndexFromMethodHandleKnotIndex",
"VM_isMethodHandleExpectedType",
"VM_getConvertedMethodHandle",
"VM_isStable",
"VM_delegatingMethodHandleTarget",
"VM_getVMTargetOffset",
Expand Down
1 change: 1 addition & 0 deletions runtime/compiler/net/MessageTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ enum MessageType : uint16_t {
VM_getMemberNameMethodInfo,
VM_getMemberNameFieldKnotIndexFromMethodHandleKnotIndex,
VM_isMethodHandleExpectedType,
VM_getConvertedMethodHandle,
VM_isStable,
VM_delegatingMethodHandleTarget,
VM_getVMTargetOffset,
Expand Down
1 change: 1 addition & 0 deletions runtime/compiler/optimizer/InlinerTempForJ9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5727,6 +5727,7 @@ TR_InlinerFailureReason TR_J9InlinerPolicy::checkIfTargetInlineable(TR_CallTarge
case TR::java_lang_StringUTF16_toBytes:
case TR::java_lang_invoke_MethodHandle_asType:
case TR::java_lang_invoke_Invokers_checkVarHandleGenericType:
case TR::java_lang_invoke_Invokers_checkGenericType:
return DontInline_Callee;
default:
break;
Expand Down
30 changes: 30 additions & 0 deletions runtime/compiler/optimizer/InterpreterEmulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,36 @@ Operand *InterpreterEmulator::getReturnValue(TR_ResolvedMethod *callee)
}
break;
}
case TR::java_lang_invoke_Invokers_checkGenericType: {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should check for and abort early if const refs are disabled.

if (!comp()->useConstRefs())
break;

Operand *receiverMHOperand = topn(1);
Operand *desiredMTOperand = topn(0);
TR::KnownObjectTable::Index mhIndex = receiverMHOperand->getKnownObjectIndex();
TR::KnownObjectTable::Index mtIndex = desiredMTOperand->getKnownObjectIndex();
debugTrace(tracer(), "Known MethodHandle koi %d\n", mhIndex);
debugTrace(tracer(), "Known MethodType koi %d\n", mtIndex);
TR::KnownObjectTable *knot = comp()->getKnownObjectTable();
if (knot && mhIndex != TR::KnownObjectTable::UNKNOWN && mtIndex != TR::KnownObjectTable::UNKNOWN
&& !knot->isNull(mhIndex) && !knot->isNull(mtIndex)) {
if (comp()->fej9()->isMethodHandleExpectedType(comp(), mhIndex, mtIndex)) {
result = new (trStackMemory()) KnownObjOperand(mhIndex);
debugTrace(tracer(), "MH.asType: exact match\n");
break;
}

TR::KnownObjectTable::Index convertedMHIndex
= comp()->fej9()->getConvertedMethodHandle(comp(), mhIndex, mtIndex);
if (TR::KnownObjectTable::UNKNOWN != convertedMHIndex) {
J9::ConstProvenanceGraph *cpg = comp()->constProvenanceGraph();
cpg->addEdge(cpg->knownObject(mhIndex), cpg->knownObject(convertedMHIndex));
result = new (trStackMemory()) KnownObjOperand(convertedMHIndex);
debugTrace(tracer(), "MH.asType: subtype match\n");
}
}
break;
}
case TR::jdk_internal_foreign_layout_ValueLayouts_AbstractValueLayout_accessHandle: {
Operand *layoutOperand = top();
TR::KnownObjectTable::Index layoutIndex = layoutOperand->getKnownObjectIndex();
Expand Down
44 changes: 43 additions & 1 deletion runtime/compiler/optimizer/MethodHandleTransformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "env/CompilerEnv.hpp"
#include "env/IO.hpp"
#include "env/VMJ9.h"
#include "env/J9ConstProvenanceGraph.hpp"
#include "env/j9method.h"
#include "il/ILOpCodes.hpp"
#include "il/ILOps.hpp"
Expand Down Expand Up @@ -556,7 +557,9 @@ void TR_MethodHandleTransformer::visitCall(TR::TreeTop *tt, TR::Node *node)
case TR::java_lang_invoke_Invokers_checkVarHandleGenericType:
process_java_lang_invoke_Invokers_checkVarHandleGenericType(tt, node);
break;

case TR::java_lang_invoke_Invokers_checkGenericType:
process_java_lang_invoke_Invokers_checkGenericType(tt, node);
break;
default:
break;
}
Expand Down Expand Up @@ -949,3 +952,42 @@ void TR_MethodHandleTransformer::process_java_lang_invoke_Invokers_checkVarHandl
}
#endif // TR_ALLOW_NON_CONST_KNOWN_OBJECTS
}

void TR_MethodHandleTransformer::process_java_lang_invoke_Invokers_checkGenericType(TR::TreeTop *tt, TR::Node *node)
{
if (!comp()->useConstRefs())
return;

auto mhNode = node->getArgument(0);
auto desiredMTNode = node->getArgument(1);
TR::KnownObjectTable::Index mhIndex = getObjectInfoOfNode(mhNode);
TR::KnownObjectTable::Index desiredMTIndex = getObjectInfoOfNode(desiredMTNode);
logprintf(trace(), comp()->log(), "MethodHandle is obj%d\n", mhIndex);
logprintf(trace(), comp()->log(), "MethodType is obj%d\n", desiredMTIndex);
auto knot = comp()->getKnownObjectTable();
TR_J9VMBase *fej9 = static_cast<TR_J9VMBase *>(comp()->fe());
if (knot && isKnownObject(mhIndex) && !knot->isNull(mhIndex) && isKnownObject(desiredMTIndex)
&& !knot->isNull(desiredMTIndex)) {
logprintf(trace(), comp()->log(), "Checking exact compatibility\n");
if (fej9->isMethodHandleExpectedType(comp(), mhIndex, desiredMTIndex)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fej9 is being accessed in 2 different ways in the same function.

logprintf(trace(), comp()->log(), "checkGenericType: exact match\n");
anchorAllChildren(node, tt);
node->removeAllChildren();
TR::Node::recreateWithSymRef(node, TR::aload, knot->constSymRef(mhIndex));
return;
}

logprintf(trace(), comp()->log(), "Exact compatibility check failed - checking subtype compatibility\n");
TR::KnownObjectTable::Index convertedMHIndex = fej9->getConvertedMethodHandle(comp(), mhIndex, desiredMTIndex);
if (TR::KnownObjectTable::UNKNOWN != convertedMHIndex) {
logprintf(trace(), comp()->log(), "checkGenericType: subtype match\n");
J9::ConstProvenanceGraph *cpg = comp()->constProvenanceGraph();
cpg->addEdge(cpg->knownObject(mhIndex), cpg->knownObject(convertedMHIndex));
anchorAllChildren(node, tt);
node->removeAllChildren();
TR::Node::recreateWithSymRef(node, TR::aload, knot->constSymRef(convertedMHIndex));
return;
}
logprintf(trace(), comp()->log(), "MethodTypes are not compatible\n");
}
}
18 changes: 18 additions & 0 deletions runtime/compiler/optimizer/MethodHandleTransformer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,24 @@ class TR_MethodHandleTransformer : public TR::Optimization {
*/
void process_java_lang_invoke_Invokers_checkVarHandleGenericType(TR::TreeTop *tt, TR::Node *node);

/**
* \brief
* Eliminates calls to Invokers.checkGenericType when both the MethodHandle and the
* MethodType are known objects and either of the following is true:
*
* a) The given MethodType is the exact same object as the MethodType of the asTypeCache
* of the receiver MethodHandle.
* b) The given MethodType is the exact same object as the receiver MethodHandle's MethodType.
*
* Otherwise, the transformation is aborted.
*
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should also document the case where this is true: if (fej9->isMethodHandleExpectedType(comp(), mhIndex, desiredMTIndex)), where the asTypeCache is not involved but does result in transformation.

* \param tt
* The treetop of the call node
* \param node
* The call node representing the call to java/lang/invoke/Invokers.checkGenericType
*/
void process_java_lang_invoke_Invokers_checkGenericType(TR::TreeTop *tt, TR::Node *node);

private:
int32_t _numLocals; // Number of parms, autos and temps
ObjectInfo *_currentObjectInfo; // Object info for current block being processed
Expand Down