Live data from Hacker News

Jank now has its own custom IR

jank-lang.org

41–50 of 57 posts

Re: Jank now has its own custom IR

#41
post #37

Earlier quoted context omitted.

Indeed. Most of the hate is due to slow start-up time, but once it gets warmed up, the modern JVM has state of the art dynamic compilation and GC. Thousands of man years have been spent getting it to that point.

With Project Leyden being implemented and JEP 516 coming very soon, those worries will be a thing of the past. Now you can get incredible AOT performance without having to depend on Babashka or GraalVM workarounds.

Yep, and I’m totally looking forward to it.

Re: Jank now has its own custom IR

#42
post #4

Great article, as always. There is one thing that I think is important to bear in mind when discussing inlining, especially in the context of Clojure. This is that once a function has been inlined, you can no longer update the definition of that function in the REPL and have that update the behaviour of functions which use it, unless you recompile those as well. This is not a criticism of course, it’s just part of th…

Is that really true? Can't you track invalidations via a dependency graph?

Right, as you said, you'd have to recompile dependents.

Re: Jank now has its own custom IR

#43
post #4

Great article, as always. There is one thing that I think is important to bear in mind when discussing inlining, especially in the context of Clojure. This is that once a function has been inlined, you can no longer update the definition of that function in the REPL and have that update the behaviour of functions which use it, unless you recompile those as well. This is not a criticism of course, it’s just part of th…

Hey lemming! You're right, which is why it should be used sparingly. Since clojure.core is compiled (on the JVM) with direct linking, reacting to var changes isn't an intended concern, since they're not going to work properly throughout any clojure.core code using that var. This makes it a good candidate ns for inlining things. But users shouldn't just be doing this for their normal application vars without giving it due consideration.

Re: Jank now has its own custom IR

#44
post #7

The natural question is why doesn't Jank use MLIR?

I spoke with a couple Clang and LLVM devs about MLIR when I was doing the original design for jank's IR. The general consensus was that MLIR added a great deal of complexity on top of designing/implementing an IR and nobody was confident it was actually worth the effort. Since I knew exactly what I wanted, I just built that.

Re: Jank now has its own custom IR

#45
post #8
post #3

Hoping to understand this better: > Clojure's dynamism is granted by a great deal of both polymorphism and indirection, but this means LLVM has very few optimization opportunities when it's dealing with the LLVM IR from jank. In my mind, what is happening here is you lower Clojure code into LLVM, with a bunch of runtime calls (e.g. your `jank::runtime::dynamic_call`) (e.g. LLVM invoking the runtime over a C ABI). If…

The article talks about inlining a two-arity call to clojure.core/max to instead be an explicit call to cpp/jank.runtime.max, eliminating the unnecessary argument count matching and recursion portions of the Clojure function. It also mentions that in Clang the runtime max function will itself be inlined, so that's something LLVM ("the LLVM project", anyway) is still doing - and beyond that, as written this IR is like…

The first three paragraphs here are on point! jank's IR passes will not worry much about things like load/store optimization, register allocation, inlining C++ functions, etc. These are in LLVM's domain. We just worry about the Clojure side of things. Polymorphic math is intense, but we do our best to avoid the extra work by unboxing whenever possible.

> A future optimisation might be to specialise for unboxed types: far more potential speed improvement over pointer tagging, and IMO quite amenable to analysis with the Jank IR

All of these math functions are templates with four specific categories:

1. Object and object

2. Primitive and primitive

3. Primitive and object

4. Object and primitive

We handle the difference between typed objects (like integer_ref) and type-erased objects (object_ref) as well. This template then gets inlined, which is exactly what the last step of the benchmark optimizations (adding annotations) ensured. The return type of these functions will prefer primitive types, rather than automatically boxing. jank's analyzer tracks all types used, at compile-time, and supports automatic boxing. This means that we're already using the most optimal primitive math whenever we can and that it will indeed inline to just an operator call when working on two primitives, or two typed objects, or a combination thereof.

You can see the code for this here: https://github.com/jank-lang/jank/blob/29c2adb344526d26c8e82...

Re: Jank now has its own custom IR

#46
post #22

Earlier quoted context omitted.

These compilers aren't replacing LLVM, they are adding a compilation step with its own IR where they do certain optimizations and translations *before* handing things off to LLVM. Basically, the idea is to do as much 'high level' optimization and transformation stuff as you can in your own IR, and then let LLVM handle the low-level stuff and the targeting of specific hardware vendors.

That makes sense, thanks. Is this IR at a level where the optimisations can't just be added to LLVM then?

As another said, jank is not replacing LLVM or LLVM IR. We still use LLVM IR! There is a diagram here which shows the pipeline: https://book.jank-lang.org/dev/ir.html

The main thing is that we just use our own IR first, to perform optimizations with contextual data which is gone by the time we get to LLVM IR. That's also why these optimizations are not practical to write in LLVM, since by the time we get to LLVM IR, we're too far separated from jank's AST with the high level semantics of Clojure.

So we just add an intermediate step. Once we have jank's AST, turn it into our own IR, do some optimizations on it for things that LLVM won't be able to see, and then hand it off to LLVM to do the rest.

Re: Jank now has its own custom IR

#47
post #22

Earlier quoted context omitted.

That makes sense, thanks. Is this IR at a level where the optimisations can't just be added to LLVM then?

I don't know much about Jank's implementation, but I can speak to how it's done in Julia (dynamic, high performance language with lispy semantics but matlaby syntax, JIT compiled to LLVM). I think the big thing is just that LLVM can't really be made to closely model everyone's different weird langauge semantics. In practice, the less C-like your language is, the more hoops you will likely need to jump through in orde…

Another example: For years rust was limited on performance optimizations in LLVM. Specifically, it was difficult to get LLVM to properly optimize for Rust's generated code, namely where one can make strong aliasing (and non-aliasing) statements using `noalias`. This is a (pre-existing iirc) LLVM attribute.

Despite being a pre-existing feature motivated by C-like languages, typical C/C++ code does not leverage this attribute that much. So there were a surprising number of bugs in the handling of the attribute, and it took a number of years (I didn't follow things closely, but >= 3 for sure, maybe as much as 6?) before they got ironed out enough where it could be enabled.

Re: Jank now has its own custom IR

#48
post #46
post #22

Earlier quoted context omitted.

That makes sense, thanks. Is this IR at a level where the optimisations can't just be added to LLVM then?

As another said, jank is not replacing LLVM or LLVM IR. We still use LLVM IR! There is a diagram here which shows the pipeline: https://book.jank-lang.org/dev/ir.html The main thing is that we just use our own IR first, to perform optimizations with contextual data which is gone by the time we get to LLVM IR. That's also why these optimizations are not practical to write in LLVM, since by the time we get to LLVM IR,…

Ahh OK, makes perfect sense, and interesting that that IR compiles to C++. Thanks for the info!

Re: Jank now has its own custom IR

#49
Very cool. My immediate thought was - could this open possibilities to compete with Rust on wasm targets? Upon reviewing it more I realized - probably not. Jank (or any other Clojure dialect) wont' really be in position to beat Rust there, and reasons are structural, not just engineering effort - we need GC and, well, that's the biggest elephant.

But to be completely honest, the question: "do you need wasm at all...?", should be always followed by "why?". For like 95% of cases, Clojurescript saves you weeks/months of work. Easier to build, easier to maintain. That's subjective, of course. Most Rustaceans don't even want to try Clojure. Most Clojurists find Rust to be needlessly complex.

Re: Jank now has its own custom IR

#50
post #44
post #7

The natural question is why doesn't Jank use MLIR?

I spoke with a couple Clang and LLVM devs about MLIR when I was doing the original design for jank's IR. The general consensus was that MLIR added a great deal of complexity on top of designing/implementing an IR and nobody was confident it was actually worth the effort. Since I knew exactly what I wanted, I just built that.

Your custom IR is above LLVM’s IR, correct? Is it like SwiftIR then? Maybe you could add a paragraph or two going through that design decision.
Post reply on HN