MethodHandles for Kawa and other functional languages?

Rémi Forax forax at univ-mlv.fr
Sun Oct 10 14:20:36 PDT 2010


Le 10/10/2010 20:08, Per Bothner a écrit :
> So we've determined that to make use of 292 Kawa should keep its
> abstract gnu.mapping.Procedure class, but add a new method:
>     MethodHandle asMethodHandle()
>
> I mis-concluded that the compiler should therefore generate code
> to call asMethodHandle() and then the MethdHandle#invoke method.
> However, that doesn't seem like the right way to do it.
>    

Why ?

> Instead, given a Scheme call to an unknown Procedure f:
>
>     (f 3 x)
>
> the compiler should generate (the invokedynamic bytecode
> corresponding to):
>
>     InvokeDynamic.apply(f, 3, x)
>
> The minimal generic CallSite support might be:
>
>       static { Linkage.registerBootstrapMethod("kawa.lang.Dynamic",
> "linkDynamic");
>
>       private static CallSite linkDynamic(Class caller, String name,
> MethodType type) {
>            CallSite site = new CallSite(caller, name, type);
>            MethodType genericHandleType = ...;
>            MethodHandle genericHandle = MethodHandles.lookup()
>              .findStatic(Dynamic.class, "genericApply", genericHandleType);
>            site.setTarget(genericHandle);
>            return site;
>      }
>
>      public static Object genericApply(Object function, Object... args) {
>         if (function instanceof Procedure) {
>           return ((Procedure) function).applyN(args);
>         }
>         else
>           ... handle array indexing and other "pseudo-function" types ...;
>      }
>
> So this seems like it should work and handle the generic case, but
> of course there is no performance win so far.
>
> So the first thing we need is to replace genericApply with one that is
> specialized on the actual function argument.  I.e. instead of:
>
>      return ((Procedure) function).applyN(args);
>
> we do:
>
>      MethodHandle mh = ((Procedure) function).asMethodHandle();
>      site.setTarget(mh);
>
> and so we end up doing:
>
>      return mh.invoke(args);
>
> So we need to register the specialized method instead, and re-try that.
> Then we need to check future calls to see if the function has changed,
> and if so re-specialize.  I haven't been able I haven't been able to find
> a tutorial/example for that - Google is not my friend this morning.
> Any cook-books out there for how to do this?
>    

guardWithTest is the missing piece.
It should be something like that:

private static CallSite linkDynamic(Class caller, String name, 
MethodType type) {
      CallSite site = new CallSite(caller, name, type);

      MethodHandle fallback = MethodHandles.insertArguments(#fallback, 
0, site);
      MethodHandle target = MethodHandles.collectArguments(fallback, type);
      site.setTarget(target);
      return site;
}


public static boolean refTest(Object procedure1, Object procedure2) {
     return procedure1 == procedure2;
}

public static Object fallback(Callsite callsite, Object procedure, 
Object[] args) {
     MethodHandle mh = ((Procedure)procedure).asMethodHandle();

     MethodHandle test = MethodHandles.insertArguments(#refTest, 1, 
procedure);
     MethodHandle invoker = 
MethodHandles.genericInvoker(callsite.getTarget().type());
     MethodHandle target = MethodHandles.insertArgument(invoker, 0, mh);

     target = MethodHandles.guardWithTest(test, target, fallback);
     callsite.setTarget(target);

     return mh.invokeVarargs(args);
}

Perhaps you need to add some conversions with convertArguments.
But as I said, why do you want to use invokedynamic here ?

> After that I need to figure out how to do argument conversions.  The Kawa
> compiler emits in each generic "apply" method necessary type conversions
> before it calls the actual specific method, and the CallSite handler has
> to duplicate that.
>    

Rémi


More information about the mlvm-dev mailing list