invokedynamic instructions added to Clojure

847 views
Skip to first unread message

Ghadi Shayban

unread,
Apr 16, 2013, 11:11:32 AM4/16/13
to cloju...@googlegroups.com

I've added a very minimal usage of invokedynamic to the compiler.  Basically the smallest delta without having to change internals of ObjExpr or break ABI compatibility.  This is minimal and raw.  There are many many usages of indy that will really help the Clojure runtime, this ain't one of them.  No benchmarks here, it's probably slower.

In current Clojure mainline, a Fn has reference slots to any vars it needs in its constant pool, like a cache. Anytime a non-dynamic Var's value needs to be accessed, the Var goes from the constant pool to the stack, and getRawRoot() is invoked on it.

With this change, an invokedynamic instruction instead creates a ConstantCallSite, which closes over a looked-up Var, and then binds the call site to invoke getRawRoot() on it directly.  This is only for non-dynamic Vars.

Simple todos:
cache the CallSite as a member on the Var itself so that all identical indy lookup instructions have fast bootstrapping.
emit a similar call for dynamic vars
remove the emission of Vars into the constant pool of a class

Lots of really interesting use cases for invokedynamic and all the associated combinators in java.lang.invoke:

Better protocol callsite caching
CallSite "middleware" for things like CLJ specific instrumentation
equality could be a special instruction
KeywordCallSite could be its own instruction as well
(apply) argument "spreading"/varargs array collection (through the combinators)
potentially removing IFn.invoke(*) and using MethodHandle invocation instead (fat chance)

You can pull down the changes at github.com/ghadishayban/clojure.
mvn clean package, tested on OpenJDK 1.7

Timothy Baldridge

unread,
Apr 16, 2013, 11:33:13 AM4/16/13
to cloju...@googlegroups.com
I'm getting errors: 

java version "1.7.0_07"
Java(TM) SE Runtime Environment (build 1.7.0_07-b10)
Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)

timbal:clojure tim$ java -jar target/clojure-1.6.0-master-SNAPSHOT.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/objectweb/asm/Opcodes
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at clojure.lang.RT.<clinit>(RT.java:38)
at clojure.main.<clinit>(main.java:20)
Caused by: java.lang.ClassNotFoundException: org.objectweb.asm.Opcodes
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
... 14 more


Ghadi Shayban

unread,
Apr 16, 2013, 11:42:01 AM4/16/13
to cloju...@googlegroups.com
This change is built on top of the CLJ-713 removal and upgrade of ASM, so if you want to run the standalone jar, make sure to add ASM 4.1 to your classpath as well (see pom.xml for the artifact).

I normally install it locally with a version number 1.6.0-indy, then rely on lein to resolve everything.

Rich Hickey

unread,
Apr 17, 2013, 3:53:45 PM4/17/13
to cloju...@googlegroups.com
That should be a MutableCallSite (after all, vars can be redef'ed), and redef should trigger a syncAll. And it theoretically *should* improve performance, iff its optimization would support inlining through the var, which currently doesn't happen w/o invokedynamic due to both the indirection and the volatile.

I know a lot of people are interested in invokedynamic, and there's certainly nothing wrong with learning about it.

However, in order for it to be used in Clojure for real, a number of things must be addressed:

There must be a plan
There must be specific benefits pursued, and results measured
We must address backwards compatibility - we can't simply force people to move to JDK 7 or 8, and what about Android?
This will most definitely hinge on support for multiple build targets
We also have to contend with the changes to invokedynamic support itself - the implementation is changing significantly in JDK 8, and there are problems with the support in JDK 7 (errors and failures to optimize) that might never be addressed, leaving the only viable target platform the as-yet-unreleased Java 8.

The next task, after moving to new ASM, remains developing a plan for multiple build targets and conditional support for invokedynamic emission in the compiler.

I remain unconvinced that invokedynamic support is ready for production use by Clojure. JRuby has been extensively enabled to use invokedynamic, by an expert tracking the development over several years [1], and yet the support is off by default on Java 7 [2]

[1] http://blog.headius.com/2007/01/invokedynamic-actually-useful.html
[2] https://github.com/jruby/jruby/wiki/PerformanceTuning
> --
> You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to clojure-dev...@googlegroups.com.
> To post to this group, send email to cloju...@googlegroups.com.
> Visit this group at http://groups.google.com/group/clojure-dev?hl=en.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

Ghadi Shayban

unread,
Apr 21, 2013, 11:38:38 PM4/21/13
to cloju...@googlegroups.com
Thanks for the guidance, I'm definitely trying to internalize it. My ideas are so far half-baked.

re: backwards compatibility
Definitely essential, at minimum to be able to target JVM 1.5/1.6 (thus Android). What about about forwards compatibility of AOT'ed CLJ 1.5 on newer versions of Clojure? Is that in scope?

re: CallSite.  The code I put up (a simple first shot), does account for redeffed vars, but it ignores unmapping and remapping brand new Vars into a namespace.  MutableCallSite.syncAll() would enable a non volatile root.

I wasn't aware that JRuby indy is off by default, though arguably Clojure's simpler invocation semantics could lead to better leverage, and a good plan is pre-requisite.

re: build targets
Do you see adding multiple emission backends in the compiler or more thorough abstractions for compilation units/linking/emission? Or something wholly different? Datomic-backed compiler, thorough type inference + inlining?

re: ASM
Any thoughts on an approach for the switch?

Thanks again your help.
Ghadi
Reply all
Reply to author
Forward
0 new messages