7 ms·
Ask HN: Why are there no traditional language compilers that target the JVM?
Or maybe there are, and my Google-fu is weak.
"Traditional" compilable languages, like C, C++, FORTRAN, etc all ought to be targetable to a portable runtime like the JVM (since it defines its own instruction set (Java byte code)).
Scala and other languages target the JVM as their runtime environment.
Why not older/traditionally-compiled languages?
- simne 3y agoThis is economically ineffective. You could write backend for most compilers, to target JVM (instead of pure machine codes), but result will be extremely slow. Going in opposite way is much more fruitful - for example, compiling Java to native machine codes you will got about 10 times faster execution.
- simne 3y agoPlus, to slowness, troubles with JVM license. Better to use Apache-licensed LLVM, or something like it (BSD/MIT licenses).
- bjourne 3y agoThere are many but most never gained traction. Compiling C to JVM bytecode obviously is possible but the resulting code would not run efficiently due to the semantic mismatch. For example, implementing call-by-value semantics that C has using bytecode would not be efficient. Neither would pointer arithmetic.
- Alifatisk 4y agoTruffleRuby & JRuby targets the JVM aswell.
- warrenm 4y agodid not know about those - I did know about Jython, however :) But (and correct me if I'm wrong, please), isn't Ruby a "scripting" language? Ie one not 'meant' to be compiled?
- Alifatisk 4y agoRuby (CRuby) itself is a scripting language, that is correct. JRuby & TruffleRuby is an implementation of the Ruby language but targets another VM. JRuby targets the JVM while TruffleRuby is based on the graalVM. If my memory stands correct, none of these have a compilation step, they both are interpreted. However, TruffleRuby has a feature called Native Image which compiles the language!
- warrenm 4y agoI've used Ruby precisely one time in my life ... and only tangentially (building-out a Mastodon instance) Thank you for the additional information :)
- alexl97 4y agoThere's also GraalVM if you want to target the JVM for multiple languages (e.g. you can use https://www.graalvm.org/latest/reference-manual/llvm/ https://www.graalvm.org/latest/reference-manual/llvm/ for C/C++/Fortran).
- jonahbenton 4y agoBecause there is no sensible mapping from, like, C source to the JVM. C expresses semantics that are not available on the JVM, like most casts, and the JVM is built around concepts that don't exist in C or Fortran, like objects and method dispatch. They do exist to some extent in C++ but the semantics, memory model, etc, are completely different. Someone could make up a mapping but it would be a numerological exercise, a flight of fancy forever lacking utility. And anyway there is a practical integration between C and Fortran world and JVM world. The JVM can link to compiled C and Fortran object code, and for those who like suffering, JVM structures can be reached from C source.
- warrenm 4y ago>Because there is no sensible mapping from, like, C source to the JVM Really? C is available on so many hardware sets ... it's practically universal (Arduino, x86, MIPS, 68k, PowerPC, ARM, SPARC, RISCV...) I find it incredibly hard to believe that you couldn't target the JVM (which also runs on SPARC, ARM, RISCV, x86, POWER...) with C as the source language!
- MagicMoonlight 3y agoYou could… but why would you want to? C can’t use any of the Java features so you’d just be adding pointless inefficiencies
- bastawhiz 4y agoThe architectures that the code runs on has no bearing on whether it makes sense. What, for instance, do you turn a malloc() call for a number of bytes into? The JVM manages your memory for you and assumes you don't do it yourself. There are no instructions for telling the JVM how to manually fiddle with raw memory. And why would you want that? You already have code that expresses how to fiddle with the memory! The JVM provides functionality that C code already implements manually itself.
- warrenm 4y agoThe JVM knows how to handle memory (to some greater or lesser degree ... hence Xmin/Xmax) - https://antipaucity.com/2011/08/08/why-technical-intricacies-matter https://antipaucity.com/2011/08/08/why-technical-intricacies...
- s17tnet 4y agoHAXE also targets JVM [0] but it is not traditional. https://haxe.org/manual/target-jvm-getting-started.html https://haxe.org/manual/target-jvm-getting-started.html
- chii 4y agoInteresting, i didnt realize haxe could generate bytecode directly, rather than generating source code (java), which would then be required to be compiled using the jdk.
- amelius 4y agoBecause of pointers. C and C++ work with a large pointer-addressable heap, the JVM works with objects. You could emulate a heap in the JVM though. You wouldn't get the advantage of JVM's automatic garbage collection, but at least you'd get some form of portability (don't expect to run the JVM inside the JVM this way).
- rjsw 4y agoThere are C compilers targeted at Lisp Machines, one targeted at the JVM could use the same techniques to implement pointers.
- lolinder 4y agoYes, but why? The original question wasn't whether it was possible it was why it hasn't been done, and the obvious answer is that there are barriers that make it impractical (not impossible).
- billythemaniam 4y agoIn addition to other answers in this thread, those languages came first. Java's main motivation initially was to fix all the memory bugs that come from manually managing memory in C/C++ and give a pure OOP development experience. Finally for the use cases that C/C++ are still good for, you typically don't want memory management or the JVM performance overhead so there isn't any incentive.
- h0l0cube 4y agoThings that are central to C, bust the memory model of the JVM (e.g., pointer arithmetic). I did come across a C to JVM project which I used to try and port something slightly complex and it failed to work, thought it may have been sufficient for the author's purposes of accelerating R projects that had dependencies on C libraries. https://www.renjin.org/blog/2016-01-31-introducing-gcc-bridge.html https://www.renjin.org/blog/2016-01-31-introducing-gcc-bridg...
- kaba0 4y agoThere is FastR, part of the Graal project which could likely do what the author was looking for - it can run both R and its C dependencies inside the JVM, and the two may be even cross-optimized across language barriers.
- kosolam 4y agoJust wanted to note that maybe “traditional” isn’t the most accurate description. They are used not because of a tradition but rather due to mountains of legacy code. And, eventually everything boils down to specific requirements and tasks that need to be accomplished, and the question should be what is the best tool for the given job, given a set of circumstances and constraints…
- warrenm 4y agoMaybe you're right ... I'm using "traditional" in place of "legacy" or "classic" (I believe ) There are a lot of "modern" or "up-and-coming" languages out there (Hare, Python, Ruby, Swift...) And there are a slew of "old", "traditional", or "classic" languages out there, too :) (APL, FORTRAN, C/C++...)
- johnbellone 4y agoUnfortunately, the JVM ecosystem did not fix that problem. There are mountains and mountains of Java code from 25+ years ago that are in the same situation. If there was a way to reliability run code on top of the JVM it would be gold. There do exist several products that perform a line-by-line analysis and spit out a comparable Java module, but it is extremely procedural, and is very high touch from an engineering perspective.
- ilyt 4y agoBecause portability is overrated compared to performance/RAM usage hit. Someone stubborn enough could figure it out but why ?
- odyssey7 4y agoIt's not as much of a counterexample now as it would have been years ago, thanks to performance improvements, but JavaScript won on portability despite not being high-performance.
- Traubenfuchs 4y agoThere is the GraalVM Python Runtime, Renjin GCC-Bridge (for C, C++, R)... I feel like all of this kind of exists but it's quite esoteric "non-standard stuff" and not necessarily something sane people want in production. https://github.com/bedatadriven/renjin/tree/master/tools/gcc-bridge https://github.com/bedatadriven/renjin/tree/master/tools/gcc... https://www.graalvm.org/python/ https://www.graalvm.org/python/
- yarg 4y agoGraal's also got an LLVM frontend. https://www.graalvm.org/latest/reference-manual/llvm/Compatibility/ https://www.graalvm.org/latest/reference-manual/llvm/Compati...
- haspok 4y agoWhat would be the point? For example, what would you gain by having a C compiler that compiled to bytecode? It would be amazingly low level (C is just an assembler with macros) yet amazingly non-performant. And of course you'd need to port libc et al to do anything non-trivial. Don't forget that the JVM already has ways to communicate with native programs (JNI originally, but now a new FFI is in the works), so if you depended on any native code you could still use it.
- circuit10 4y agoThere are some advantages to it, at least if the bytecode is designed for it like WebAssembly is. You get portability and the loss of performance isn’t always that bad (WASM is maybe 70%ish as fast as native though that will depend on the workload and VM implementation), and you can reuse existing code. But the JVM isn’t really designed for it
- JohnFen 4y ago> WASM is maybe 70%ish as fast as native That counts as bad performance in my book.
- circuit10 4y agoIt’s not “amazingly non-performant” though. I assume the comment was thinking of something dynamic being run through an interpreter, which might be 1-10% as performant as native, but if you design the bytecode to be statically compilable and simple you can do a lot better. A 30%ish performance hit really isn’t bad for perfect cross-platform compatibility, determinism, and sandboxing for free, and the gap will close as it matures
- JohnFen 4y agoTrue, it's not amazingly poor. But the performance hit is very discouraging nonetheless.
- an-unknown 4y ago
- doublejames 4y ago[flagged]
- Riverheart 4y agoThis comment is the same as jonahbenton’s just reworded, presumably via ChatGPT. Please don’t do this. https://news.ycombinator.com/item?id=35828235 https://news.ycombinator.com/item?id=35828235
- warrenm 4y agoHas something dramatically changed in the last several years? Java supported casts last time I used it (admittedly ... in the 1.6 era)
- Thiez 4y agoNot like C. Try building a xor-linked list in Java.
- warrenm 4y agoan "xor-linked list"?
- Thiez 4y agoI imagine typing that comment took about as much effort typing those words on your search engine would have taken. Read this: https://en.m.wikipedia.org/wiki/XOR_linked_list https://en.m.wikipedia.org/wiki/XOR_linked_list It is a datastructure that can easily be implemented in C but cannot be implemented directly on the JVM.
- ablob 4y agoA java cast is fundamentally different from a c cast, if thats what you're aiming at. In C, you ask the compiler to treat a memory location differently (in some cases it also implies a conversion), while in java you ask the compiler to check, whether an object is of a specific type and only if it is, then you get to treat that object in that way. Edit: casts and conversions are unfortunatelly written the same way This is what is meant when people say that Java is type-safe. If you cast a car into a bird a java program will enter a failure-mode, while the C program will tag along happily without further notice.
- bigbillheck 4y agoThere's a whole bunch of reasons, but like so many other things one of them involves RMS: https://gcc.gnu.org/legacy-ml/gcc/2001-02/msg00895.html https://gcc.gnu.org/legacy-ml/gcc/2001-02/msg00895.html
- warrenm 4y agoI kinda don't care about RMS any more ... especially on a nearly-quarter-century-old opinion post that doesn't jive with "today" ;)
- mike_hearn 4y agoHere is the More Than You Ever Wanted To Know answer whilst I wait for a build to go green. Firstly, for the last few years when you talk about this topic you have to distinguish between the "old world" JVMs which can only be given bytecode as input, and GraalVM, which is a superset of those JVMs and enables language implementations to use the JVM's features whilst bypassing the bytecode layer entirely. Java byte code has some problems that make it unsuitable for C-like languages. Most obviously you can't do pointer arithmetic or arbitrary casts, which is a fundamental requirement for real C code. This doesn't mean it can't be done, these are all Turing complete machines after all, but it means there's no point because performance would be very poor due to all the workarounds. For C++ the gap gets wider because Java has its own ideas about how objects work that aren't the same as those in C++, e.g. multiple inheritance, so you can't implement C++ classes as Java classes. These sorts of problems affect any language that is semantically too far away from Java, like scripting languages. JVM bytecode is fairly well designed, is reasonably general, and the invokedynamic bytecode was put in there to make scripting languages easier. But it's a high level bytecode so the semantic mismatch is still there. For a long time this problem seemed inherent to the design space. Any VM bytecode language you can design will end up encoding some assumptions about language design into it, if it doesn't then you've just got assembly language and we already have those. In effect, trying to create a universal bytecode is like trying to create a universal programming language. But the JVM guys didn't give up. Sun/Oracle Labs spent many years on research and the result is Truffle. Instead of asking people to encode their language into a universal ISA so the JVM can understand it, Truffle is an API. It comes as part of GraalVM. You write an interpreter for your language using this API and compile that to JVM bytecode instead (it doesn't have to be written in Java but it does have to be a language that can produce bytecode). This interpreter is then fused with the code of your target language at runtime and fed through a very advanced optimizing compiler (Graal) which generates machine code for your language. This code is then "installed" into the running JVM using another API called JVMCI, and then has access to all the normal JVM services like the garbage collectors, profiling, deoptimization, observability, standard libraries, OS abstractions, ability to call to/from bytecode world and so on. With Truffle you don't think about the low level details of all that. You just write an interpreter. You do have the learn the API - your generated machine code will be relatively slow until you start using the API to annotate your interpreter and optimize it for common cases. But the API is pretty comprehensive and offers a lot of functionality, like language interop, debugging, tracing, hot swapping ... The first languages implemented with Truffle were scripting languages. But the technique is general. It's not scripting or JVM specific and there's no specific reason the language your interpreter reads has to be textual. So they implemented an interpreter for LLVM bitcode. Now you can compile C/C++/FORTRAN/Rust using LLVM, and then execute that on the JVM. Performance is good, not quite as fast as natively compiled with GCC but in the general area. It's a fairly specialized thing to do and today is mostly used for running Python/Ruby extensions. However, because the whole thing is virtualized you can do some mind-bending tricks with it, for example, you can eliminate all the memory errors in the software run this way and then sandbox it without using kernel sandboxing. So it has some potentially big security benefits. And that's how you can run C or any other language on the JVM without killing performance.
- AndrewDucker 4y agoSure, compile to WASM and then use https://github.com/cretz/asmble https://github.com/cretz/asmble to convert to JVM bytecode.
- lboasso 4y agoThe Oberon programming language is 37 years old. Since it is a memory safe language a compiler for the JVM can be written (with some workarounds), for example see the self-hosting compiler oberonc [0]. [0] https://github.com/lboasso/oberonc https://github.com/lboasso/oberonc
- jfengel 4y agoI wrote a book on that almost a quarter-century ago (Programming for the Java Virtual Machine). It includes toy versions of both a Lisp and Prolog compiler. It can definitely be done. I don't know if it's worth it to port existing code to the JVM that way. There are so many pitfalls in porting that it's often easier to rewrite it. The older code is, the more likely it has something specific to the compiler/platform/library, and you'll spend more effort debugging that than just using it as a reference for a clean-sheet implementation in a modern language. I picked Lisp and Prolog because those are languages with a very different style. Compared to that C/C++/Fortran are all kinda the same language. Since then functional programming has taken on a lot more prominence, though logic-style programming still hasn't caught much attention. (The book actually came out of a project to use logic programming as a database query language, and I still feel like that's better than where we are now.) It blows my mind that the book is still in print, and dribbles out a few copies a year. It does need a good rewrite, since so many of the things I talked about are now practical rather than theoretical. The JVM has some new features for supporting non-Java languages, and there are now standard libraries for manipulating bytecode. (I had to roll my own.)
- shadowfox 3y agoVery curious what bytecode manipulation libraries you would recommend. I am aware of ASM [1], which was pleasant, but written in Java itself, I was wondering about something in (say) C, so that it can be used from other languages more easily. [1] https://asm.ow2.io/ https://asm.ow2.io/
- exabrial 4y agoYour assumption is incorrect; it does exist. GraalVM actually supports some of these. And long before GraalVM, there have been multiple commercial offerings to run FORTRAN on the JVM, but they've gone in and out of existence. Most of the time if you're writing C, Cpp, FORTRAN, your primary goal is performance, not memory safety or portability. So executing on the JVM wouldn't buy you much.
- millerm 4y agoYou can literally compile C/C++(using clang)/Rust to LLVM bitcode and run those using GraalVM: https://www.graalvm.org/latest/reference-manual/llvm/Compiling/ https://www.graalvm.org/latest/reference-manual/llvm/Compili...
- jbn 4y agoI will just leave this here: http://nestedvm.ibex.org/ http://nestedvm.ibex.org/
- JohnFen 4y agoI don't understand what the use case for doing that would be. One of the advantages of traditionally compiled languages is that you don't need a runtime component for the resulting binary.
- jimwhite 4y agoFour COBOL for the JVM implementations here along with Prolog, Common Lisp, four versions of Scheme (don't forget Clojure), Smalltalk, two versions of Pascal, Go, Rust, three JavaScript, Simula (the original OO language), and Basic (among others): https://en.wikipedia.org/wiki/List_of_JVM_languages https://en.wikipedia.org/wiki/List_of_JVM_languages GCC-Bridge, a C/Fortran compiler targeting the Java Virtual Machine (JVM) that makes it possible for Renjin to run R packages that include "native" C and Fortran code https://www.renjin.org/blog/2016-01-31-introducing-gcc-bridge.html https://www.renjin.org/blog/2016-01-31-introducing-gcc-bridg... Earlier attempts at FORTRAN struggle because of performance issues but there were some successes: JLAPACK – Compiling LAPACK FORTRAN to Java https://www.hindawi.com/journals/sp/1999/179617/ https://www.hindawi.com/journals/sp/1999/179617/ https://www.semanticscholar.org/paper/Automatic-translation-of-Fortran-to-JVM-bytecode-Seymour-Dongarra/7991f7c66871a4a483fe9ea11413221cee519989 https://www.semanticscholar.org/paper/Automatic-translation-...
- wsherman 4y agoFree Pascal (fpc) has had a JVM target since 2011: https://wiki.freepascal.org/FPC_JVM https://wiki.freepascal.org/FPC_JVM
- bedatadriven 4y agoI have written one (gcc-bridge). Fortran is actually a really good fit for JVM byte code. C/c++ is doable, but only with some unpleasant trade offs that others have mentioned here.