112
invokedynamic Tales from the Trenches Sunday, February 3, 13

Invokedynamic: Tales from the Trenches

Embed Size (px)

DESCRIPTION

Talk on the history of JRuby and invokedynamic given at FOSDEM 2013.

Citation preview

Page 1: Invokedynamic: Tales from the Trenches

invokedynamicTales from the Trenches

Sunday, February 3, 13

Page 2: Invokedynamic: Tales from the Trenches

Me

• Charles Oliver Nutter

• Programmer my whole life

• Professional Java since 1996

[email protected], @headius

• “Full time” JRuby guy

• Sun (06-09), Engine Yard (09-12), Red Hat

Sunday, February 3, 13

Page 3: Invokedynamic: Tales from the Trenches

invokedynamic 101

• invokevirtual, invokestatic, etc

• Predefined behavior

• Based on simple VM primitives

• invokedynamic

• User-defined bytecode

• Primitives available as an IR

Sunday, February 3, 13

Page 4: Invokedynamic: Tales from the Trenches

invokedynamic 101

• Emit invokedynamic bytecode

• Name + signature + bootstrap pointer

• On first call, bootstrap called

• Name + signature + lookup context

• Return a CallSite

• Subsequent calls go through CallSite

Sunday, February 3, 13

Page 5: Invokedynamic: Tales from the Trenches

Terminology

• Invokedynamic: bytecode or JSR 292

• Call site: place in code where a call is made

• Bootstrap: method called to set up indy call site

Sunday, February 3, 13

Page 6: Invokedynamic: Tales from the Trenches

Terminology

• Method handle: endpoint (call, field, ...) or logic (branches, arg manip, ...) to wrap an endpoint handle

• Guard: logic to ensure site is bound to the proper target

• GWT: guard-with-test, branch based on user test

• SwitchPoint: on-until-off branch handle

Sunday, February 3, 13

Page 7: Invokedynamic: Tales from the Trenches

Promises

• “invokedynamic should be no slower than invokeinterface”

• “all Hotspot optimizations should apply through indy sites”

• “SwitchPoint should be ‘free’ when valid”

Sunday, February 3, 13

Page 8: Invokedynamic: Tales from the Trenches

Promises

• “JRuby plus invokedynamic will be fast”

• “The fully-working optimized OpenJDK indy impl is just around the corner”

Sunday, February 3, 13

Page 9: Invokedynamic: Tales from the Trenches

The Beginning

Sunday, February 3, 13

Page 10: Invokedynamic: Tales from the Trenches

2006

• Tom Enebo and I join Sun Microsystems

• Working on JRuby full time

• Dream come true!

• Sun officially starts promoting JVM as a multi-language platform

• Needed some work to get there...

Sunday, February 3, 13

Page 11: Invokedynamic: Tales from the Trenches

2007

• Talks start between JRuby and John Rose

• How does JRuby work?

• What do we need?

• JSR 292 restarts with John Rose leading

• Not much to see yet

Sunday, February 3, 13

Page 12: Invokedynamic: Tales from the Trenches

2008

• Early design work in JSR

• Ola Bini represented JRuby

• Few working builds

• John Rose private builds

• Multi-Language VM (Da Vinci) later on

• We banked on the future

Sunday, February 3, 13

Page 13: Invokedynamic: Tales from the Trenches

2008

• “A First Taste of InvokeDynamic”

• http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html

• 11 Sept, 2008

• ~7000 words, ~26k views

Sunday, February 3, 13

Page 14: Invokedynamic: Tales from the Trenches

JRuby 1.1.5

• First version with indy support

• Based on early proposal

• invokeinterface on java.dyn.Dynamic

• Dynamic type treated specially

• Manual bootstrap setup

• Worked...sorta

Sunday, February 3, 13

Page 15: Invokedynamic: Tales from the Trenches

java.dyn.Dynamic

public java.lang.Object doDynamicCall(java.lang.Object); Code: 0: aload_1 1: invokeinterface #3; //Method java/dyn/Dynamic.myDynamicMethod:()V 4: areturn

Sunday, February 3, 13

Page 16: Invokedynamic: Tales from the Trenches

registerBootstrapMethod

static { Linkage.registerBootstrapMethod( SimpleExample.class, MethodHandles.findStatic( DynamicInvokerThingy.class, "bootstrap", Linkage.BOOTSTRAP_METHOD_TYPE));}

Sunday, February 3, 13

Page 17: Invokedynamic: Tales from the Trenches

2009

• Continuing evolution of JSR 292

• We tracked design changes

• Performance work deferred

• We kept up the faith :-)

• First RI rev lands

Sunday, February 3, 13

Page 18: Invokedynamic: Tales from the Trenches

Running on What?

• Still no standard builds for OS X

• MLVM repo

• Based on bsd-port

• External, periodic dump of indy work

• Incubator for other projects

• Henri Gomez: openjdk-osx-build

Sunday, February 3, 13

Page 19: Invokedynamic: Tales from the Trenches

2009-04-21; 6655646: dynamic languages need dynamically linked call sitesSummary: invokedynamic instruction (JSR 292 RI)

2009-07-21; 6862576: vmIntrinsics needs cleanup in order to support JSR 292 intrinsicsSummary: remove useless lazy evaluation of intrinsics; add LAST_COMPILER_INLINE to help categorize them

2009-09-15; 6863023: need non-perm oops in code cache for JSR 292Summary: Make a special root-list for those few nmethods which might contain non-perm oops.

2009-12-16; 6829192: JSR 292 needs to support 64-bit x86Summary: changes for method handles and invokedynamic

hotspot

Sunday, February 3, 13

Page 20: Invokedynamic: Tales from the Trenches

2009-05-05; 6829144: JSR 292 JVM features need a provisional Java APISummary: JDK API and runtime (partial) for anonk, meth, indy

2009-10-21; 6891770: JSR 292 API needs initial unit testsSummary: backport working mlvm regression test to M3 implementation of JSR 292; requires jtreg 4.1

jdk

Sunday, February 3, 13

Page 21: Invokedynamic: Tales from the Trenches

Meanwhile...

• Mirah: statically-typed, Ruby-like language

• Because why not

• Local type inference

• No runtime library

• .java and .class backends

• Way more interest than I expected...

Sunday, February 3, 13

Page 22: Invokedynamic: Tales from the Trenches

Sunday, February 3, 13

Page 23: Invokedynamic: Tales from the Trenches

Sunday, February 3, 13

Page 24: Invokedynamic: Tales from the Trenches

What If This...public class Foo { private int a;

public Foo(int a) { this.a = a; }

public void show() { System.out.println(a); }}

Sunday, February 3, 13

Page 25: Invokedynamic: Tales from the Trenches

...Could Be Thisclass Foo def initialize(a) @a = a end

def show puts @a endend

Sunday, February 3, 13

Page 26: Invokedynamic: Tales from the Trenches

Mirahclass Foo def initialize(a:int) @a = a end

def show puts @a endend

Sunday, February 3, 13

Page 27: Invokedynamic: Tales from the Trenches

Mirahclass Foo def initialize(a:dynamic) @a = a end

def show puts @a endend

Sunday, February 3, 13

Page 28: Invokedynamic: Tales from the Trenches

Middle Ages

Sunday, February 3, 13

Page 29: Invokedynamic: Tales from the Trenches

2010

• Performance work starts a bit

• Back and forth with Hotspot team

• Testing JRuby, dumping assembly

• Things start to look promising

• Inlining thresholds biggest issue

Sunday, February 3, 13

Page 30: Invokedynamic: Tales from the Trenches

2010-01-05; 6829187: compiler optimizations required for JSR 2922010-01-13; 6912065: final fields in objects need to support inlining optimizations for JSR 2922010-01-29; 6917766: JSR 292 needs its own deopt handler2010-02-01; 6921352: JSR 292 needs its own deopt handler2010-02-01; 6921799: JSR 292 call sites should not be fixed-up2010-02-23; 6928839: JSR 292 typo in x86 _adapter_check_cast2010-03-08; 6932536: JSR 292 modified JDK MethodHandlesTest fails on x86_642010-03-09; 6919934: JSR 292 needs to support x86 C12010-03-16; 6934494: JSR 292 MethodHandles adapters should be generated into their own CodeBlob2010-03-17; 6934966: JSR 292 add C1 logic for saved SP over MethodHandle calls2010-03-18; 6932091: JSR 292 x86 code cleanup2010-03-31; 6939731: JSR 292 Zero build fix after 69344942010-04-29; 6829193: JSR 292 needs to support SPARC2010-05-01; 6939134: JSR 292 adjustments to method handle invocation2010-05-21; 6930772: JSR 292 needs to support SPARC C12010-05-25; 6934104: JSR 292 needs to support SPARC C22010-06-09; 6939203: JSR 292 needs method handle constants2010-07-15; 6964498: JSR 292 invokedynamic sites need local bootstrap methods2010-09-24; 6986944: JSR 292 assert(caller_nm->is_method_handle_return(caller_frame.pc())) failed: must be MH call site2010-09-29; 6987634: JSR 292 assert(start_bci() >= 0 && start_bci() < code_size()) failed: correct osr_bci argument2010-10-11; 6829194: JSR 292 needs to support compressed oops2010-10-13; 6987555: JSR 292 unboxing to a boolean value fails on big-endian SPARC2010-10-18; 6991596: JSR 292 unimplemented adapter_opt_i2i and adapter_opt_l2i on SPARC2010-10-30; 6981777: implement JSR 292 EG adjustments from summer 20102010-10-30; 6984311: JSR 292 needs optional bootstrap method parameters2010-11-04; 6997459: JSR 292 after 6994093 getting: on return to interpreted call, restored SP is corrupted2010-11-09; 6998737: JSR 292: Remove the plug guarding the use of compressed oops2010-11-30; 7001363: java/dyn/InvokeDynamic should not be a well-known class in the JVM2010-12-22; 7007377: JSR 292 MethodHandlesTest.testCastFailure fails on SPARC with -Xcomp +DeoptimizeALot

Sunday, February 3, 13

Page 31: Invokedynamic: Tales from the Trenches

2011

• Wrapping things up for FCS

• Focus on correctness

• Some JIT work landing for perf

• JRuby a primary test case

Sunday, February 3, 13

Page 32: Invokedynamic: Tales from the Trenches

Java 7 GA

• Changes within couple months of release

• Signature polymorphism

• Minor API and naming adjustments

• java.lang.invoke package name

• Performance still dismal

• JIT work not completed in time

Sunday, February 3, 13

Page 33: Invokedynamic: Tales from the Trenches

2011-04-25; 7030715: JSR 292 JRuby test/test_super_call_site_caching.rb asserts with +DoEscapeAnalysis

2011-05-10; 7042122: JSR 292: adjust various inline thresholds for JSR 292 API methods and method handle adapters

2011-05-17; 7045513: JSR 292 inlining causes crashes in methodHandleWalk.cpp

2011-05-26; 7047961: JSR 292 MethodHandleWalk swap args doesn't handle T_LONG and T_DOUBLE properly

2011-06-22; 7057587: JSR 292 - crash with jruby in test/test_respond_to.rb

Sunday, February 3, 13

Page 34: Invokedynamic: Tales from the Trenches

2011-02-11; 7013417: JSR 292 needs to support variadic method handle calls

2011-02-11; 7012650: implement JSR 292 EG adjustments through January 2010

2011-02-11; 7013730: JSR 292 reflective operations should report errors with standard exception types

2011-02-15; 7016261: JSR 292 MethodType objects should be serializable

2011-02-15; 7014755: JSR 292 member lookup interaction with security manager

2011-02-15; 7016520: JSR 292 rules for polymorphic signature processing must be in package-info

2011-03-18; 6839872: remove implementation inheritance from JSR 292 APIs

2011-03-23; 7012648: move JSR 292 to package java.lang.invoke and adjust names

2011-04-07; 6817525: turn on method handle functionality by default for JSR 292

2011-05-12; 7034977: JSR 292 MethodHandle.invokeGeneric should be renamed MethodHandle.invoke

2011-05-17; 7044892: JSR 292: API entry points sometimes throw the wrong exceptions or doesn't throw the expected one

2011-05-26; 7032323: code changes for JSR 292 EG adjustments to API, through Public Review

2011-06-03; 7051206: JSR 292 method name SwitchPoint.isValid is misleading to unwary users; should be hasBeenInvalidated

Sunday, February 3, 13

Page 35: Invokedynamic: Tales from the Trenches

INVOKEDYNAMIC vcall:foo(LThreadContext;LIRubyObject;LIRubyObject;)LIRubyObject; [ // handle kind 0x6 : INVOKESTATIC InvocationLinker.invocationBootstrap( (LMethodHandles$Lookup;LString;LMethodType;LString;I)LCallSite;) // arguments: "-e", 1]

Sunday, February 3, 13

Page 36: Invokedynamic: Tales from the Trenches

What to Do?

Sunday, February 3, 13

Page 37: Invokedynamic: Tales from the Trenches

What to Do?

• GA indy perf slower than non-indy!

Sunday, February 3, 13

Page 38: Invokedynamic: Tales from the Trenches

What to Do?

• GA indy perf slower than non-indy!

• Enable indy, knowing perf is bad?

Sunday, February 3, 13

Page 39: Invokedynamic: Tales from the Trenches

What to Do?

• GA indy perf slower than non-indy!

• Enable indy, knowing perf is bad?

• Disable, despite promises of future perf?

Sunday, February 3, 13

Page 40: Invokedynamic: Tales from the Trenches

What to Do?

• GA indy perf slower than non-indy!

• Enable indy, knowing perf is bad?

• Disable, despite promises of future perf?

• Detect JVM version?

Sunday, February 3, 13

Page 41: Invokedynamic: Tales from the Trenches

What to Do?

• GA indy perf slower than non-indy!

• Enable indy, knowing perf is bad?

• Disable, despite promises of future perf?

• Detect JVM version?

• Detect VM version?

Sunday, February 3, 13

Page 42: Invokedynamic: Tales from the Trenches

2011-08-31; 7078382: JSR 292: don't count method handle adapters against inlining budgets

2011-09-02; 7071709: JSR 292: switchpoint invalidation should be pushed not pulled

2011-09-08; 7085860: JSR 292: implement CallSite.setTargetNormal and setTargetVolatile as native methods

2011-10-12; 7092712: JSR 292: unloaded invokedynamic call sites can lead to a crash with signature types not on BCP

2011-10-24; 7090904: JSR 292: JRuby junit test crashes in PSScavengeRootsClosure::do_oop

2011-10-25; 7094138: JSR 292: JRuby junit test fails in CallSite.setTargetNormal: obj->is_oop() failed: sanity check

2011-11-17; 7108383: JSR 292: JRuby bench_define_method_methods.rb: assert(slow_jvms != NULL) failed: miss path must not

Sunday, February 3, 13

Page 43: Invokedynamic: Tales from the Trenches

Java 7u2

• Performance work finally released!

• JRuby users put indy into production

• NoClassDefFound era (ha!) begins

• To this day, no 100% working Hotspot indy!

• ...and apparently J9 is not much better

• Maybe we should have Java syntax for indy?

Sunday, February 3, 13

Page 44: Invokedynamic: Tales from the Trenches

NCDFE Bug

• Method handles operate at bootstrap level

• Must be able to bind user-loaded classes

• Handle impl only handled first type right

• Subsequent types presented incorrectly

• NCDFE resulted in JITed code

Sunday, February 3, 13

Page 45: Invokedynamic: Tales from the Trenches

NCDFE Workarounds

• Load everything on bootstrap

• JRuby does this for noverify

• Not practical for server setups

• Erase all types to bootstrap classes

• Introduced checkcasts hurt perf

• Impossible to maintain both versions

Sunday, February 3, 13

Page 46: Invokedynamic: Tales from the Trenches

Other Issues

• Inlining thresholds must be special-cased

• Native handles are opaque

• Native handles are expensive to create

• Native handles are hard to inspect

• Many optz can’t cross indy site

Sunday, February 3, 13

Page 47: Invokedynamic: Tales from the Trenches

2012

• Rewrite of Hotspot’s 292 subsystem

• Mostly-native moves into Java code

• LambdaForm instead of native

• @ForceInline hints added for JIT purposes

• Erases types, avoiding NCDFE issues

Sunday, February 3, 13

Page 48: Invokedynamic: Tales from the Trenches

LambdaForm

• Java objects represent MH chain

• Unjitted, call directly through LFs

• LF “compiler” emits bytecoded adapters

• Bytecode inlined, optimized as normal

• JIT becomes more important

• Not universally loved...

Sunday, February 3, 13

Page 49: Invokedynamic: Tales from the Trenches

def foo barend

def bar bazend

def baz sleepend

foo

Sunday, February 3, 13

Page 50: Invokedynamic: Tales from the Trenches

LF pre-JITat org.jruby.RubyKernel.sleep(RubyKernel.java:801)at org.jruby.RubyKernel$INVOKER$s$0$1$sleep.call(RubyKernel$INVOKER$s$0$1$sleep.gen)at org.jruby.internal.runtime.methods.JavaMethod$JavaMethodN.call(JavaMethod.java:642)at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:197)at java.lang.invoke.LambdaForm$DMH/692342133.invokeVirtual_LLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1395089624.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1254526270.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1744347043.invokeSpecial_LLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)

Sunday, February 3, 13

Page 51: Invokedynamic: Tales from the Trenches

Anonymous Classesat java.lang.invoke.LambdaForm$LFI/1395089624.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)

• Shared class metadata

• Patched versions of existing classes

• Reduced impact on symbol table, permgen

Sunday, February 3, 13

Page 52: Invokedynamic: Tales from the Trenches

at org.jruby.RubyKernel.sleep(RubyKernel.java:801)at org.jruby.RubyKernel$INVOKER$s$0$1$sleep.call(RubyKernel$INVOKER$s$0$1$sleep.gen)at org.jruby.internal.runtime.methods.JavaMethod$JavaMethodN.call(JavaMethod.java:642)at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:197)at java.lang.invoke.LambdaForm$DMH/692342133.invokeVirtual_LLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1395089624.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1254526270.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1744347043.invokeSpecial_LLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1191747167.invokeSpecial_LLLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1395089624.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1191747167.invokeSpecial_LLLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/662441761.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LL_L(LambdaForm.java:1097)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/662441761.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$MH/1190524793.invokeExact_MT(LambdaForm$MH)at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:599)at org.jruby.runtime.invokedynamic.InvocationLinker.invocationFallback(InvocationLinker.java:138)at java.lang.invoke.LambdaForm$DMH/1304836502.invokeStatic_LLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$DMH/1151020327.invokeSpecial_LLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1254526270.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$MH/1451043227.linkToCallSite(LambdaForm$MH)at ruby.__dash_e__.method__2$RUBY$baz(-e:1)at java.lang.invoke.LambdaForm$DMH/1304836502.invokeStatic_LLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$DMH/1151020327.invokeSpecial_LLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1254526270.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1744347043.invokeSpecial_LLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)

at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1191747167.invokeSpecial_LLLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1395089624.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1191747167.invokeSpecial_LLLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/662441761.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LL_L(LambdaForm.java:1097)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/662441761.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$MH/1190524793.invokeExact_MT(LambdaForm$MH)at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:599)at org.jruby.runtime.invokedynamic.InvocationLinker.invocationFallback(InvocationLinker.java:138)at java.lang.invoke.LambdaForm$DMH/1304836502.invokeStatic_LLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$DMH/1151020327.invokeSpecial_LLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1254526270.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$MH/1451043227.linkToCallSite(LambdaForm$MH)at ruby.__dash_e__.method__1$RUBY$bar(-e:1)at java.lang.invoke.LambdaForm$DMH/1304836502.invokeStatic_LLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$DMH/1151020327.invokeSpecial_LLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1254526270.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1744347043.invokeSpecial_LLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1191747167.invokeSpecial_LLLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1395089624.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1191747167.invokeSpecial_LLLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)

at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/662441761.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LL_L(LambdaForm.java:1097)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/662441761.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$MH/1190524793.invokeExact_MT(LambdaForm$MH)at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:599)at org.jruby.runtime.invokedynamic.InvocationLinker.invocationFallback(InvocationLinker.java:138)at java.lang.invoke.LambdaForm$DMH/1304836502.invokeStatic_LLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$DMH/1151020327.invokeSpecial_LLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1254526270.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$MH/1451043227.linkToCallSite(LambdaForm$MH)at ruby.__dash_e__.method__0$RUBY$foo(-e:1)at java.lang.invoke.LambdaForm$DMH/1304836502.invokeStatic_LLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$DMH/1151020327.invokeSpecial_LLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1254526270.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1744347043.invokeSpecial_LLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1191747167.invokeSpecial_LLLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1395089624.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1837543557.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$DMH/1191747167.invokeSpecial_LLLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NFI/792791759.invoke_LLLLLL_L(LambdaForm$NFI)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/874088044.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLL_L(LambdaForm.java:1107)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/662441761.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LL_L(LambdaForm.java:1097)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/662441761.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$MH/1190524793.invokeExact_MT(LambdaForm$MH)at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:599)at org.jruby.runtime.invokedynamic.InvocationLinker.invocationFallback(InvocationLinker.java:138)at java.lang.invoke.LambdaForm$DMH/1304836502.invokeStatic_LLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$DMH/1151020327.invokeSpecial_LLLLL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invoke_LLLLL_L(LambdaForm.java:1112)at java.lang.invoke.LambdaForm$DMH/1627674070.invokeStatic_LL_L(LambdaForm$DMH)at java.lang.invoke.LambdaForm$NamedFunction.invokeWithArguments(LambdaForm.java:1136)at java.lang.invoke.LambdaForm.interpretName(LambdaForm.java:625)at java.lang.invoke.LambdaForm.interpretWithArguments(LambdaForm.java:604)at java.lang.invoke.LambdaForm$LFI/1254526270.interpret_L(LambdaForm$LFI)at java.lang.invoke.LambdaForm$MH/1451043227.linkToCallSite(LambdaForm$MH)at ruby.__dash_e__.__file__(-e:1)

sleep

baz

bar

foo

top-level of scriptSunday, February 3, 13

Page 53: Invokedynamic: Tales from the Trenches

Java Release?

• Work-in-progress until recent months

• Only made it to OpenJDK8 this fall

• Targeted for 7u12/14 right now

• Approval seems to be going well

• Still no 100% working indy release!

Sunday, February 3, 13

Page 54: Invokedynamic: Tales from the Trenches

How Good Is It?

• We’ll come back to this

Sunday, February 3, 13

Page 55: Invokedynamic: Tales from the Trenches

Meanwhile...

• RubyFlux: Ruby to .java compiler

• Dynamic calls converted to virtual

• Generated superclass with all methods

• method_missing, send, etc possible

• Eclipse JDT for generating source

• Early prototype

Sunday, February 3, 13

Page 56: Invokedynamic: Tales from the Trenches

def foo barend

def bar selfend

a = 100_000_000while a > 0 foo; foo; foo; foo; foo a -= 1end

Sunday, February 3, 13

Page 57: Invokedynamic: Tales from the Trenches

public class foo_bar extends RObject { public static void main(String[] args) { new foo_bar().$main(); }

public void $main() { RObject $last = RNil; RObject a = RNil; $last = a = new RFixnum(100000000L); while (a.$greater(new RFixnum(0L)).toBoolean()) { $last = foo(); $last = foo(); $last = foo(); $last = foo(); $last = foo(); $last = a = a.$minus(new RFixnum(1L)); ; } $last = RNil; ; }

public RObject foo() { RObject $last = RNil; $last = bar(); return $last; }

public RObject bar() { RObject $last = RNil; $last = RNil; return $last; }}

Sunday, February 3, 13

Page 58: Invokedynamic: Tales from the Trenches

Other Pain

• Performance

• Debugging

• Security

• MethodHandle API

Sunday, February 3, 13

Page 59: Invokedynamic: Tales from the Trenches

Performance

Sunday, February 3, 13

Page 60: Invokedynamic: Tales from the Trenches

Performance

• I believe, I really do

Sunday, February 3, 13

Page 61: Invokedynamic: Tales from the Trenches

Performance

• I believe, I really do

• But it has gone back and forth

Sunday, February 3, 13

Page 62: Invokedynamic: Tales from the Trenches

Performance

• I believe, I really do

• But it has gone back and forth

• And I have shamefully not contributed

Sunday, February 3, 13

Page 63: Invokedynamic: Tales from the Trenches

Performance

• I believe, I really do

• But it has gone back and forth

• And I have shamefully not contributed

• Other than whining and providing benches

Sunday, February 3, 13

Page 64: Invokedynamic: Tales from the Trenches

Performance

• I believe, I really do

• But it has gone back and forth

• And I have shamefully not contributed

• Other than whining and providing benches

• I vow to change this in 2013

Sunday, February 3, 13

Page 65: Invokedynamic: Tales from the Trenches

Debugging

• MethodHandle errors are often cryptic

• No MH chain inspection

• Backward binding flips brain ugh bad

• Errors may occur after repeated calls

• Site rebinding bugs

• PIC bugs

• Failure path bugs

Sunday, February 3, 13

Page 66: Invokedynamic: Tales from the Trenches

Security

• With great power...comes great exploits

• Recent hacks related to indy, MHs

• I knew at least one would come out

• ...so I helped test recent fixes

• Imagine building reflection API today

• In the presence of stack hacks!

Sunday, February 3, 13

Page 67: Invokedynamic: Tales from the Trenches

MethodHandle API

• MH API is very bare bones, low level

• InvokeBinder - fluent DSL for MH chains

• Build forward, not backward

• Track signature, arg names

• Many conveniences

• https://github.com/headius/invokebinder

Sunday, February 3, 13

Page 68: Invokedynamic: Tales from the Trenches

MethodHandle mh = MethodHandles.insertArguments( otherMH, index arg1, arg2);

mh = MethodHandles.dropArguments(mh, 1, Blah.class)

mh = MethodHandles.permuteArguments( mh, MethodType.methodType( String.class, Object.class, int.class new int[] {0, 1, 4, 5});

mh = MethodHandles.explicitCastArguments( mh, MethodType.methodType( String.class, Object.class, int.class);

Sunday, February 3, 13

Page 69: Invokedynamic: Tales from the Trenches

MethodHandle mh = Binder .from(String.class, String.class, String.class) // String w(String, String) .drop(1, String.class) // String x(String) .insert(0, 'hello') // String y(String, String) .cast(String.class, CharSequence.class, Object.class) // String z(CharSequence, Object) .invoke(someTargetHandle);

MethodHandle m11 = lookup        .findStatic(Demo1.class, "twoArgs",                MethodType.methodType(String.class, String.class, String.class));m11 = MethodHandles.permuteArguments(        m11,        MethodType.methodType(String.class, String.class, String.class, int.class),        1, 0);

MethodHandle m12 = Binder.from(String.class, String.class, String.class, int.class)        .permute(1, 0)        .invokeStatic(lookup, Demo1.class, "initials");

m12.invoke("one", "two", 3); // => "[two,one]"

Argument Manipulation

Sunday, February 3, 13

Page 70: Invokedynamic: Tales from the Trenches

MethodHandle mh = Binder .from(String.class, String.class, String.class) // String w(String, String) .drop(1, String.class) // String x(String) .insert(0, 'hello') // String y(String, String) .cast(String.class, CharSequence.class, Object.class) // String z(CharSequence, Object) .invoke(someTargetHandle);

MethodHandle m11 = lookup        .findStatic(Demo1.class, "twoArgs",                MethodType.methodType(String.class, String.class, String.class));m11 = MethodHandles.permuteArguments(        m11,        MethodType.methodType(String.class, String.class, String.class, int.class),        1, 0);

MethodHandle m12 = Binder.from(String.class, String.class, String.class, int.class)        .permute(1, 0)        .invokeStatic(lookup, Demo1.class, "initials");

m12.invoke("one", "two", 3); // => "[two,one]"

Argument Manipulation

Sunday, February 3, 13

Page 71: Invokedynamic: Tales from the Trenches

MethodHandle post = Binder .from(void.class, String[].class) .invokeStatic(lookup, BinderTest.class, "finallyLogic"); MethodHandle handle = Binder .from(void.class, String[].class) .tryFinally(post) .invokeStatic(lookup, BinderTest.class, "bodyOfCode");

try/finally

Sunday, February 3, 13

Page 72: Invokedynamic: Tales from the Trenches

MethodHandle exceptionHandler = Binder .from(target.type().insertParameterTypes(0, Throwable.class).changeReturnType(void.class)) .drop(0) .invoke(post); MethodHandle rethrow = Binder .from(target.type().insertParameterTypes(0, Throwable.class)) .fold(exceptionHandler) .drop(1, target.type().parameterCount()) .throwException(); target = MethodHandles.catchException(target, Throwable.class, rethrow); // if target returns a value, we must return it regardless of postMethodHandle realPost = post;if (target.type().returnType() != void.class) { // modify post to ignore return value MethodHandle newPost = Binder .from(target.type().insertParameterTypes(0, target.type().returnType()).changeReturnType(void.class)) .drop(0) .invoke(post);  // fold post into an identity chain that only returns the value realPost = Binder .from(target.type().insertParameterTypes(0, target.type().returnType())) .fold(newPost) .drop(1, target.type().parameterCount()) .identity();} return MethodHandles.foldArguments(realPost, target);

try/finally

Sunday, February 3, 13

Page 73: Invokedynamic: Tales from the Trenches

Symbolic Arguments

• Easier to follow than indices

• Easier to adapt to different signatures

• Arity less of a challenge

Sunday, February 3, 13

Page 74: Invokedynamic: Tales from the Trenches

MethodHandle mh = MethodHandles.insertArguments( otherMH, 0 arg1, arg2);

mh = MethodHandles.dropArguments(mh, 1, Blah.class)

mh = MethodHandles.permuteArguments( mh, MethodType.methodType( String.class, Object.class, int.class new int[] {0, 1, 4, 5});

mh = MethodHandles.explicitCastArguments( mh, MethodType.methodType( String.class, Object.class, int.class);

Sunday, February 3, 13

Page 75: Invokedynamic: Tales from the Trenches

// Incoming args for DynamicMethod.callprivate static final Signature DYNAMIC_METHOD_SIG = Signature .returning(IRubyObject.class) .appendArg("method", DynamicMethod.class) .appendArg("context", ThreadContext.class) .appendArg("self", IRubyObject.class) .appendArg("selfClass", RubyModule.class) .appendArg("name", String.class); // Only want context and selfprivate static final Signature CONTEXT_AND_SELF = DYNAMIC_METHOD_SIG .permute("context", "self"); // Easy generic transformations from one sig to anotherprivate static MethodHandle dynamicCallTarget(Signature from, Signature to) { return SmartBinder .from(from) .fold("selfClass", from.asFold(RubyClass.class).permuteTo(PGC, "context", "self")) .permute(to) .cast(to) .invokeVirtualQuiet(lookup(), "call") .handle();}

Args By Name

Sunday, February 3, 13

Page 76: Invokedynamic: Tales from the Trenches

Modern Era

Sunday, February 3, 13

Page 77: Invokedynamic: Tales from the Trenches

Use in JRuby

• Dynamic calls

• Dynamic “constants”

• Global variables

• Thread events

• Lazy literals

• Instance variables

Sunday, February 3, 13

Page 78: Invokedynamic: Tales from the Trenches

Dynamic Calls

• Per-class mutable method tables

• Consistent call signature

• Bootstrap binds fallback path

• Fallback binds guards + target

• Call site fails out after N invalidations

Sunday, February 3, 13

Page 79: Invokedynamic: Tales from the Trenches

Dyncall Guard

• GWT

• Exact type check for “final” types

• Metaclass identity

• Slightly weaker than Hotspot

• SwitchPoint

• Per-class modification guard

Sunday, February 3, 13

Page 80: Invokedynamic: Tales from the Trenches

if (self instanceof RubySymbol || self instanceof RubyFixnum || self instanceof RubyFloat || self instanceof RubyNil || self instanceof RubyBoolean.True || self instanceof RubyBoolean.False) {  test = selfTest .insert(1, "selfJavaType", self.getClass()) .cast(boolean.class, Object.class, Class.class) .invoke(TEST_CLASS); } else {  selfTest = selfTest.insert(0, "selfClass", selfClass);  test = selfTest .cast(boolean.class, RubyClass.class, IRubyObject.class) .invoke(TEST);} gwt = createGWT(test, target, fallback, entry, site, curry); // wrap in switchpoint for mutation invalidationgwt = switchPoint.guardWithTest( gwt, curry ? insertArguments(fallback, 0, site) : fallback);

Sunday, February 3, 13

Page 81: Invokedynamic: Tales from the Trenches

if (self instanceof RubySymbol || self instanceof RubyFixnum || self instanceof RubyFloat || self instanceof RubyNil || self instanceof RubyBoolean.True || self instanceof RubyBoolean.False) {  test = selfTest .insert(1, "selfJavaType", self.getClass()) .cast(boolean.class, Object.class, Class.class) .invoke(TEST_CLASS); } else {  selfTest = selfTest.insert(0, "selfClass", selfClass);  test = selfTest .cast(boolean.class, RubyClass.class, IRubyObject.class) .invoke(TEST);} gwt = createGWT(test, target, fallback, entry, site, curry); // wrap in switchpoint for mutation invalidationgwt = switchPoint.guardWithTest( gwt, curry ? insertArguments(fallback, 0, site) : fallback);

Sunday, February 3, 13

Page 82: Invokedynamic: Tales from the Trenches

if (self instanceof RubySymbol || self instanceof RubyFixnum || self instanceof RubyFloat || self instanceof RubyNil || self instanceof RubyBoolean.True || self instanceof RubyBoolean.False) {  test = selfTest .insert(1, "selfJavaType", self.getClass()) .cast(boolean.class, Object.class, Class.class) .invoke(TEST_CLASS); } else {  selfTest = selfTest.insert(0, "selfClass", selfClass);  test = selfTest .cast(boolean.class, RubyClass.class, IRubyObject.class) .invoke(TEST);} gwt = createGWT(test, target, fallback, entry, site, curry); // wrap in switchpoint for mutation invalidationgwt = switchPoint.guardWithTest( gwt, curry ? insertArguments(fallback, 0, site) : fallback);

Sunday, February 3, 13

Page 83: Invokedynamic: Tales from the Trenches

public static boolean testMetaclass( RubyClass metaclass, IRubyObject self) { return metaclass == ((RubyBasicObject)self).getMetaClass();}

Icky Hotspot artifact...

Sunday, February 3, 13

Page 84: Invokedynamic: Tales from the Trenches

def foo selfend def bar fooend 100_000.times do barend

Sunday, February 3, 13

Page 85: Invokedynamic: Tales from the Trenches

HERE BE DRAGONS

• x86_64 ASM output from Hotspot

• Google “hotspot printassembly”

• hsdis shared lib

• Drop into JVM dylib dir

• -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly

• PROFIT

Sunday, February 3, 13

Page 86: Invokedynamic: Tales from the Trenches

# parm0: rsi:rsi = '$01_dyncall' # parm1: rdx:rdx = 'org/jruby/runtime/ThreadContext' # parm2: rcx:rcx = 'org/jruby/runtime/builtin/IRubyObject' # parm3: r8:r8 = 'org/jruby/runtime/Block'

... 0x000000010c8e266c: test %rdx,%rdx 0x000000010c8e266f: je 0x000000010c8e26b5 ;*ifnull ; - java.lang.Class::cast@1 (line 3008) ; - java.lang.invoke.LambdaForm$MH/1631862159::guard@12 ; - java.lang.invoke.LambdaForm$MH/783286238::linkToCallSite@14 ; - $01_dyncall::method__1$RUBY$bar@3 (line 6)

0x000000010c8e2671: mov 0x8(%rcx),%r11d ; implicit exception: dispatches to 0x000000010c8e26f1 0x000000010c8e2675: cmp $0x21f1a6ba,%r11d ; {metadata('org/jruby/RubyObject')} 0x000000010c8e267c: jne 0x000000010c8e26d5 0x000000010c8e267e: mov %rcx,%r10 ;*checkcast ; - org.jruby.runtime.invokedynamic.InvocationLinker::testMetaclass@2 (line 694) ; - java.lang.invoke.LambdaForm$DMH/1348949648::invokeStatic_LL_I@11 ; - java.lang.invoke.LambdaForm$MH/591137559::convert@26 ; - java.lang.invoke.LambdaForm$MH/1638215613::guard@17 ; - java.lang.invoke.LambdaForm$DMH/476800120::invokeSpecial_LLLL_L@16 ; - java.lang.invoke.LambdaForm$MH/1631862159::guard@50 ; - java.lang.invoke.LambdaForm$MH/783286238::linkToCallSite@14 ; - $01_dyncall::method__1$RUBY$bar@3 (line 6)

0x000000010c8e2681: mov 0x14(%r10),%ebp ;*getfield metaClass ; - org.jruby.RubyBasicObject::getMetaClass@1 (line 517) ; - org.jruby.runtime.invokedynamic.InvocationLinker::testMetaclass@5 (line 694) ; - java.lang.invoke.LambdaForm$DMH/1348949648::invokeStatic_LL_I@11 ; - java.lang.invoke.LambdaForm$MH/591137559::convert@26 ; - java.lang.invoke.LambdaForm$MH/1638215613::guard@17 ; - java.lang.invoke.LambdaForm$DMH/476800120::invokeSpecial_LLLL_L@16 ; - java.lang.invoke.LambdaForm$MH/1631862159::guard@50 ; - java.lang.invoke.LambdaForm$MH/783286238::linkToCallSite@14 ; - $01_dyncall::method__1$RUBY$bar@3 (line 6)

0x000000010c8e2685: cmp $0x2576cde9,%ebp ; {oop(a 'org/jruby/MetaClass')} 0x000000010c8e268b: jne 0x000000010c8e269c ;*if_acmpne ; - org.jruby.runtime.invokedynamic.InvocationLinker::testMetaclass@8 (line 694) ; - java.lang.invoke.LambdaForm$DMH/1348949648::invokeStatic_LL_I@11 ; - java.lang.invoke.LambdaForm$MH/591137559::convert@26 ; - java.lang.invoke.LambdaForm$MH/1638215613::guard@17 ; - java.lang.invoke.LambdaForm$DMH/476800120::invokeSpecial_LLLL_L@16 ; - java.lang.invoke.LambdaForm$MH/1631862159::guard@50 ; - java.lang.invoke.LambdaForm$MH/783286238::linkToCallSite@14 ; - $01_dyncall::method__1$RUBY$bar@3 (line 6)

0x000000010c8e268d: mov %rcx,%rax 0x000000010c8e2690: add $0x30,%rsp 0x000000010c8e2694: pop %rbp 0x000000010c8e2695: test %eax,-0x18e369b(%rip) # 0x000000010afff000 ; {poll_return} 0x000000010c8e269b: retq

Sunday, February 3, 13

Page 87: Invokedynamic: Tales from the Trenches

# parm0: rsi:rsi = '$01_dyncall' # parm1: rdx:rdx = 'org/jruby/runtime/ThreadContext' # parm2: rcx:rcx = 'org/jruby/runtime/builtin/IRubyObject' # parm3: r8:r8 = 'org/jruby/runtime/Block'

... mov 0x8(%rcx),%r11d ; implicit exception: dispatches to 0x000000010c8e26f1 cmp $0x21f1a6ba,%r11d ; {metadata('org/jruby/RubyObject')} jne 0x000000010c8e26d5 mov %rcx,%r10 ;*checkcast ; - InvocationLinker::testMetaclass@2 (line 694)

mov 0x14(%r10),%ebp ;*getfield metaClass ; - RubyBasicObject::getMetaClass@1 (line 517) ; - InvocationLinker::testMetaclass@5 (line 694)

cmp $0x2576cde9,%ebp ; {oop(a 'org/jruby/MetaClass')} jne 0x000000010c8e269c ;*if_acmpne ; - InvocationLinker::testMetaclass@8 (line 694)

mov %rcx,%rax... retq + safepoint checks...

Sunday, February 3, 13

Page 88: Invokedynamic: Tales from the Trenches

Better Than Java?

• Polymorphic calls

• Hotspot inlines up to bimorphic

• JRuby: trimorphic PIC, configurable

• Smarter type checks possible

• Eliminate boxing at call site

Sunday, February 3, 13

Page 89: Invokedynamic: Tales from the Trenches

Constants

• Lexical lazily-defined variables

• Redefinable, but usually static

• Single global invalidator (SwitchPoint)

• Mostly due to lexical scoping

• Constant target

Sunday, February 3, 13

Page 90: Invokedynamic: Tales from the Trenches

Global Variables

• Global, thread-local, frame-local

• Only global gets cached

• Per-variable guard

• Constant value

• Failover to lookup after N invalidations

Sunday, February 3, 13

Page 91: Invokedynamic: Tales from the Trenches

Foo = 1$bar = 1 def get_foo Fooend def get_bar $barend

Sunday, February 3, 13

Page 92: Invokedynamic: Tales from the Trenches

# parm0: rsi:rsi = '$06_constants' # parm1: rdx:rdx = 'org/jruby/runtime/ThreadContext' # parm2: rcx:rcx = 'org/jruby/runtime/builtin/IRubyObject' # parm3: r8:r8 = 'org/jruby/runtime/Block' # [sp+0x50] (sp of caller) mov %eax,-0x14000(%rsp) push %rbp sub $0x40,%rsp ;*synchronization entry ; - $06_constants::method__0$RUBY$get_foo@-1 (line 5)

test %rsi,%rsi je 0x000000010d5d464c ;*ifnull ; - java.lang.Class::cast@1 (line 3008) ; - $06_constants::method__0$RUBY$get_foo@2 (line 5)

test %rdx,%rdx je 0x000000010d5d466d ;*ifnull ; - java.lang.Class::cast@1 (line 3008) ; - $06_constants::method__0$RUBY$get_foo@2 (line 5)

movabs $0x12c9a2e88,%rax ;*synchronization entry ; - $06_constants::method__0$RUBY$get_foo@-1 (line 5) ; {oop(a 'org/jruby/RubyFixnum')} add $0x40,%rsp pop %rbp test %eax,-0x95364b(%rip) # 0x000000010cc81000 ; {poll_return} retq

Sunday, February 3, 13

Page 93: Invokedynamic: Tales from the Trenches

# parm0: rsi:rsi = '$06_constants' # parm1: rdx:rdx = 'org/jruby/runtime/ThreadContext' # parm2: rcx:rcx = 'org/jruby/runtime/builtin/IRubyObject' # parm3: r8:r8 = 'org/jruby/runtime/Block' # [sp+0x50] (sp of caller)... movabs $0x12c9a2e88,%rax ;*synchronization entry ; - $06_constants::method__0$RUBY$get_foo@-1 (line 5) ; {oop(a 'org/jruby/RubyFixnum')}... retq

+ safepoint yadda yadda

Sunday, February 3, 13

Page 94: Invokedynamic: Tales from the Trenches

# parm0: rsi:rsi = '$06_constants' # parm1: rdx:rdx = 'org/jruby/runtime/ThreadContext' # parm2: rcx:rcx = 'org/jruby/runtime/builtin/IRubyObject' # parm3: r8:r8 = 'org/jruby/runtime/Block' # [sp+0x30] (sp of caller) mov %eax,-0x14000(%rsp) push %rbp sub $0x20,%rsp ;*synchronization entry ; - $06_constants::method__1$RUBY$get_bar@-1 (line 13)

test %rdx,%rdx je 0x00000001057275e7 ;*ifnull ; - java.lang.Class::cast@1 (line 3008) ; - $06_constants::method__1$RUBY$get_bar@1 (line 17)

movabs $0x7f6bef838,%rax ;*synchronization entry ; - $06_constants::method__1$RUBY$get_bar@-1 (line 13) ; {oop(a 'org/jruby/RubyFixnum')} add $0x20,%rsp pop %rbp test %eax,-0x18405e6(%rip) # 0x0000000103ee7000 ; {poll_return} retq

Sunday, February 3, 13

Page 95: Invokedynamic: Tales from the Trenches

# parm0: rsi:rsi = '$06_constants' # parm1: rdx:rdx = 'org/jruby/runtime/ThreadContext' # parm2: rcx:rcx = 'org/jruby/runtime/builtin/IRubyObject' # parm3: r8:r8 = 'org/jruby/runtime/Block' # [sp+0x30] (sp of caller)... movabs $0x7f6bef838,%rax ;*synchronization entry ; - $06_constants::method__1$RUBY$get_bar@-1 (line 13) ; {oop(a 'org/jruby/RubyFixnum')}... retq

Sunday, February 3, 13

Page 96: Invokedynamic: Tales from the Trenches

Better Than Java?

• Constant folding

• Lazy static finals can’t fold in Hotspot

• SwitchPoint + constant value can!

• Broke several benchmarks :-)

Sunday, February 3, 13

Page 97: Invokedynamic: Tales from the Trenches

Better Than Java?

• Constant folding

• Lazy static finals can’t fold in Hotspot

• SwitchPoint + constant value can!

• Broke several benchmarks :-)

Fixed in 7

Sunday, February 3, 13

Page 98: Invokedynamic: Tales from the Trenches

public class JavaLazyFinal { private static final long FINAL = System.currentTimeMillis(); private static long accum = 0 ;  public static void main(String[] args) { for (int i = 0; i < 1000000; i++) { accumulate(); } System.out.println(accum); }  public static void accumulate() { accum += FINAL; }}

Sunday, February 3, 13

Page 99: Invokedynamic: Tales from the Trenches

# {method} 'accumulate' '()V' in 'JavaLazyFinal' # [sp+0x20] (sp of caller)... 1063a6726: movabs $0x7fb283d70,%r10 ; {oop('JavaLazyFinal')} 1063a6730: mov 0x258(%r10),%r8d ;*getstatic FINAL... retq

... movabs $0x7f6bef838,%rax ;*synchronization entry ; - $06_constants::method__1$RUBY$get_bar@-1 (line 13) ; {oop(a 'org/jruby/RubyFixnum')}... retq

Java 6

Sunday, February 3, 13

Page 100: Invokedynamic: Tales from the Trenches

{0x0000000102e40460} 'accumulate' '()V' in 'JavaLazyFinal' # [sp+0x20] (sp of caller)... movabs $0x13c88b682e5,%r10... retq

... movabs $0x7f6bef838,%rax ;*synchronization entry ; - $06_constants::method__1$RUBY$get_bar@-1 (line 13) ; {oop(a 'org/jruby/RubyFixnum')}... retq

Java 7

Sunday, February 3, 13

Page 101: Invokedynamic: Tales from the Trenches

Instance Variables

• Per-class mutable table of “fields”

• Class maps names to offsets

• Type guard

• Specificity is up for debate...

• Offset cached in MH chain directly

• Future: true Java fields?

Sunday, February 3, 13

Page 102: Invokedynamic: Tales from the Trenches

Thread Events

• Rare cross-thread interrupts

• kill, raise exception

• Guard call sites, method entry, backedge

• Invalidates everything...hmmm

Sunday, February 3, 13

Page 103: Invokedynamic: Tales from the Trenches

Thread Event Guard

• Global SwitchPoint

• Target is a no-op

• Fallback checks thread events

• On event, invalidate SP

• All code thrown out

• Looking at hybrid volatile + SP options

Sunday, February 3, 13

Page 104: Invokedynamic: Tales from the Trenches

Lazy Literals

• Numbers, Strings, Regexp, True/False/Nil

• Local caching of JRuby runtime

• ConstantCallSite for runtime-isolated

• MutableCallSite for runtime-specific

• Classloader fails us here anyway

• Do isolated constant handles pointing at same objects fold away?

Sunday, February 3, 13

Page 105: Invokedynamic: Tales from the Trenches

JRuby/Java 7 JRuby/Java 7 + indy

Sunday, February 3, 13

Page 106: Invokedynamic: Tales from the Trenches

base64 richards neural mandelbrot0

1.25

2.5

3.75

5

Tim

es fa

ster

tha

n R

uby

2.0.

0pre

1

JRuby/Java 7 JRuby/Java 7 + indy

Sunday, February 3, 13

Page 107: Invokedynamic: Tales from the Trenches

base64 richards neural mandelbrot0

1.25

2.5

3.75

5

3.434

2.519

2.048

2.99

3.973

Tim

es fa

ster

tha

n R

uby

2.0.

0pre

1

JRuby/Java 7 JRuby/Java 7 + indy

Sunday, February 3, 13

Page 108: Invokedynamic: Tales from the Trenches

base64 richards neural mandelbrot0

1.25

2.5

3.75

5

3.434

2.519

2.048

2.99

3.9734.107

2.946

3.84

Tim

es fa

ster

tha

n R

uby

2.0.

0pre

1

JRuby/Java 7 JRuby/Java 7 + indy

Sunday, February 3, 13

Page 109: Invokedynamic: Tales from the Trenches

JRuby

Rubinius 2.0.0rc1

MagLev

MacRuby 0.12

Ruby 2.0.0

Ruby 1.9.3

Ruby 1.8.7

0 25000 50000 75000 100000

4750

11439

19590.7

19609.5

23954.5

60232.3

90593

Smooth sort of a small array

Iterations per second

Sunday, February 3, 13

Page 110: Invokedynamic: Tales from the Trenches

jruby + Java ext

jruby + Ruby

ruby-2.0.0 + C ext

ruby-1.9.3 + C ext

rbx-2.0.0rc1 + Ruby

macruby-0.12 + Ruby

maglev + Ruby

ruby-2.0.0 + Ruby

ruby-1.9.3 + Ruby

0 1 2 3 4

3.96

2.48

1.39

1.19

0.51

0.51

0.51

0.29

0.1

red/black tree, pure Ruby versus native

Runtime per iteration

Sunday, February 3, 13

Page 111: Invokedynamic: Tales from the Trenches

Future

• Ongoing tuning of JIT, inlining

• Reduce perf cliff for unjitted or uninlined

• Partial inlining helping a lot here

• Inlining through closure receivers

• Tie inlining to closure-receiving site?

• Better EA! value types? tagged values?

• Android!

Sunday, February 3, 13

Page 112: Invokedynamic: Tales from the Trenches

Thank you!

[email protected], @headius

• github/headius/invokebinder

• github/headius/rubyflux

• github/mirah/mirah

• blog.headius.com

Sunday, February 3, 13