On Jul 5, 2009, at 4:38 PM, Rémi Forax wrote:

Obviously avoiding arg boxing is key to
performance, so call paths will need to support a large number of
unboxed arguments to allow passing call protocols along cleanly.

One nice goal will be to be able to express call protocol with only one
object

I'm making progress on the Last Adapter, which I call the "flyby adapter". I think (and please help me figure this out) that it provides a reasonable way to manage very complex argument lists with little or no boxing effort.

A flyby adapter is typed (as usual) and contains two method handles, "probe" and "target". The "probe" is of type (ArgumentList)void, and the "target" is of the same type as the adapter itself. The flyby adapter packages up the incoming argument lists into an ArgumentList object, and runs the probe. After the probe returns, control is transferred to the target.

The probe is free to side-effect ArgumentList object, though it cannot change the types of any arguments. Any changes made by the probe are seen by the target.

Note that the flyby, the probe, and the target all see the same arguments; this may seem to be a limitation but it is not. In general, if it is useful for them to see somewhat different argument lists, this can be done by inserting and dropping arguments at various points.

Expressed using varargs, the flyby pattern looks something like this:

class FlyBy extends JavaMethodHandle {
  final MethodHandle probe, target;
  Object invoke(Object... argList) {
    probe.<void>invoke(argList);
    return MethodHandles.invoke(target, argList);
  }
}

The opaque interface type ArgumentList provides access to the same information as a varargs Object[] array would, plus the static type of the arguments. In fact, some platforms (those with very good unboxing optimizations) may implement it on top of varargs arrays.

The interface type is a subtype of List, and provides additional accessors:
  MethodType type()
  Object get/setReference(i)  // non-prims only
  int get/setRawInt(i)  // all prims except long, double
  long get/setRawLong(i)   // long, double prims

The List get/set accessors DTRT with respect to the static type, and bottom out in the above accessors.

The ArgumentList object is allocated on the heap, but is only a few words in size, and contains a pointer straight into the stacked argument list. The JVM takes care to null out the stack reference when the frame is taken down.

By using flyby adapters, we can write generic code that can process any argument list. We can do this without ever spinning bytecodes at runtime. This is why the current implementation throws NYI wherever bytecodes would have to be generated: If this works, I plan to use flybys instead of generated classes.

As noted above, flybys are probably most useful in conjunction with some argument adding and dropping. Particularly useful, probably, is the ability to insert zero-valued arguments before the adapter, thus giving the ArgumentList some scratch variables to work with; these values (amended to values computed by the probe) are then passed to the target.

Another useful transformation, probably, is dropping some of the original arguments just before calling the target (presumably they were only useful to the probe). These two transformation could be encoded (for example) as a trailing set of zero values to append just before calling the probe, and a leading sequence of arguments to drop just before calling the target.

The flyby is the only JVM-primitive adapter that is not completely tail-recursive, because it must push a stack frame while the probe executes. But it will guarantee that the call to the target is a tail call. This will provide a hook for building state machines and other low-level patterns.

Other adapters defined by JSR 292 which currently seem to need bytecode generation will be implemented on top of flybys. For example, collectArguments can be built on a flyby which performs all the boxing and stores it into a trailing argument.

A completely non-allocating version of the flyby adapter is useful; this would avoid creating the ArgumentList, and pass the whole argument list (spread out as usual) to the probe. The probe would in turn return a new value to be appended to the argument list. This is quite powerful if the first argument is a method handle; what you get is a continuation-passing state machine, where the rest of the arguments are "along for the ride"; they might be a pointer to an abstract machine state which itself is mutable. This suggests that flybys have two inherent degrees of freedom (beyond their method type): Whether the arguments are collected or not going into the probe, and whether a return value from the probe is inserted or not into the target arguments. The add-zero/drop degrees of freedom mentioned above are not inherent to the pattern.

One last point: A key motivation for flybys is running method handles on very small platforms where dynamic bytecode generation is difficult or impossible.

-- John

_______________________________________________
mlvm-dev mailing list
[email protected]
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

Reply via email to