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