Skip to content

Fold MethodHandle.asType in Lambdaform-generated methods#23481

Merged
mpirvu merged 1 commit into
eclipse-openj9:masterfrom
matthewhall2:mh_fold_asType
Jun 16, 2026
Merged

Fold MethodHandle.asType in Lambdaform-generated methods#23481
mpirvu merged 1 commit into
eclipse-openj9:masterfrom
matthewhall2:mh_fold_asType

Conversation

@matthewhall2

@matthewhall2 matthewhall2 commented Mar 10, 2026

Copy link
Copy Markdown
Contributor

When calling originalMH.invoke(args), MH.asType is called at runtime to ensure the arguments are compatible with the original MH. A MethodType for the arguments is constructed and checked against the original MethodHandle's MethodType. This is essentially subtype compatibility.
When the types do not match exactly (but are compatible), the LambdaForm needs to be edited to perform typecasts before making the call to the target, resulting in a new MethodHandle. This can result in substantial runtime overhead.

Const refs allows us to create a reference to the resulting new MethodHandle when it is known to be constant at compile time, and to simply use that reference instead of having to call asType on each invocation of the MethodHandle.

This PR:

  • Adds recognized method for Invokers.checkGenericType (the main callsite for MH.asType).
  • Folds call to the asTypeCache if the types are compatible and the asTypeCache is non-null.
    • Adds const provenance edge from the original MethodHandle to the asTypeCache (the result of the asType) methodhandle.

(for calls like mh.invoke(args), mh is the original MethodHandle, and the result of the asType call is the target MethodHandle.)
Type checking each type in the MT in not needed. If the MT of the asTypeCache MH is the same as the MT of the target MH, we can just use the cached MH. If they are not the same, then we cannot fold since we have no way of obtaining the asType result at compile time. (we also should not even get here if they are not the same, since we shouldn't have a known-object for the target MT in that case).

@matthewhall2 matthewhall2 requested a review from dsouzai as a code owner March 10, 2026 19:53
@matthewhall2 matthewhall2 force-pushed the mh_fold_asType branch 3 times, most recently from e7b9e2f to 62f4605 Compare March 10, 2026 20:45
Comment thread runtime/compiler/env/j9method.cpp Outdated
{ x(TR::java_lang_invoke_MethodHandle_asType_instance, "asType",
"(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;") },
{ x(TR::java_lang_invoke_Invokers_checkGenericType, "checkGenericType",
"(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;") },

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 is not the right place to add this method. It is only for methods of class MethodHandle.

@matthewhall2 matthewhall2 marked this pull request as draft March 16, 2026 16:41
@matthewhall2 matthewhall2 force-pushed the mh_fold_asType branch 6 times, most recently from e63aa6b to c07d3c9 Compare March 16, 2026 20:21
@matthewhall2

Copy link
Copy Markdown
Contributor Author

thanks @nbhuiyan . I realized there is a much simpler way of checking the type compatibility. See PR description.

@matthewhall2 matthewhall2 marked this pull request as ready for review March 16, 2026 20:43
@matthewhall2 matthewhall2 requested a review from nbhuiyan March 16, 2026 20:43
Comment thread runtime/compiler/env/VMJ9.cpp Outdated
*/
TR::KnownObjectTable *knot = comp->getKnownObjectTable();
if (!knot)
return false;

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.

Return 0 here similar to the return 0 at the end, as the return type of the function is uintptr_t.

Comment thread runtime/compiler/env/VMJ9.cpp Outdated

/* 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.
* TODO: if we find that the normal asTypeCache doesn't get many hits, add (careful) checks for the soft cache.å

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.

Looks like some unintended character at the end of this line.

Also, I prefer not to add more TODO labels. The comment can be updated to describe the TODO as something to consider in the future.

return cachedMH;
}
return 0;
}

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.

*/
void process_java_lang_invoke_Invokers_checkVarHandleGenericType(TR::TreeTop *tt, TR::Node *node);

void process_java_lang_invoke_MethodHandle_asType(TR::TreeTop *tt, TR::Node *node);

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.

Add doxygen documentation

#endif // TR_ALLOW_NON_CONST_KNOWN_OBJECTS
}

void TR_MethodHandleTransformer::process_java_lang_invoke_MethodHandle_asType(TR::TreeTop *tt, TR::Node *node)

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 function should check if const resfs are enabled, without which it should abort.

J9::ConstProvenanceGraph *cpg = comp()->constProvenanceGraph();
// cpg->addEdge(cpg->knownObject(idx), clazz);
auto knot = comp()->getKnownObjectTable();
bool transformed = false;

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 will trigger unused variable warnings.

logprintf(trace(), comp()->log(), "MethodHandle is obj%d\n", mhIndex);
logprintf(trace(), comp()->log(), "MethodType is obj%d\n", desiredMTIndex);
J9::ConstProvenanceGraph *cpg = comp()->constProvenanceGraph();
// cpg->addEdge(cpg->knownObject(idx), clazz);

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.

We should not be adding commented out code.

uintptr_t convertedMH = fej9->getConvertedMethodhandle(comp(), mhIndex, desiredMTIndex);
if (0 != convertedMH) {
logprintf(trace(), comp()->log(), "Method types are the compatible%d\n");
TR::KnownObjectTable::Index convertedMHIndex = knot->getOrCreateIndex(convertedMH);

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.

Same issue as in InterpreterEmulator. It is not safe to call getOrCreateIndex with a raw pointer without VM access being held for the whole duration.

void TR_MethodHandleTransformer::process_java_lang_invoke_MethodHandle_asType(TR::TreeTop *tt, TR::Node *node)
{
auto mhNode = node->getChild(0);
auto desiredMTNode = node->getChild(1);

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.

Better to use node->getArgument instead of node->getChild when trying to access specific args of a call node, as otherwise we run into the risk of unintentionally accessing the receiver instead of the first arg. In this case, using getChild is not incorrect, but should ideally use getArgument.

logprintf(trace(), comp()->log(), "Exact compatibilty check failed - checking subtype compatibility\n");
uintptr_t convertedMH = fej9->getConvertedMethodhandle(comp(), mhIndex, desiredMTIndex);
if (0 != convertedMH) {
logprintf(trace(), comp()->log(), "Method types are the compatible%d\n");

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.

Typo, and a format specifier with no args to feed it.

Comment thread runtime/compiler/env/VMJ9.cpp Outdated
uintptr_t TR_J9VMBase::getConvertedMethodhandle(TR::Compilation *comp, TR::KnownObjectTable::Index mhIndex,
TR::KnownObjectTable::Index desiredTypeIndex)
{
/* Indidivual type compatibilty checks on each type in the MT are not needed. Since we only fold

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

sp: Individual

@matthewhall2

Copy link
Copy Markdown
Contributor Author

thanks @nbhuiyan , this is ready for another review

@matthewhall2 matthewhall2 requested a review from nbhuiyan March 24, 2026 14:17

@nbhuiyan nbhuiyan left a comment

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.

In addition to these review comments, please update JITServer MINOR_NUMBER in CommunicationStream.hpp

Comment thread runtime/compiler/optimizer/MethodHandleTransformer.cpp Outdated
Comment thread runtime/compiler/optimizer/MethodHandleTransformer.cpp
Comment thread runtime/compiler/net/MessageTypes.cpp
Comment thread runtime/compiler/optimizer/MethodHandleTransformer.hpp
Comment thread runtime/compiler/optimizer/MethodHandleTransformer.hpp Outdated
Comment thread runtime/compiler/optimizer/MethodHandleTransformer.cpp Outdated
Comment thread runtime/compiler/env/VMJ9.h Outdated
Comment thread runtime/compiler/env/VMJ9.cpp Outdated
Comment thread runtime/compiler/env/VMJ9.cpp Outdated
@matthewhall2

Copy link
Copy Markdown
Contributor Author

eclipse-omr/omr#8209

@0xdaryl

0xdaryl commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Jenkins test sanity all jdk21 depends eclipse-omr/omr#8209

@matthewhall2 matthewhall2 force-pushed the mh_fold_asType branch 4 times, most recently from edbb7ea to 363a558 Compare April 22, 2026 14:29
@matthewhall2

matthewhall2 commented Apr 22, 2026

Copy link
Copy Markdown
Contributor Author

@0xdaryl the change I just pushed should fix the issue, can you run the tests again?

@0xdaryl

0xdaryl commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Jenkins test sanity all jdk21 depends eclipse-omr/omr#8209

@matthewhall2

matthewhall2 commented May 21, 2026

Copy link
Copy Markdown
Contributor Author

aarch64 failure is a jit server fail, looks like it is #14706

Z failure is an illegal instruction in the vector tests, looks unrelated

X sanity.functional failure looks related to #22758 (comment)

X sanity.openjdk is a java compile failure, unrelated #23943

@matthewhall2

matthewhall2 commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

all failures were found to be known in #23481 (comment)
does this need another rebase and test run before merging?

@mpirvu mpirvu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There are some conflicts that need to be resolved and it makes sense to rebase to test against the latest changes.
As far as I understand, this code will be exercised when const refs will be enabled. If that's the case, wouldn't be more prudent to enable const refs first and then run tests for this PR?

Comment thread runtime/compiler/env/VMJ9.cpp Outdated
@mpirvu mpirvu removed the request for review from dsouzai June 10, 2026 17:01
Adds recognized method for Invokers.checkGenericType (the callsite for
MH.asType).
Folds call to the asTypeCache if the MTs are exact same object.
- Type checking each type in the MT in not needed. If the MT of
  the asTypeCache MH is the same as the MT of the target MH, we can
just use the cache. If they are not the same, then we cannot fold as we
have no way of obtaining the asType result at compile time. (we also
should not even get here if they are not the same, since we shouldn't
have a known-object for the target MT in that case).

Signed-off-by: Matthew Hall <matthew.hall3@outlook.com>
@matthewhall2

Copy link
Copy Markdown
Contributor Author

@mpirvu

As far as I understand, this code will be exercised when const refs will be enabled. If that's the case, wouldn't be more prudent to enable const refs first and then run tests for this PR?

The OMR branch that the tests used enables const refs (eclipse-omr/omr#8209), so the tests runs are valid.
Are there remaining const ref opts that have not been merged into master that might affect this @nbhuiyan ?

@matthewhall2 matthewhall2 requested a review from mpirvu June 10, 2026 21:41
@nbhuiyan

Copy link
Copy Markdown
Member

Are there remaining const ref opts that have not been merged into master that might affect this

All constrefs-related opts have been merged into master, but not yet enabled. As far as I can tell, this change is independent of those opts being enabled.

@mpirvu

mpirvu commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Jenkins test sanity xlinux jdk25 depends eclipse-omr/omr#8209

@mpirvu

mpirvu commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

If the xlinux tests are clean, I will launch tests on the other platforms too.

@mpirvu mpirvu self-assigned this Jun 11, 2026
@matthewhall2

Copy link
Copy Markdown
Contributor Author

failures look related to #23814

@mpirvu

mpirvu commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Jenkins test sanity plinux,zlinux,alinux64,aix,win,xmac,amac jdk25 depends eclipse-omr/omr#8209

@mpirvu

mpirvu commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Jenkins test sanity.functional plinux,zlinux,alinux64,aix,win,xmac,amac jdk25 depends eclipse-omr/omr#8209

@matthewhall2

Copy link
Copy Markdown
Contributor Author

plinux fail is #24139
s390x_linux and x86-64_mac look to be stuck waiting for a machine

@mpirvu

mpirvu commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

openjdk alinux, xmac and xlinux have a vectorapi assert captured here: #23814
plinux is riddled with crashes from a known problem #24139 which should be fixed now, so we can retry.
zlinux openjdk failed jdk_lang_j9_0 due to a known problem: #22110. There are also a few vectorAPI failures without a clear explanation.

@mpirvu

mpirvu commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Since the failures that were seen have been accounted for, this PR is ready to be merged.

@mpirvu mpirvu merged commit 92c682e into eclipse-openj9:master Jun 16, 2026
16 of 28 checks passed
@github-project-automation github-project-automation Bot moved this from In progress to Done in Adopt OpenJDK MethodHandles Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Development

Successfully merging this pull request may close these issues.

4 participants