MethodHandle usage for interpreters

154 views
Skip to first unread message

James Mitchell

unread,
Dec 27, 2013, 1:39:15 AM12/27/13
to jvm-la...@googlegroups.com
Hi there,

I sent a question on MethodHandle use to the regular Sun/Oracle java forum and didn't get any responses, and I'm wondering: is this forum appropriate to interpreter-oriented questions for use of the Method Handle Java APIs, or is this strictly for bytecode-level stuff?

thanks in advance,

James


Charles Oliver Nutter

unread,
Dec 27, 2013, 5:00:55 AM12/27/13
to JVM Languages

Definitely appropriate! Please repost!

- Charlie (mobile)

--
You received this message because you are subscribed to the Google Groups "JVM Languages" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jvm-language...@googlegroups.com.
To post to this group, send email to jvm-la...@googlegroups.com.
Visit this group at http://groups.google.com/group/jvm-languages.
For more options, visit https://groups.google.com/groups/opt_out.

Martijn Verburg

unread,
Dec 27, 2013, 4:54:40 AM12/27/13
to jvm-la...@googlegroups.com
Hi James,

Hopefully Charlie or someone else qualified can help, but this list is usually pretty quiet.  When you say Sun/Oracle forum, did you mean the OpenJDK mailing lists or the actual Java forums?

Cheers,
Martijn


James Mitchell

unread,
Dec 27, 2013, 12:28:46 PM12/27/13
to jvm-la...@googlegroups.com
On Friday, December 27, 2013 1:54:40 AM UTC-8, Martijn Verburg wrote:
Hi James,

Hopefully Charlie or someone else qualified can help, but this list is usually pretty quiet.  When you say Sun/Oracle forum, did you mean the OpenJDK mailing lists or the actual Java forums?
<snip>

It was on the Java forums.  My original post was....

MethodHandle combinators: simulating activation-records & ANF
This question is Not Answered.

JimDesu Dec 20, 2013 7:05 AMHi there,
 
I'm working my way through understanding Call Sites & Method Handles for some interpreter work I'm considering, and everything looks very sensible, except for argument marshalling...  I'd like to be able to have a MethodHandle combinator that takes a sequence of MethodHandles and, for each one, an array of indices into an Object[], such that the MethodHandle objects are executed sequentially, with the values indicated passed in as each MethodHandle's argument list --> the end result of the combinator being a MethodHandle accepting the Object[] as an input and returning it as an output.
 
I could write a method that did something analogous, calling each MethodHandle in sequence, but I'd like to have the end result being the generation of aload or aaload instructions to marshal arguments, not the (much more expensive) creation, population & spreading of new Object[] instances per MethodHandle call.
 
Can y'all recommend an approach that'll lead to optimal performance vis-a-vis the design philosophy behind the invoke package?  I'm still a newbie here and am probably missing something, but so far I'm missing it....
 
thanks,
 
James
Thanks y'all!

// James


Remi Forax

unread,
Dec 27, 2013, 12:36:20 PM12/27/13
to jvm-la...@googlegroups.com
I'm not sure to fully understand what you want to do,
anyway, a combination of spreadArguments + filterArguments +
collectArguments should do something near what you want.
spread transform an array into several arguments, filter allow you to
apply different methodhandles for each argument and collect is the dual
of spread so it takes several arguments and collect them to one array.

cheers,
R�mi

On 12/27/2013 06:28 PM, James Mitchell wrote:
> On Friday, December 27, 2013 1:54:40 AM UTC-8, Martijn Verburg wrote:
>
> Hi James,
>
> Hopefully Charlie or someone else qualified can help, but this
> list is usually pretty quiet. When you say Sun/Oracle forum, did
> you mean the OpenJDK mailing lists or the actual Java forums?
>
> <snip>
>
> It was on the Java forums. My original post was....
>
> MethodHandle combinators: simulating activation-records & ANF
> <https://community.oracle.com/message/11309077#11309077>This
> question is *Not Answered.
> *
> *JimDesu <https://community.oracle.com/people/JimDesu>* Dec 20,
> 2013 7:05 AMHi there,
>
> I'm working my way through understanding Call Sites & Method
> Handles for some interpreter work I'm considering, and everything
> looks very sensible, except for argument marshalling... I'd like
> to be able to have a MethodHandle combinator that takes a sequence
> of MethodHandles and, for each one, an array of indices into an
> Object[], such that the MethodHandle objects are executed
> sequentially, with the values indicated passed in as each
> MethodHandle's argument list --> the end result of the combinator
> being a MethodHandle accepting the Object[] as an input and
> returning it as an output.
>
> I could write a method that did something analogous, calling each
> MethodHandle in sequence, but I'd like to have the end result
> being the generation of aload or aaload instructions to marshal
> arguments, not the (much more expensive) creation, population &
> spreading of new Object[] instances per MethodHandle call.
>
> Can y'all recommend an approach that'll lead to optimal
> performance vis-a-vis the design philosophy behind the invoke
> package? I'm still a newbie here and am probably missing
> something, but so far I'm missing it....
>
> thanks,
>
> James
>
> Thanks y'all!
>
> // James
>
>

James Mitchell

unread,
Dec 27, 2013, 1:26:03 PM12/27/13
to jvm-la...@googlegroups.com
I'm wanting to use an Object[] to simulate an ANF-style activation-record, and to load arguments to MethodHandles by their indices in the Object[] (including use of the arrayElementSetter combinator as needed to set/update slots in the Object[]).  Besides wanting to use A-normal form (just because I like it's conceptual simplicity -- I'm just a hobbyist, not an actual compiler expert), I was hoping to generate code that didn't have to create, spread & collect other Object[] instances, and which would instead run each MethodHandle by marshalling their argument lists from the single Object[] instance that I passed to it.  

I looked briefly at using spread/filter to accomplish this, by having an Object[] with each index being the input Object[] that I want to use as my AR, and doing the actual work in filters, but it looked to me that this whole approach, besides seeming very inefficient, also required me to have a "top-level" MethodHandle of the same arity as the cardinality of the sequence of method handles that I wanted to execute.  And it seemed so ungainly that at that point just writing my own combinator to do that work looked more efficient... except for the creation of all the Object[] instances needed to glue it all together, which would be far less preferable than something which could generate aaload instructions instead.

Does that make more sense?  Am I barking up the wrong tree vis-a-vis method handles, and need to statically generate java or bytecode instead?  Perhaps method handles are intended for an "expression tree" approach where a new tree is constructed for each instance that's to be run/evaluated?

thanks,

James

On Friday, December 27, 2013 9:36:20 AM UTC-8, fo...@univ-mlv.fr wrote:
I'm not sure to fully understand what you want to do,
anyway,  a combination of spreadArguments + filterArguments +
collectArguments should do something near what you want.
spread transform an array into several arguments, filter allow you to
apply different methodhandles for each argument and collect is the dual
of spread so it takes several arguments and collect them to one array.

cheers,
R�mi
<snip>

Remi Forax

unread,
Dec 28, 2013, 8:21:20 AM12/28/13
to jvm-la...@googlegroups.com
On 12/27/2013 07:26 PM, James Mitchell wrote:
> I'm wanting to use an Object[] to simulate an ANF-style
> activation-record, and to load arguments to MethodHandles by their
> indices in the Object[] (including use of the arrayElementSetter
> combinator as needed to set/update slots in the Object[]). Besides
> wanting to use A-normal form (just because I like it's conceptual
> simplicity -- I'm just a hobbyist, not an actual compiler expert), I
> was hoping to generate code that didn't have to create, spread &
> collect other Object[] instances, and which would instead run each
> MethodHandle by marshalling their argument lists from the single
> Object[] instance that I passed to it.
>
> I looked briefly at using spread/filter to accomplish this, by having
> an Object[] with each index being the input Object[] that I want to
> use as my AR, and doing the actual work in filters, but it looked to
> me that this whole approach, besides seeming very inefficient, also
> required me to have a "top-level" MethodHandle of the same arity as
> the cardinality of the sequence of method handles that I wanted to
> execute. And it seemed so ungainly that at that point just writing my
> own combinator to do that work looked more efficient... except for the
> creation of all the Object[] instances needed to glue it all together,
> which would be far less preferable than something which could generate
> aaload instructions instead.

Ok, let's pretend that I have fully understand what an ANF activation
record is.
You want something that creates a method handle that takes an array of
Object,
get a value from an index, apply a fonction (a filter here) and store
the value in the same index
(we will generalize this later).

So the code is something like:
private static final MethodHandle GETTER =
MethodHandles.arrayElementGetter(Object[].class);
private static final MethodHandle SETTER =
MethodHandles.arrayElementSetter(Object[].class);

public static MethodHandle anfCombiner(int index, MethodHandle filter) {
MethodHandle getter = MethodHandles.filterReturnValue(
MethodHandles.insertArguments(GETTER, 1, index), filter);
MethodHandle setter =
MethodHandles.insertArguments(SETTER, 1, index);
MethodHandle permuted = MethodHandles.permuteArguments(setter,
MethodType.methodType(void.class, Object.class, Object[].class),
new int[] { 1, 0 });
return MethodHandles.foldArguments(permuted, getter);
}

Note the permuteArgument that swap the parameter of the setter to be in
th right order for fold.

In that case, appending "bar" to 'foo" can be written that way:

private static Object apply(Object o) {
return o.toString() + "bar";
}
public static void main(String[] args) throws Throwable {
MethodHandle apply = MethodHandles.lookup().findStatic(ANF.class,
"apply",
MethodType.methodType(Object.class, Object.class));

MethodHandle mh = anfCombiner(0, apply);
Object[] array = new Object[] { "foo" };
mh.invokeExact(array);
System.out.println(Arrays.toString(array));
}

Now, I suppose that you want something a little more powerful, with a
filter function that takes
several parameters from different indices and store the result in
another index.
From the previous code, only the way to combine the filter with the
getter has changed,
the last part of the method is the same.
Instead of using a spread here, I use permute (again) to duplicate the
array (for each argument of the filter) and then extract the value from
the array) and use the value as parameter of the filter function.

public static MethodHandle anfCombiner2(int result, MethodHandle filter,
int... params) {
MethodHandle[] getters = new MethodHandle[params.length];
for(int i = 0; i < params.length; i++) {
getters[i] = MethodHandles.insertArguments(GETTER, 1, params[i]);
}
MethodHandle filters = MethodHandles.filterArguments(filter, 0,
getters);
MethodHandle getter = MethodHandles.permuteArguments(filters,
MethodType.methodType(Object.class, Object[].class), new
int[params.length]);

MethodHandle setter =
MethodHandles.insertArguments(SETTER, 1, result);
MethodHandle permuted = MethodHandles.permuteArguments(setter,
MethodType.methodType(void.class, Object.class,
Object[].class), new int[] { 1, 0 });

return MethodHandles.foldArguments(permuted, getter);
}

In that case, the previous example can be written like this:
public static void main(String[] args) throws Throwable {
MethodHandle apply = MethodHandles.lookup().findStatic(ANF.class,
"apply",
MethodType.methodType(Object.class, Object.class));

MethodHandle mh = anfCombiner2(0, apply, 0);
Object[] array = new Object[] { "foo" };
mh.invokeExact(array);
System.out.println(Arrays.toString(array));
}

or if you want to do the sum of two values

public static Object plus(Object o, Object o2) {
return ((Integer)o).value + ((Integer)o2).value;
}

public static void main(String[] args) throws Throwable {
MethodHandle plus = MethodHandles.lookup().findStatic(ANF.class,
"plus",
MethodType.methodType(Object.class, Object.class, Object.class));

MethodHandle mh = anfCombiner2(0, plus, 0, 1);
Object[] array = new Object[] { 1, 2 };
mh.invokeExact(array);
System.out.println(array[0]);
}

also note that the method handle returned by anfCombiner and anfCominer2
returns void
and not the array, I found it was better because it allows to combine
several combiners simply by using foldArguments.

>
> Does that make more sense? Am I barking up the wrong tree vis-a-vis
> method handles, and need to statically generate java or bytecode
> instead? Perhaps method handles are intended for an "expression tree"
> approach where a new tree is constructed for each instance that's to
> be run/evaluated?

MethodHandles were not intended to be used as an intermediary language
for a whole program but just for the dynamic part of one expression that
changed at runtime.

So in term of performance, one small issue will be the boxing/unboxing
due to the fact that you use an array of Object and that I'm pretty sure
that the JIT will not be able to remove the boxing here.
The other problem is that for complex expressions you will generate a
giant method handle tree, and I'm pretty sure that at some point the JIT
will stop to compile it because it will generate too many SSA nodes.

>
> thanks,
>
> James

regards,
R�mi

>
> On Friday, December 27, 2013 9:36:20 AM UTC-8, fo...@univ-mlv.fr wrote:
>
> I'm not sure to fully understand what you want to do,
> anyway, a combination of spreadArguments + filterArguments +
> collectArguments should do something near what you want.
> spread transform an array into several arguments, filter allow you to
> apply different methodhandles for each argument and collect is the
> dual
> of spread so it takes several arguments and collect them to one
> array.
>
> cheers,
> R�mi
>
> <snip>

James Mitchell

unread,
Dec 28, 2013, 4:04:02 PM12/28/13
to jvm-la...@googlegroups.com
I won't have time to work through that until tonight when my daughter's asleep, but wanted to send a quick thank-you for the depth of the reply.  More once I've digested that.

// James

James Mitchell

unread,
Dec 30, 2013, 1:05:48 PM12/30/13
to jvm-la...@googlegroups.com
Bummer.  I'd hoped that MethodHandles would allow me to focus on the interpreter rather than having to reinvent the wheel lower down.  Doubly so, because your foldArguments combinator has the semantics I want rather than the regular function composition pattern.  Sorta like the lambda mess, I'd been really excited by what I'd thought they were doing, rather than what they were actually up to.  Oh well.

I really appreciate the time and patience answering my questions, nonetheless.  Thanks a bunch.

// James

James Mitchell

unread,
Feb 11, 2014, 4:04:00 PM2/11/14
to jvm-la...@googlegroups.com
I'd like to make a public apology to Remi Forax here.  I didn't realize at the time that I wrote my comment that you were one of the forces behind the Java 8 "SAMbda" effort, nor did I understand at the time the constraints that the nominal type system + type erasure together put on the effort.  Actually, I still don't understand those constraints, but I have the gyst now, and I'd just like to say that my comment was unfair & potentially hurtful.  I still don't understand some things about the intent behind the interaction of lambdas & method-handles, but irrespective, the term "mess" is unduly perjorative, unjustly harsh, and moderately ignorant, to boot.

James Mitchell


 
Reply all
Reply to author
Forward
0 new messages