On Mar 4, 2015, at 2:47 PM, Charles Oliver Nutter <head...@headius.com> wrote:
> 
> Busy week, finally circling back to this thread...
> 
> On Wed, Feb 25, 2015 at 8:29 PM, John Rose <john.r.r...@oracle.com> wrote:
>>> * A loop handle :-)
>>> 
>>> Given a body and a test, run the body until the test is false. I'm
>>> guessing there's a good reason we don't have this already.
>> 
>> A few reasons:   1. You can code your own easily.
> 
> I can't code one that will specialize for every call path, though,
> unless I generate a new loop body for every call path.

Good point; if we had an intrinsic we could specialize this more readily, 
across more degrees of freedom.

OTOH:  What you are proposing is a workaround for a basic weakness in our 
system which we must eventually address.
There is no reason why you shouldn't be able to write a fully generic algorithm 
(generic across calling sequences) and get good performance for each distinct 
instance of the algorithm.  (Cue rants about the Loop Customization Problem.)

But, as with try/finally, if there is a common use case which can only be 
expressed in a strained fashion using existing API points, we should provide a 
more natural notation for that use case; both users and JVM will find it easier 
to DTRT.

> 2. There's no One True Loop the way there is a One True If.
>> The "run until test is false" model assumes all the real work is
>> done with side-effects, which are off-center from the MH model.
> 
> This I can appreciate. My mental model of MHs started to trend toward
> a general-purpose IR, and I believe if I had some sort of backward
> branch it could be that. But I understand if that's the wrong
> conceptual model, and I realize now that MHs are basically call stack
> adapters with a bit of forward branching thrown in.

Good; you are hitting me where it counts, in the bytecodes.

> It does feel like there's a need for better representation of
> branch-joining or phi or whatever you want to call it, though.

More details on this, please?  The tail-part of a GWT is a phi.  Or are you 
talking about phis on back-edges?

I agree we need a loop, if only because we want to compile them to (the 
equivalent of) backwards-branching bytecodes.  It's the same argument as for 
try/finally.

>> 3. A really clean looping mechanism probably needs a sprinkle
>> of tail call optimization.
>> 
>> I'm not saying that loops should never have side effects, but I
>> am saying that a loop mechanism should not mandate them.
>> 
>> Maybe this is general enough:
>> 
>>    MHs.loop(init, predicate, body)(*a)
>>    => { let i = init(*a); while (predicate(i, *a)) { i = body(i, *a); } 
>> return i; }
>> 
>> ...where the type of i depends on init, and if init returns void then you
>> have a classic side-effect-only loop.
> 
> Ahh yes, this makes sense. If it were unrolled, it would simply be a
> series of folds and drops as each iteration through the body modified
> the condition in some way. So then we just need it to work without
> unrolling.

Glad you like it.  This looks like a candidate, then.

(I wish we had a similar candidate for invokespecial/super.  That is badly 
twisted around the verifier.)

> My silly use case for this would be to emit simple expressions
> entirely as a MH chain, so we'd get the benefit of MH optimizations
> without generating our own bytecode (and with forced inlining and
> perhaps a richer semantic representation than just bytecode). It's not
> a very compelling case, of course, since I could just emit bytecode
> too.

If it's a common use case, even if you could do it with bytecode spinning, it 
is reasonable to support a MH version, to help users avoid the friction of 
switching notations between MHs and bytecodes.

> 
>>> * try/finally as a core atom of MethodHandles API.
>>> 
>>> Libraries like invokebinder provide a shortcut API To generating the
>>> large tree of handles needed for try/finally, but the JVM may not be
>>> able to optimize that tree as well as a purpose-built adapter.
>> 
>> I agree there.  We should put this in.
>> 
>>   MHs.tryFinally(target, cleanup)(*a)
>>     => { try { return target(*a); } finally { cleanup(*a); } }
>> 
>> (Even here there are non-universalities; what if the cleanup
>> wants to see the return value and/or the thrown exception?
>> Should it take those as one or two leading arguments?)
> 
> In InvokeBinder, the finally is expected to require no additional
> arguments compared to the try body, since that was the use case I
> needed.

Good to know.  We start from what we know we need.

> You bring up a good point...and perhaps the built-in JSR292
> tryFinally should take *two* handles: one for the exceptional path
> (with exception in hand) and one for the non-exceptional path (with
> return value in hand)? The exceptional path would be expected to
> return the same type as the try body or re-raise the exception. The
> non-exceptional path would be expected to return void.

Yup.  Or, the exception path could be a single MH that takes a "union" argument 
of U(normal-value, exception) and returns the expected value.  In the JVM a 
natural union is often just a 2-tuple; the catch MH would take arguments (T, X, 
...) where T is the normal return type (or null/zero in the case of exception) 
and X is the exception (or null in the case of normal return).

As Vladimir notes, maybe this is a bridge too far.  It's really the same 
question as before:  Given that you could compose this clumsily from existing 
API points, the real questions are (1) is this a common use case, and (2) would 
a direct notation make it easier for the JVM to work in terms of "natural" code?
> 
>> We now have MHs.collectArguments.  Do you want MHs.spreadArguments
>> to reverse the effect?  Or is there something else I'm missing?
> 
> I want to be able to group arguments at any position in the argument
> list. Example:
> 
> Incoming signature: (String foo, int a1, int a2, int a3, Object obj)
> Target signature: (String foo, int[] as, Object obj)
> 
> ... without permuting arguments

Yup, that is spreadArguments, position/count parameters.  The inverse would be 
collectArguments, again with position/count: Which we have.

I think we have three winners so far:

1. try/finally
2. loop
3. spreadArguments

(Still needing clarity: PICs, profiling hooks, invokespecial/super.)

>> Idea of the day:  An ASM-like library for method handles.
>> Make a MethodHandleReader which can run a visitor over the MH.
>> The ops of the visitor would be a selection of public MH operations
>> like filter, collect, spread, lookup, etc.
>> Also ASM-like, the library would have a MethodHandleWriter
>> would could be hooked up with the reader to make filters.
> 
> That would certainly cover it! I'd expect this to add:
> 
> * MethodHandleVisitor interface of some kind
> * MethodHandle#accept(MethodHandleVisitor) or similar

Yup.  As Vladimir points out, the risk is that after LF encoding we might not 
be able to recover something expressible in the public API.

> * MethodHandleType enum with all the base MH types (so we're not
> forcing all types into a static interface).

Good idea.  You mean the enum could be compatibly extended in the future?  
(Can't we do that with default methods in interfaces now?)

— John
_______________________________________________
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

Reply via email to