Static method market not entrant?

Charles Nutter cnutter at engineyard.com
Tue Sep 7 14:35:33 PDT 2010


I've been working on JRuby performance lately and ran into a peculiar situation.

I have a static utility method in JRuby that checks whether a given
object's class is the same as the when the compiler optimized it. So
for a snippit of code like this:

def foo
  bar
end

def bar
  # whatever
end

After running for some time, the "foo" call will be compiled, the
compiler will see that the "bar" call has a cached method handle, and
it will emit both a dynamic call and a static-typed call plus guard.
The static-typed call looks like this:

    ALOAD 8
    LDC 446
    INVOKESTATIC
org/jruby/javasupport/util/RuntimeHelpers.isGenerationEqual
(Lorg/jruby/runtime/builtin/IRubyObject;I)Z
    IFNE L2
    ALOAD 8
    CHECKCAST org/jruby/RubyFixnum
    ALOAD 1
    LDC 1
    INVOKEVIRTUAL org/jruby/RubyFixnum.op_plus
(Lorg/jruby/runtime/ThreadContext;J)Lorg/jruby/runtime/builtin/IRubyObject;

And the isGenerationEqual method looks like this:

    public static boolean isGenerationEqual(IRubyObject object, int
generation) {
        return object.getMetaClass().getCacheToken() == generation;
    }

While running benchmarks, I noticed a peculiar thing happening. For
"fib", the method JITs in JRuby very quickly and is soon after JITed
by Hotspot. But later compiles cause "fib" to get deoptimized and
marked not-entrant. Around the same time, isGenerationEqual gets
marked not entrant. Unfortunately, when fib re-optimizes, it does so
without inlining the isGenerationEqual call, and I can see that where
it was inlined before, it now actually does a CALL in assembly.

Manually inlining the same bytecode everywhere isGenerationEqual would
be called does not seem to be subject to the same effect.

Any thoughts? The only theory I have is that early in optimization
Hotspot sees that the target object type (IRubyObject object in the
method def) is the same, and so it optimizes based on that. Later, as
other compiled methods start to hit this code, the tyoe of "object"
changes. But the logic behind the scenes should be identical in every
case... IRubyObject.getMetaClass() only has one final implementation
on org.jruby.RubyBasicObject, and getCacheToken() has only one final
implementation on org.jruby.RubyModule, which simply returns an int
field.

So I'm stumped why at least isGenerationEqual would not inline in all cases.

- Charlie


More information about the hotspot-compiler-dev mailing list