News from the MOP front

2 views
Skip to first unread message

Attila Szegedi

unread,
Jul 28, 2009, 6:33:21 PM7/28/09
to jvm-la...@googlegroups.com
Hi folks,

I'm on vacation this and next week, which in practical terms means
that I get to hack on things I love to hack on. So, I'm now working on
the MOP equivalent that relies on invokedynamic. Both because I have
time for it and also because I'm terrified I won't have anything
interesting to report for this year's JVM Language Summit :-).

I did encounter something that at first seemed like a problem, but now
actually seems like an opportunity, and would like a confirmation I'm
correct (or wrong, as the case may be).

So, sometime in the past, bootstrap method signature was changed from
"Object bootstrap(CallSite callSite, Object receiver, Object...
arguments) to "CallSite bootstrap(Object caller, String methodName,
MethodType type)". I was actually a bit terrified by this at first.
See, the old signature actually allowed me to find the most suitable
method to link based on the receiver (is it a POJO? Link to a POJO
method handle? Is it a Ruby object? Link to a Ruby MH. Is it a Jython
object? Link to a Jython MH, and so on). It basically allowed for a
monomorphic inline cache if the final MH was also set as the target,
actually composed in an appropriate guardWithTest() where test was
"receiver instanceof x" and fallback was a MH that redid the whole
linking process.

Now I'm faced with the fact that I have none of this with the new
bootstrap method signature, as all I have is the MethodType, which
reflects the static types of arguments to a particular invokedynamic
call site.

Consider for a moment a scripting language that compiles to bytecode
with invokedynamic. In quite a lot of cases, the static types of all
arguments will simply be java.lang.Object, 'cause we can't know at the
time we emit the bytecode what will the argument types be. Okay, we
might be able - depending on the language - to discern a boolean here
and a double there as the argument types, but that's pretty much as
far as it gets. That's not much help in dispatch scheme that relies on
receiver type.

Then I realized that this actually made the bootstrap protocol much
more flexible, and it should actually be viewed as an opportunity.
Namely, my top-level composite "Dynamic Linker" (which is more or less
what the MOP should be named with invokedynamic, or rather we could
say that the DL is the means of implementing a MOP) could install a
general-purpose MH that will intercept the first call, then dispatch
to the appropriate language-specific linker based on the type of
"this", and said linker (JRuby, Jython, Rhino, or POJO) then replaces
the general-purpose MH as the target of the call site with a language-
specific one plus a guard.

Well, okay, I could go this direction and not even feel particularly
bad about it, except that if I did this, then I'd force all languages
into only having a monomorphic inline cache. Wait, you might ask,
didn't the old signature of bootstrap methods actually force that
anyway? Well, yes, it did, and that's exactly my point - since the new
signature allows more flexibility in linkage, wouldn't it be blunt if
I now took it away in an inter-language interop protocol? Actually,
with this interop protocol, I could mandate CallSite subclasses that
have any inline caching strategy - strictly monomorphic, monomorphic
escalating into megamorphic, monomorphic evolving into polymorphic,
then escalating into megamorphic, and so on. But no matter how I slice
it it looks to me like the top-level dynamic linker that composes
multiple language-specific linkers will impose inline caching strategy
on the individual language linkers.

I was entertaining the idea that it might be possible to go to the
other extreme, and have the master linker get out of the way instead.
Namely, it'd still install the general-purpose MH, but then let the
language-specific linker (selected by the type of the actual receiver
on the first call) install whatever call site it wishes, that can
follow any inline caching strategy. So, if I have two language
runtimes in my JVM, A and B, and A creates MIC call sites, and B
creates PIC call sites, then the inline caching strategy of the call
site will be determined by whichever object is the receiver of the
first call at the site - if the object came from runtime A, it's a
MIC; if it came from B, it's a PIC. Except, this wouldn't really work,
as a PIC call site would actually be defeated by a later linker that
installs a MIC. And anyway, we can't recreate the call site, we can
just relink its target once it's created for the first time. So the
behaviour would be very nondeterministic, and I don't like that, and
to boot, this might actually not be possible under current operational
semantics of dynamic call sites in the JVM.

Either that, or different runtimes would need to know about a finite
number of available call site types that are part of the protocol.

Which actually brings a third option to the table - have few
predefined inline caching strategies as part of the linker protocol,
that all language runtimes would know about and be able to respect
without stepping on each other's toes. Yes, it's a compromise, and at
the moment and don't even see how viable it is, or what would it look
like in technical implementation details, but it might work. It's also
a good question who gets to decide which call site follows which
caching strategy (gut feeling would be to say it's the language
runtime of the caller class, of course).

Or for now, just accept that a framework for inter-language linking
will force the participating languages' linkages into a single inline
caching strategy (monomoprhic, or optionally, monomorphic escalating
to megamorphic). Does that sound acceptable at least to "big" dynalang
stakeholders out there (JRuby, Jython, I'm looking at you. Rhino has
no invokedynamic yet, but if it did, I could definitely live with a
MIC). Do I worry too much about this?

Hope this all made sense :-)

Attila.

--
twitter: http://twitter.com/szegedi
weblog: http://constc.blogspot.com

Rémi Forax

unread,
Aug 1, 2009, 10:01:21 AM8/1/09
to jvm-la...@googlegroups.com

Hi Attila,
in my opinion a MOP (Meta Object Protocol) imposes
that all receiver should have a method like getMOP()
that returns an object implementing the following interface:

public interface MOP {
// return an (Object)Object
public MethodHandle getMember(String member);
// returns an (Object,String)Object
public MethodHandle getMember();

// returns an (Object)Object
public MethodHandle getIndex(int[] index);
// returns an (Object,int...)Object
// i.e if indexDimension=2 returns (Object,int, int)Object
public MethodHandle getIndex(int indexDimension);

// returns an (Object)Object
public MethodHandle convertTo(Class<?> anotherType);
// returns an (Object,Class<?>)Object i.e a kind of dynamic cast
public MethodHandle convertTo();

// returns a MethodHandle such as mh.type == methodType
public MethodHandle call(MethodType methodType);
// returns a (Object, Object[])Object
public MethodHandle call();

// retruns a MethodHandle such as mh.type == methodType
public MethodHandle methodCall(String name, MethodType methodType);
// returns a (Object, Object[])Object
public MethodHandle methodCall(String name);

//etc.
}

All methods are paired because the former knows statically some
informations by example for getMember, getMember(String) is used
if the name of the member is statically known and the later getMember()
is used
if the name of the member is only given at runtime.

a.m => getMember("m")
a["m"] => getMember()

So the MOP linker is responsible for calling the MOP object corresponding to
the receiver using any cache it wants: MIC, PIC, etc.

Given that, we will able to see how to do a better invalidation, i.e
if a MOP returns a method handle which is a guard,
and if this guard detects that an invariant of the runtime isn't an
invariant
anymore, it will want to replace the current method handle by another one.
A real MOP should provide a way to do that.

A simple way to achieve that is to take another parameter providing
a way to invalidate a method handle and
a way to get the last method handle used (the one invalidated) if it exists.

public interface MOP {
// return an (Object)Object
public MethodHandle getMember(String member, MOPControl control);
// returns an (Object,String)Object
public MethodHandle getMember(MOPControl control);
...
}

public interface MOPControl {
public MethodHandle getInvalidatorFallback();
public MethodHandle getInvalidatedMethodHandle();
}

In that case, it will be used like this:

public class MyLanguageMOP implements MOP {
public MethodHandle getMember(String member, MOPControl control) {
// control.getInvalidatedMethodHandle() returns the last method handle
// used or null, here I don't care but it will be usefull to
implement PIC

// this method handle will call MOP.getMember with the method handle
// retruns by this method in case of invalidation
MethodHandle invalidator = control.getInvalidatorFallback();

// to get a field my language provide a method Object getField(String)
return MethodHandles.guardWithTest(INVARIANT_TEST, GET_FIELD,
invalidator);
}

private static final MethodHandle GET_FIELD =

MethodHandles.publicLookup().findVirtual(MyLanguageBaseObject.class,
"getField",
MethodType.make(Object.class, String.class));

private static final MethodHandle INVARIANT_TEST = ...
}


There is also a case where the receiver doesn't have a method getMOP(),
in that case the MOP can consider it has a POJO and use a special POJOMOP
object for that. Or perhaps it can try to inject an interface with the
method
getMOP() when interface injection will work.

What do you think about that ?

cheers,
Rémi

Attila Szegedi

unread,
Aug 2, 2009, 5:41:24 AM8/2/09
to jvm-la...@googlegroups.com
Hi Rémi,

thanks for the input. My idea is somewhat different. I no longer think
of any kind of MOP interface. Rather, I'm thinking in terms of a
linker framework that recognizes some special method names (I
discussed this with John Rose last year at the lang summit, and I
think his thinking is along the same lines). The way it'd work would be:

Source code: obj.foo
Bytecode:
ALOAD 2 (or wherever "obj" is)
INVOKEDYNAMIC "dyn:getprop:foo"(Ljava/lang/Object;)Ljava/lang/Object;

The linker framework is such that it has a master linker that contains
sub-linkers for all available language runtimes on the classpath + has
a fallback POJO linker. It first links the call site to a "relink"
method that queries every sub-linker for a resolution of the name with
particular arguments. These sub-linkers return an invocation MH + a
guard MH when they recognize the receiver and the master linker then
installs a guardWithTest(guard, invocation, relink) - where "relink"
is a method handle to the same method doing the relinking, so it
repeats the linking on guard failure (that is, then it'll again go
around all the language sub-linkers).

I actually implemented that over this week; you can go to <https://sourceforge.net/projects/dynalang/develop
> and pull the source code from SVN, specifically all the work is in
the "invoke" module. The linker framework, complete with autodiscovery
of available language linkers and the master linker implementation
(DynamicLinkerImpl) is complete. There's an embryonic POJO linker,
it's quite unfinished for now, really just a proof-of-concept, but
there's already a JUnit test in testsrc that dispatches a dyn:getprop
call to two different classes using this embryonic POJO linker; you
can use it as a demo example of where's this heading.

Check out the README.txt for details: <http://dynalang.svn.sourceforge.net/viewvc/dynalang/trunk/invoke/README.txt?revision=159&view=markup
>

Of course, none of this is set in stone, but I think this is the
generic direction I want to take it. I was thinking a bit about the
possibility of a PIC with the current CallSite architecture, and I'm
coming to the realization that I don't really see it... The fact of
the matter is, CallSite can have only one method linked at a time, and
this quite predisposes it for a MIC. Also, the guard MH is opaque -
you can just invoke it, but can't analyze it, so it's impossible to do
smart dispatching optimizations for a PIC. The only PIC I can imagine
is having nested guardWithTest() - newly linked guardWithTest() MH
that has the previously likned guardWithTest() MH as its fallback,
creating a sequentially dispatchig PIC; if there's interest in that,
it's actually easy to modify my master linker to provide several
common caching strategies, or even make it pluggable. As each language
runtime would create its own DynamicLinker instance, it is okay for it
to customize its caching. Hm... I might actually just go and do that.

So anyway, talk is cheap (and less exact!) compared to working code,
so why don't you look at the "invoke" module in SVN above?

Interface injection for getMOP() could also work, but my preferred
approach are MOPs disguised as linkers responding to hierarchical
method names (separated with colons), and a master linker that ties
them together.

Attila.

Rémi Forax

unread,
Aug 2, 2009, 10:28:47 AM8/2/09
to jvm-la...@googlegroups.com
Le 02/08/2009 11:41, Attila Szegedi a écrit :
> Hi Rémi,
>
> thanks for the input. My idea is somewhat different. I no longer think
> of any kind of MOP interface. Rather, I'm thinking in terms of a
> linker framework that recognizes some special method names (I
> discussed this with John Rose last year at the lang summit, and I
> think his thinking is along the same lines). The way it'd work would be:
>
> Source code: obj.foo
> Bytecode:
> ALOAD 2 (or wherever "obj" is)
> INVOKEDYNAMIC "dyn:getprop:foo"(Ljava/lang/Object;)Ljava/lang/Object;
>
> The linker framework is such that it has a master linker that contains
> sub-linkers for all available language runtimes on the classpath + has
> a fallback POJO linker. It first links the call site to a "relink"
> method that queries every sub-linker for a resolution of the name with
> particular arguments. These sub-linkers return an invocation MH + a
> guard MH when they recognize the receiver and the master linker then
> installs a guardWithTest(guard, invocation, relink) - where "relink"
> is a method handle to the same method doing the relinking, so it
> repeats the linking on guard failure (that is, then it'll again go
> around all the language sub-linkers).
>

Ok, having different linkers that are able to say if an object
belong to it or not is better than having a method getMOP().

[...]
> Attila.
>

I've quickly browse the source code.

I've found a small error in
CompositeTypeBasedGuardingDynamicLinker#addTypeBased,
case 1 should be:

case 1: {
llinkers.addAll(tblinkers);
tblinkers.clear();
break;
}


I think you can simplify drastically the way you
find a linker for a given object.

The JSR292 already recognizes that a class is responsible
to register itself to handle all invokedynamic
of all methods of that class.

I think you can follow exactly the same way and says
that a class should register its own linker.
More that one class can have the same linker and
the way linkers are managed must be let to the dyn language
runtime.

class ADynamicClass {
static {
MOPLinkage.registerLinker(ADynamicClass.class, MyDynamicLanguageRuntime.LINKER);
}
}
// with
classMyDynamicLanguageRuntime {
public static final LinkerLINKER = ...
}

registerLinker will do two things :
- use Linkage.registerBootstrapMethod with the MOP bootstrap method
- register that the linker should be used for all receivers of class ADynamicClass.

Rémi

Attila Szegedi

unread,
Aug 2, 2009, 3:23:09 PM8/2/09
to jvm-la...@googlegroups.com
On 2009.08.02., at 16:28, Rémi Forax wrote:

> I've quickly browse the source code.
>
> I've found a small error in
> CompositeTypeBasedGuardingDynamicLinker#addTypeBased,
> case 1 should be:
>
> case 1: {
> llinkers.addAll(tblinkers);
> tblinkers.clear();
> break;
> }

Thanks. How on Earth did you spot that?!

> I think you can simplify drastically the way you
> find a linker for a given object.
>
> The JSR292 already recognizes that a class is responsible
> to register itself to handle all invokedynamic
> of all methods of that class.
>
> I think you can follow exactly the same way and says
> that a class should register its own linker.
> More that one class can have the same linker and
> the way linkers are managed must be let to the dyn language
> runtime.
>
> class ADynamicClass {
> static {
> MOPLinkage.registerLinker(ADynamicClass.class,
> MyDynamicLanguageRuntime.LINKER);
> }
> }
> // with
> classMyDynamicLanguageRuntime {
> public static final LinkerLINKER = ...
> }
>
> registerLinker will do two things :
> - use Linkage.registerBootstrapMethod with the MOP bootstrap method
> - register that the linker should be used for all receivers of class
> ADynamicClass.

Arguably, this could be done. I do have several reasons for choosing
the approach I chose (JAR service mechanism), namely:
1. it's less complex for the developer of the language runtime;
instead of including code to associate a linker with a class in every
class, they just need to declare it in a file
2. not every linker needs to be class-based (although I expect the
majority will be)
3. 3rd party users of language runtimes can create implementations of
language-specific classes; i.e. you can have your own class
"implements IRubyObject" and have JRuby use it as a native. With my
approach, a theoretical RubyLinker would handle that class too, with
class-based registering you'd need to have the class register itself
explicitly.

And thanks for thinking this along with me. (I read on your blog
you're on vacation too, right? Seems like most productive periods for
working on cool stuff :-) )

Attila.

> Rémi
>

Attila Szegedi

unread,
Aug 2, 2009, 6:39:56 PM8/2/09
to Attila Szegedi, jvm-la...@googlegroups.com
On 2009.07.29., at 0:33, Attila Szegedi wrote:

> Well, okay, I could go this direction and not even feel particularly
> bad about it, except that if I did this, then I'd force all
> languages into only having a monomorphic inline cache.

Okay, I solved this. In keeping with the principle that the whole
concern of inline caching should be owned by the call site, not the
linker, I reduced the linker to a resolver of the method handle and
the guard. I introduced a special CallSite abstract subclass named
"RelinkableCallSite" that receives them and contains an abstract
method responsible for actual linking and/or caching; I also provided
a concrete "MonomorphicCallSite" subclass of "RelinkableCallSite" that
implements a MIC. Since it's the language runtime that creates the
call site anyway, it can choose to use whatever call site subclass it
wishes - if its designer is unhappy with the MonomorphicCallSite I
provided, he's free to write his own with whatever inline caching
strategy he wants.

I'm quite happy with how it turned out; I avoided placing constraints
on language runtime implementations where none were needed. New code
available under <https://sourceforge.net/projects/dynalang/develop>,
with README being the most up-to-date document at <http://dynalang.svn.sourceforge.net/viewvc/dynalang/trunk/invoke/README.txt?revision=159&view=markup
> if you wish to take a look. I'm off to bed now :-)

Attila.

Rémi Forax

unread,
Aug 2, 2009, 7:01:06 PM8/2/09
to jvm-la...@googlegroups.com, Attila Szegedi
Le 03/08/2009 00:39, Attila Szegedi a écrit :
On 2009.07.29., at 0:33, Attila Szegedi wrote:

  
Well, okay, I could go this direction and not even feel particularly  
bad about it, except that if I did this, then I'd force all  
languages into only having a monomorphic inline cache.
    
Okay, I solved this. In keeping with the principle that the whole  
concern of inline caching should be owned by the call site, not the  
linker, I reduced the linker to a resolver of the method handle and  
the guard. I introduced a special CallSite abstract subclass named  
"RelinkableCallSite" that receives them and contains an abstract  
method responsible for actual linking and/or caching; I also provided  
a concrete "MonomorphicCallSite" subclass of "RelinkableCallSite" that  
implements a MIC. Since it's the language runtime that creates the  
call site anyway, it can choose to use whatever call site subclass it  
wishes - if its designer is unhappy with the MonomorphicCallSite I  
provided, he's free to write his own with whatever inline caching  
strategy he wants.
  

I think that the MOP runtime should manage the callsite
not the language runtime because another language runtime
will want to insert information into that call site too.

The MOP runtime should manage the JSR292 callsite and
provide a kind of local callsite for each linker i.e
each language should see its part of the method handle
tree and be able to modify it without destroying/disturbing
all other parts.

I'm quite happy with how it turned out; I avoided placing constraints  
on language runtime implementations where none were needed. New code  
available under <https://sourceforge.net/projects/dynalang/develop>,  
with README being the most up-to-date document at <http://dynalang.svn.sourceforge.net/viewvc/dynalang/trunk/invoke/README.txt?revision=159&view=markup 
 > if you wish to take a look. I'm off to bed now :-)
  

I have to go to bed too :)

Attila.
  

Rémi

Rémi Forax

unread,
Aug 2, 2009, 7:01:35 PM8/2/09
to jvm-la...@googlegroups.com
Le 02/08/2009 21:23, Attila Szegedi a écrit :
On 2009.08.02., at 16:28, Rémi Forax wrote:

  
I've quickly browse the source code.

I've found a small error in
CompositeTypeBasedGuardingDynamicLinker#addTypeBased,
case 1 should be:

case  1: {
  llinkers.addAll(tblinkers);
  tblinkers.clear();
  break;
}
    
Thanks. How on Earth did you spot that?!
  

:)
Professional alteration. I read lot of code produced by students.
Don't agree. I love the approach
"all is in the bytecode"


2. not every linker needs to be class-based (although I expect the  
majority will be)
  

If it is not class based,
One could see that as class-based linker than is able to delegate
its operation to the receiver object.


the linker has to be stored in the object
all other approaches will not scale.



Trying to optimize something that will be stored in a cache



3. 3rd party users of language runtimes can create implementations of  
language-specific classes; i.e. you can have your own class  
"implements IRubyObject" and have JRuby use it as a native. With my  
approach, a theoretical RubyLinker would handle that class too, with  
class-based registering you'd need to have the class register itself  
explicitly.

  



And thanks for thinking this along with me. (I read on your blog  
you're on vacation too, right? Seems like most productive periods for  
working on cool stuff :-) )
  

I am in vacation close to the sea, so I'm not sure that this
is my most productive period :)

Attila.
  
Rémi
Reply all
Reply to author
Forward
0 new messages