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>