-
Notifications
You must be signed in to change notification settings - Fork 5.9k
8350607: Consolidate MethodHandles::zero into MethodHandles::constant #23706
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
LF editor spins classes
👋 Welcome back liach! A progress list of the required criteria for merging this PR into |
@liach This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 174 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. ➡️ To integrate this PR with the above commit message to the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are on the right track. Some of the details don’t look right to me, so I left comments on the PR.
I realize it is tricky to get this stuff bootstrapped, and sometimes it helps to copy initialization code to avoid hitting a common path too soon. But I think the right answer here is probably to lean hard on common paths in BMH, for both the zero and the constant cases.
Another matter: The intrinsicData thingy bothers me. Do we really need to use it? Why not just have a clean identity NF, and then call NF(mycon) in a LF?
It looks like intrinsicData is under-used, and perhaps can be removed completely. It only carries the length of a lookup table, before this PR…? If so, I’d support lazily making N different NFs for table lookup, for each table arity in N. (I.e., N names, not one name, and no side-data.) But maybe that would be equally bad.
Anyway, those are my first reactions. Thanks!
@@ -1343,7 +1343,7 @@ enum Intrinsic { | |||
ARRAY_STORE, | |||
ARRAY_LENGTH, | |||
IDENTITY, | |||
ZERO, | |||
CONSTANT, | |||
NONE // no intrinsic associated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I notice you are using the intrinsicData
property to keep track of the constant, which is a good call.
A little below here, around line 1400, there is a possible bug in the handling of intrinsicData
.
Possible fix:
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
@@ -1409,7 +1409,8 @@ static MethodHandle makeIntrinsic(MethodHandle target, Intrinsic intrinsicName)
}
static MethodHandle makeIntrinsic(MethodHandle target, Intrinsic intrinsicName, Object intrinsicData) {
- if (intrinsicName == target.intrinsicName())
+ if (intrinsicName == target.intrinsicName() &&
+ intrinsicData == target.intrinsicData())
return target;
return new IntrinsicMethodHandle(target, intrinsicName, intrinsicData);
}
It should be added to this PR, lest constants get confused somehow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed the other use of intrinsicData
, tableSwitch
. I believe the intrinsic is actually a regression by growing the bytecode size - we should just select a MH via hash table lookup and invoke that MH, given all MHs in that list have the same type. I have removed the use of intrinsic data here and we can move on to remove it later.
I noticed that intrinsics are useful really only as part of named functions. And named functions only reuse arbitrary MHs for the invoker form. Is my understanding here correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should just select a MH via hash table lookup and invoke that MH
I had something like this in an early prototype of the tableSwitch
combinator, but it does not work, as it prevents the method handle calls for each case from being inlined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI this is being addressed in #23763
src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java
Outdated
Show resolved
Hide resolved
src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
Outdated
Show resolved
Hide resolved
I think this version should be fine: Perfstartup-LambdaNoop's |
Webrevs
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just got back home. Some comments inline, will need to run some tests and mull this over before approval.
var basicType = BasicType.basicType(type); | ||
var form = constantForm(basicType); | ||
|
||
if (type.isPrimitive()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could simplify this using Wrapper.forBasicType
; all variants should be able to use wrapper.convert
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cast is not performed if the type
is an interface; this is a behavioral disparity with the old value = ptype.cast(value);
in insertArguments
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, we'd need an explicit cast
in the OBJECT
case and that would be a bit redundant. Might still amount to a code simplification.
@@ -4881,7 +4870,8 @@ public static MethodHandle identity(Class<?> type) { | |||
*/ | |||
public static MethodHandle zero(Class<?> type) { | |||
Objects.requireNonNull(type); | |||
return type.isPrimitive() ? zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type); | |||
return type.isPrimitive() ? primitiveZero(Wrapper.forPrimitiveType(type)) | |||
: MethodHandleImpl.makeConstantReturning(type, null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if it's important but the existing impl would cache zero(Object.class)
while this new impl won't. Behaviorally neutral for any other reference type, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type == Object.class
can use the primitive path, but I am too lazy to add another of this check.
var species = SimpleMethodHandle.BMH_SPECIES.extendWith(type); | ||
var carrier = argument(0, L_TYPE).withConstraint(species); // BMH bound with data | ||
Name[] constNames = new Name[] { carrier, new Name(species.getterFunction(0), carrier) }; | ||
return LF_constant[type.ordinal()] = create(1, constNames, Kind.CONSTANT); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this caching logic should be in constantForm
, which also does the cache lookup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Putting the field write in the create
method is to help reduce the code that JIT needs to compile by reducing the getter's code size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough.
@@ -969,7 +969,7 @@ LambdaForm filterReturnForm(BasicType newType, boolean constantZero) { | |||
if (newType == V_TYPE) | |||
callFilter = null; | |||
else | |||
callFilter = new Name(constantZero(newType)); | |||
callFilter = new Name(LambdaForm.identity(newType), newType.btWrapper.zero()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could have a Name
constructor that just takes an Object
and returns it like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that is necessary - the majority of constant values like this are immediately sent to a NamedFunction, which already accepts such constants natively. The only case such a Name is necessary is for LF return values.
@@ -1343,7 +1343,7 @@ enum Intrinsic { | |||
ARRAY_STORE, | |||
ARRAY_LENGTH, | |||
IDENTITY, | |||
ZERO, | |||
CONSTANT, | |||
NONE // no intrinsic associated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should just select a MH via hash table lookup and invoke that MH
I had something like this in an early prototype of the tableSwitch
combinator, but it does not work, as it prevents the method handle calls for each case from being inlined.
@@ -39,7 +39,6 @@ | |||
public class TestDynamicRegenerateHolderClasses extends DynamicArchiveTestBase { | |||
static String CHECK_MESSAGES[] = {"java.lang.invoke.Invokers$Holder source: shared objects file (top)", | |||
"java.lang.invoke.DirectMethodHandle$Holder source: shared objects file (top)", | |||
"java.lang.invoke.DelegatingMethodHandle$Holder source: shared objects file (top)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why this change is needed. Could you please explain it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the simplification of MethodHandles.constant
, it seems invoking MethodHandles.constant
no longer need to initialize DelegatingMethodHandle
because there is no longer need to rebind some MethodHandle; anyways we are loading fewer classes now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope that the intrinsic mechanism can be further simplified, at some point. But I defer to Jorn's comments about tableswitch.
Excellent cleanups. Lots and lots of deleted code, and other code regularized. Thank you!
@@ -32,6 +32,8 @@ | |||
|
|||
/** | |||
* A method handle whose behavior is determined only by its LambdaForm. | |||
* Access to SimpleMethodHandle should ensure BoundMethodHandle is initialized | |||
* first. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you try factoring UNSAFE.ensureInit(BMH) into a static block in SimpleMethodHandle.java?
Sometimes that works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be exact, I did not encounter any initialization issue during my use of BMH/SimpleMH. We don't need ensureInit as SimpleMH already extends BMH. Maybe my concern is just red herring, as I saw a warning on BMH$Species_L that its access should go through BMH first.
var basicType = BasicType.basicType(type); | ||
var form = constantForm(basicType); | ||
|
||
if (type.isPrimitive()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, we'd need an explicit cast
in the OBJECT
case and that would be a bit redundant. Might still amount to a code simplification.
Tier 1-3 tests only have 2 unrelated failures. Thanks for the reviews. /integrate |
Going to push as commit 8ed6c1d.
Your commit was automatically rebased without conflicts. |
@@ -4900,7 +4890,7 @@ private static MethodHandle identityOrVoid(Class<?> type) { | |||
* @param type the type of the desired method handle | |||
* @return a constant method handle of the given type, which returns a default value of the given return type | |||
* @throws NullPointerException if the argument is null | |||
* @see MethodHandles#zero | |||
* @see MethodHandles#primitiveZero |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MethodHandles.primitiveZero(…)
is a private internal API.
* @see MethodHandles#primitiveZero | |
* @see MethodHandles#zero |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a general rfe for checking inaccessible links. Don't recall why thisv was even changed, most likely ide being too smart.
LF editor spins classes, this avoids the spinning overhead and should speed up non-capturing lambdas too.
There may need to be additional intrinsic work for MH combinator lf bytecode generation.
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/23706/head:pull/23706
$ git checkout pull/23706
Update a local copy of the PR:
$ git checkout pull/23706
$ git pull https://git.openjdk.org/jdk.git pull/23706/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 23706
View PR using the GUI difftool:
$ git pr show -t 23706
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/23706.diff
Using Webrev
Link to Webrev Comment