MethodHandles for Kawa and other functional languages?

Rémi Forax forax at univ-mlv.fr
Mon Oct 11 00:41:47 PDT 2010


  Le 11/10/2010 02:21, Per Bothner a écrit :
> On 10/10/2010 02:20 PM, Rémi Forax wrote:
>> guardWithTest is the missing piece.
>> It should be something like that:
>> ...
> Thanks!  This is somewhat tricky - it will take a bit to grok it
> property.

And I think I've forgotten to drop the first argument of the target
and to initialize the fallback.

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

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

      MethodHandle fallback = callsite.getTarget();
      // chain to create a tree of decision or
      // link just to default fallback
      MethodHandle fallback = MethodHandles.collectArguments(
         MethodHandles.insertArguments(#fallback, 0, callsite),
         type);

      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 ?
> That is the question - how much would a language like Kawa
> benefit from using invokedynamic - or MethodHandles in some
> other way.\?  Some potential benefits:
>
> * Reduce boxing - e.g passing an int to an unknown function
> which in turns expects an int.
> * A better implementation of "multi-methods" - which are not
> standard Scheme and probably not very common, except for a few
> built-ins.
>
> What I suspect might be the biggest benefit:
>
> * Less indirection, allowing potential other VM optimizations,
> like inlining.  Currently, a user-defined Procedure is an
> object that references a ModuleBody and an index.  Appying
> the procedure is implemented by calling a handler method in
> the ModuleBody.  The handler method does a switch on the
> index, then does some argument type checking/conversion, then
> calls the static method that does the actual work.  I'm
> guessing this indirection makes it difficult for the VM to
> optimize such calls, but using invokedynamic would help.

With method handles, you can remove the dispatch handler in ModuleBody.
The idea is to have no megamorphic dispatch code between the callsite 
and the
target method. Said differently, your runtime should have neither 
conversions
nor signature polymorphism tricks.

> Bu it is also possible I might get more bang-for-the-effort
> by focusing on, say, arithmetic.  Rather than try to optimize
> calling unknown procedures it might be more worthwhile to
> concentrate on calling known procedures with unknown argument
> types, like + when the operands don't have type specifiers.

Calling an unknown procedure is a lambda call, so your Procedure should 
store
a MethodHandle. Applying a procedure is equivalent to:
procedure.asMethodHandle().invokeGeneric(args...)

For arithmetic, you should use invokedynamic.
a + 1 is invokedynamic (Object,int)
a + b is invokedynamic (Object,Object)
Both use the same bootstrap method, let say a is an Integer and b a Double.

the first time a + 1 is called, the boostrap method install a fallback 
method that will
reconfigure the target depending on the actual type of the call.
So the fallback see that the first argument is dynamic
but not the second one. Because a is an integer, it installs a guardWith 
test like this:
if (a instanceof Integer) plus((Integer)a, 1) else fallback
The instanceof test is done by creating a MethodHandle on 
Class.isInstance() +
an insertArgument with Integer.class.
The cast to Integer and the unboxing is done by convertArguments.
So the whole code is generic, you only have to provide something that 
say that
a call with Integer + int will call plus(int + int).

for a + b, both are dynamic, so the test method should test the class of 
a and
the class of b. Like for a + 1, convertArguments handles conversions.

In PHP.reboot, the code that handle arithmetic is starts here
http://code.google.com/p/phpreboot/source/browse/trunk/phpreboot/src/com/googlecode/phpreboot/runtime/RT.java#1184
And the slow path (fallback) is here:
http://code.google.com/p/phpreboot/source/browse/trunk/phpreboot/src/com/googlecode/phpreboot/runtime/RT.java#1377

I'm sorry, the code is a little obfuscated because PHP.reboot allows + 
between strings like Java and
the same code also deals with comparison operators (<, >, <=, >=).
All the magic to match a +(Integer,Double) at callsite to a 
plus(double,double)
is done by OpBehavior.getMethodHandle() which is overriden depending on 
the kind of operators.

cheers,
Rémi



More information about the mlvm-dev mailing list