13 comments

[ 5.9 ms ] story [ 38.4 ms ] thread
Why would anyone compile to Java when it is much easier to compile directly to JVM bytecode?

A lot of languages are doing exactly that, like Mirah, Clojure, ABCL, Scala and many more.

is that actually easier? I know its probably way more efficient in the long run, but i really doubt you could write a bytecode compiler as easily as this was written.
IMHO it is easier because the type system in Java has extra restrictions that the bytecode doesn't have -- for example in bytecode there is no notion of checked exceptions, and variables themselves don't have types, you only need to know the interface of the implicit parameter when calling a method, but not until the call-site.

And what you gain by not learning to output bytecode, you lose by writing supporting libs.

And the example in the article is too simple. Yes you can translate Scheme to Java easily, until you need to do I/O, or until you need to optimize it such that you could use it for real work. I also don't see any mention of continuations, which are surely part of the Scheme standard, or am I wrong?

People think real compilers are complicated, but often writing a native code compiler is simpler than writing an interpreter or translator. Abdulaziz Ghuloum has a great paper called An Incremental Approach to Compiler Construction (http://scheme2006.cs.uchicago.edu/11-ghuloum.pdf), which shows how to build a Scheme native code x86 compiler in 10 pages. JVM bytecode isn't that far away from machine code.

Another great example of how straightforward compilers can be is the Tiny C Compiler (http://bellard.org/tcc/).

    JVM bytecode isn't that far away from machine code.
It is far away in the case the type system is a complete mismatch, like Haskell. Or in the case of Scheme, if you want to have continuations without a huge performance penalty, you've got to put some effort into it.

For these instances I've see advices on Lambda-the-Ultimate to just go for native-code, as it is easier to experiment since you have a lot more freedom.

That said, building on top of the JVM can have advantages other than being easy to write the compiler.

I tried to read through An Incremental Approach but found that it was really too brief. I had questions that I wanted answered but he was briskly moving on to compiling the next Scheme form. I found the first chapter of a longer version from his site, but this seems incomplete. Does anyone know what happened to that? Did it not turn into a book?
Gwt requires .java.
GWT is not a compiler, it is translator.
Near as I can tell Mirah is still just a syntax skin around Java, and compiling directly to .class involves a Java step.

mirahc still does have a --java option, anyway...

Indeed mirahc has --java option, but this is a separate backed from class backend. Class backend produce jvm bytecode directly, not through java source files, and is considered more stable.
It is easy if the language your compiler is written in is itself running on the JVM and you can access the nice libraries for generating the bytecode, for example ASM (http://asm.ow2.org/) which Clojure uses. I built a toy compiler for a Pascal-like language targeting JVM and conceptually it is easy (as you maybe meant), but generating binary bytecode "by hand" would require a lot of extra work. I took a look at ABCL source, it dedicates at least 100kb of Lisp code to a bytecode-generating library.

By the way, ABCL source code seems very interesting for anyone interested in compilers/interpreters/Lisp.

I disagree with one statement:

> performance will go up, but implementation code size and > complexity will go up by about a factor of two

Moving from basic compiler optimizations (CSE, let-floating, unused variable elimination) to static-analysis driven optimizations (arity raising, useless variable elimination, super-beta, CFA-driven closure conversion, transducer identification and optimization for concurrency) may only double code size, but the implementation complexity is pretty intense. Those optimizations often are performing global reasoning against a CPS-style intermediate representation, and debugging issues with them can take a while. We do all of the parenthesized operations listed earlier (in Manticore, http://manticore.cs.uchicago.edu), and I would claim order of magnitude more time/thought spent on the them.

That said, I think I'm supporting Matt's argument of avoiding moving up the complexity order unless you have to!