MethodHandles.constant not inlining?

Charles Oliver Nutter headius at headius.com
Tue Jun 7 15:38:03 PDT 2011


I'm adding more invokedynamic-based cache logic for lazily-initialized
Ruby literals. Rather than construct all literals on script load, we
have been initializing them lazily as they are first encountered. In
stock JRuby, this involves pinging a per-script array and constructing
and inserting the literal object into that array if it does not
already exist. Subsequent hits just get the array-stored value.

With invokedynamic, I am binding the literals directly into the call
site, either using a ConstantCallSite or using a MutableCallSite
rebound on first call to a MethodHandles.constant value.

This is working great, and for example a literal regexp goes from this bytecode:

   ALOAD 0
    ALOAD 1
    ALOAD 0
    INVOKEVIRTUAL ruby/__dash_e__.getByteList0 ()Lorg/jruby/util/ByteList;
    LDC 512
    INVOKEVIRTUAL ruby/__dash_e__.getRegexp0
(Lorg/jruby/runtime/ThreadContext;Lorg/jruby/util/ByteList;I)Lorg/jruby/RubyRegexp;
    ARETURN

To this bytecode:

    ALOAD 1
    INVOKEDYNAMIC getRegexp
(Lorg/jruby/runtime/ThreadContext;)Lorg/jruby/RubyRegexp;
[org/jruby/runtime/invokedynamic/InvokeDynamicSupport.getRegexpBootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
(6), "foo", "ASCII-8BIT", 512]
    ARETURN

And of course after the first call, there's no overhead other than
traversing the MutableCallSite and a dropArguments handle. Quite
slick, and especially interesting since it improves inlining budgeting
and bytecode size substantially (since it cuts out the extra
args/calls and the test + load from that per-script array).

So I figured I'd check assembly output, and I was surprised to see a
callq right where I expect to see the literal. Are
MethodHandle.constant handles not inlined right now?

Here's the assembly that results in the relevant location from a
script like "def foo; 1; end; 100_000.times { foo }":

https://gist.github.com/1013347

Some logic at the beginning and end is the invokedynamic of "foo"
inlined properly, but right smack in the middle is a callq where I
would expect to see the literal bound.

In this case, the bootstrap produces a MutableCallSite pointing at a
first-time initFixnum method. That method rebinds the call site with a
MethodHandles.constant containing the lazily-initialized RubyFixnum
object. The whole chain after the first call ends up looking like
this:

MutableCallSite => dropArgments => constant

I'm surprised to see a callq here at all. Thoughts on why?

- Charlie


More information about the mlvm-dev mailing list