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>

--
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-languages+unsubscr...@googlegroups.com.
To post to this group, send email to jvm-languages@googlegroups.com.
Visit this group at http://groups.google.com/group/jvm-languages.
For more options, visit https://groups.google.com/groups/opt_out.

--
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-languages+unsubscr...@googlegroups.com.
To post to this group, send email to jvm-languages@googlegroups.com.
Visit this group at http://groups.google.com/group/jvm-languages.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to