[HN Gopher] Java Hello World, LLVM Edition
___________________________________________________________________
Java Hello World, LLVM Edition
Author : ingve
Score : 156 points
Date : 2025-12-07 11:51 UTC (11 hours ago)
(HTM) web link (www.javaadvent.com)
(TXT) w3m dump (www.javaadvent.com)
| jakozaur wrote:
| LLVM IR is quite fun to play with from many programming
| languages. The Java example is rather educational, but there are
| several practical example,s such as in Go Lang:
|
| https://github.com/llir/llvm
| znpy wrote:
| 500 internal server error...
| zkmon wrote:
| What's wrong with using the standard JDK for Java code?
| throwaway150 wrote:
| Nothing wrong with it. Why would you assume the author is in
| anyway hinting that there's something wrong with using the
| standard JDK for Java code?
| zkmon wrote:
| Ok. Let me ask differently. Why would I download and use LLVM
| for working with java code? Which usecases favor this?
| drzaiusx11 wrote:
| This is interop glue to cross language boundaries in the
| JVM without the problems that come with JNI. The natural
| goal/use-case being that you can call pre-existing code in
| other languages that target LLVM IR.
| TazeTSchnitzel wrote:
| That's not what the article is about.
| connicpu wrote:
| The article is presenting something different entirely.
| This is the precursor to what it would take to create a
| compiler written in java that produces native code.
| mands wrote:
| It's more an fun educational overview of the new FFM API.
|
| I can't think of many actual use-cases where you'd want to
| use the LLVM JIT over those built-in to HotSpot.
|
| Interfacing with existing LLVM-based systems, writing a
| very tight inner loop using LLVM where you absolutely need
| LLVM-like performance, or creating a compiler that targets
| LLVM using Java would be the main "real-world" use-cases.
| almostgotcaught wrote:
| "why would I use a frying pan when I can use a flashlight"
|
| The two things have nothing to do with each other.
| kachapopopow wrote:
| LLVM is such an amazing piece of software, the amount of uses for
| it are unlimited especially when it comes to obfuscation. The IR
| is also really fun for compiling bytecode to native code since
| it's pretty trivial to translate it into IR (opposite of what is
| done in this article)
| troymc wrote:
| I made a poster showing how one might write a Hello World program
| in 39 different programming languages, and even different
| versions of some common languages like Java:
|
| https://troymcconaghy.blog/2025/01/13/39-hello-world-program...
| throwaway150 wrote:
| Cool poster! If you don't mind me asking, would you share what
| tools you use to create this poster? You've got syntax
| highlighting going on there too. What did you use for that?
| iTokio wrote:
| You just have to read his blog, it is short and he answered
| everything.
|
| > he used python and xelatex
|
| > https://github.com/ttmc/hello-world-ways
| troymc wrote:
| Yep, and for syntax highlighting, I used the minted package
| [1]. Internally, minted uses the Pygments library [2].
|
| [1] https://ctan.org/pkg/minted
|
| [2] https://pygments.org/
| throwaway150 wrote:
| Thanks!
| pron wrote:
| Nice, but as of JDK 25 (the preview JEP 445 has become the
| permanent JEP 512), the canonical Hello World in Java is:
| void main() { IO.println("Hello World"); }
| troymc wrote:
| Thanks, I made a note to update that someday.
| prmoustache wrote:
| Not a java developer but why the void? Shouldn't your main
| function and program return an integer?
| tadfisher wrote:
| I believe that is a C-ism, where the C runtime calls your
| main() and exits the process with the return value. The
| Java equivalent is System.exit(int status).
| gavinray wrote:
| The return type of a Java main is the JVM platform return
| type
|
| Sending system signals is external to the JVM platform
| pmdr wrote:
| Objective C is by far the weirdest on that list.
| watersb wrote:
| Smalltalk, but in C
| realo wrote:
| This is super cool! Now someone should make a similar poster
| with Hello World sent to a serial port.
|
| Bonus points if it is a RS485 port.
|
| Some language that seem to look good might show their true ugly
| face...
| Octoth0rpe wrote:
| I've been playing with a very basic compiler for a language that
| looks a bit like go -> llvm ir, but I'm finding myself constantly
| revising my AST implementation as I progressively add more things
| that it needs to represent. Is anyone aware of any kind of
| vaguely standardized AST implementation used by more than one
| project? I've been searching this morning for one and am coming
| up empty. My thinking is that if I can find some reasonably
| widely used implementation, then hopefully that implementation
| has thought out lots of the corner cases that I haven't gotten to
| yet.
| znkr wrote:
| LISP ;-)
| xnacly wrote:
| This, lisp is perfect for representing arbitrary data,
| nesting is just another sexpr, easy to produce, easy to parse
| and easy to debug / reason about
| pjmlp wrote:
| When I did my degree, the years prior to mine had some
| flexibility choosing the implementation language for
| compilers class.
|
| Lisp and Prolog were forbidden due to how easy the whole
| exercise would be.
| Octoth0rpe wrote:
| I can appreciate this answer, but I don't think it's really
| what I'm asking.
|
| I think I'm more looking for some kind of standardized struct
| definition that translates easily to llvm IR and is flexible
| enough for a wide variety of languages to target.
|
| Something like this: https://gist.github.com/thomaswp/8c8ef19
| bd5203ce8b6cd4d6df5e... (Which doesn't meet my criteria
| because AFAICT isn't used by anything, but is reasonably
| close to what I want) or this: https://docs.rs/sap-
| ast/latest/src/ast/lib.rs.html#1-83 (which seems specific to
| SAP, I would like something more general)
| emptysea wrote:
| Ruff's ast is used by Ruff, Ty, and Pyrefly
| Octoth0rpe wrote:
| Thank you! this looks pretty helpful
| rendaw wrote:
| Self plug, I put together this reference/example before+after
| (high and corresponding intermediate/low level) example gallery
| for for a couple languages:
| https://andrewbaxter.github.io/semicompiled/
| https://github.com/andrewbaxter/semicompiled?tab=readme-ov-f...
|
| I was using it while dabbling on compiler stuff, it was useful to
| have a set of concise compilation examples. I haven't touched it
| much lately, unfortunately, and I added the eBPF because the
| target was there but had no way to validate it (standalone eBPF
| validator where?) so I think it's probably somewhat wrong... or
| invalid at least, maybe that's a separate concern for people who
| would want this.
| tuhgdetzhh wrote:
| I'm always a bit shocked how casual people people wget and
| execute shell scripts as part of their install process.
|
| This is the equivalent of giving an author of a website remote
| code execution (RCE) on your computer.
|
| I get the idea that you can download the script first and
| carefully read it, but I think that 99% of people won't.
| balder1991 wrote:
| Even assuming it's not malicious, the script can mess up your
| environment configuration.
| exe34 wrote:
| I'm so thankful for nixos for making it hard for me to give
| in to that temptation. you always think "oh just this once".
| but with nixos I either have to do it right or not bother.
| hombre_fatal wrote:
| NixOS gives you a place to configure things in a
| reproducible way, but it doesn't require you do it.
| tombert wrote:
| It sort of does actually, at least if you don't have nix-
| ld enabled. A lot of programs simply won't start if
| they're not static-linked, and so a lot of the time if
| you download a third-party script, or try to install it
| when the `curl somesite.blah | sh`, it actually will not
| work. Moreover, it also is likely that it won't be
| properly linked in your path unless you do it thr right
| way.
| exe34 wrote:
| $ ./Downloads/tmp/xpack-riscv-none-elf-
| gcc-15.2.0-1/bin/riscv-none-elf-cpp Could not start
| dynamically linked executable: ./Downloads/tmp/xpack-
| riscv-none-elf-gcc-15.2.0-1/bin/riscv-none-elf-cpp NixOS
| cannot run dynamically linked executables intended for
| generic linux environments out of the box. For more
| information, see: https://nix.dev/permalink/stub-ld
|
| You have to go out of your way to make something like
| that run in an fhs env. By that point, you've had enough
| time to think, even with ADHD.
| zenlot wrote:
| If you don't trust the software, don't install it.
| OptionOfT wrote:
| Equally I don't like how many instructions and scripts
| everywhere use shorthands.
|
| Sometimes you see curl -sSLfO. Please, use the long form. It
| makes life easier for everybody. It makes it easier to verify,
| and to look up. Finding --silent in curl's docs is easier than
| reading through every occurrence of -s. curl
| --silent --show-error --location --fail --remote name
| https://example.com/script.sh
|
| Obligatory xkcd: https://xkcd.com/1168/
| ndsipa_pomu wrote:
| Absolutely agree.
|
| The shorthands are for when typing it at a console and the
| long form versions should be used in scripts.
| Terr_ wrote:
| For a small flight of fancy, imagine if each program had a
| --for-docs argument, which causes it to simply spit out the
| canonical long-form version equivalent to whatever else it
| has been called with.
| VMG wrote:
| The thing that gets installed, if it is an executable, usually
| also has permissions to do scary things. Why is the
| installation process so scrutinized?
| stouset wrote:
| _I'm_ always a bit shocked how seriously people take concerns
| over the install script for a binary executable they're already
| intending to trust.
| romaniitedomum wrote:
| > I'm always a bit shocked how seriously people take concerns
| over the install script for a binary executable they're
| already intending to trust.
|
| The issue is provenance. Where is the script getting the
| binary from? Who built that binary? How do we know that
| binary wasn't tampered with? I'll lay odds the install script
| isn't doing any kind of GPG/PGP signature check. It's
| probably not even doing a checksum check.
|
| I'm prepared to trust an executable built by certain
| organisations and persons, provided I can trace a chain of
| trust from what I get back to them.
| mands wrote:
| Nice read up of the new FFM API.
|
| Recently saw a new FFM-based zero-copy transport and RPC
| framework using io_uring at https://www.mvp.express/
|
| An interesting time to be in the Java/JVM ecosystem, meanwhile,
| back to my Spring Boot app...tho least we're on Java 25
| pron wrote:
| Tangential:
|
| The --enable-native-access option mentioned in the article is
| part of a large effort we call "Integrity by Default"[1]. The
| idea is that a library module can violate invariants established
| by another module (e.g. access to private fields and methods,
| mutation of final fields etc.) requires approval by the
| _application_ , so that a library will not be able to have a
| global effect on the application without its knowledge, and the
| correctness of each module could be verfied in isolation.
|
| Now, --enable-native-access is also required to use JNI, but JNI
| can violate the integrity of Java invariants in a much more
| extensive way than FFM can. For example, JNI gives native code
| access to private fields of classes in arbitrary modules, while
| FFM does not. The only invariant FFM can break is freedom from
| undefined behaviour in the C sense. This is dangerous, but not
| nearly as dangerous as what JNI can do.
|
| For the time being, we decided to enable both FFM and JNI with
| the same flag, but, given how more dangerous JNI is, in the
| future we may introduce a more fine-grained flag that would allow
| the use of FFM but not of JNI.
|
| [1]: https://openjdk.org/jeps/8305968
| tadfisher wrote:
| Where does the "final means final" effort fit in? Can the JVM
| prevent modification of final fields via JNI, or is --enable-
| native-access also going to require (or imply) the flag which
| enables setAccessible() and friends?
| pron wrote:
| Ah, that's a great question, and the answer is in the JEP
| (https://openjdk.org/jeps/500#Mutating-final-fields-from-
| nati...).
|
| When running with -Xcheck:jni, you'll get a warning when
| trying to mutate a final field with JNI.
|
| Now, enabling this check by default without harming JNI
| performance proved to be too much of an effort. However,
| mutating final fields with JNI even today can already lead to
| undefined behaviour, including horrible miscompilation, where
| different Java methods can read different values of the
| field, for final fields that the JVM already trusts to be
| immutable, such as static finals, record components, or a few
| other cases (indeed, there are non-final fields that the JVM
| trusts to be assigned only once, and mutating those with JNI
| is also undefined behaviour). As the compiler starts trusting
| more final fields after this change, mutating almost all
| final fields will lead to undefined behaviour. Then again,
| using JNI can lead to undefined behaviour in many ways.
|
| So to make sure your JNI code isn't mutating finals, test
| with -Xcheck:jni (as of JDK 26).
| gorset wrote:
| This brings back memories debugging an azul zing bug where
| an effectively final optimization ended up doing the wrong
| thing with zstd-jni. It was painful enough that I couldn't
| convince the team to enable the optimization again for
| years after it was fixed.
| namegulf wrote:
| Wondering the benefits and how is this different from using
| GraalVM to build native images?
|
| For eg. we could use Spring + Graal VM and get the application
| into native binaries without worrying too much about the low
| level stuff.
|
| What are we missing?
| gavinray wrote:
| This article specifically discusses calling external C ABI
| libraries via the FFM API.
|
| GraalVM is for compiling JVM bytecode to native, architecture-
| specific binaries.
|
| FFM is like "[DllImport]" in .NET, or "extern" definitions in
| other languages.
|
| The article shows how to auto-generate JVM bindings from C
| headers, and then allocate managed memory + interact with
| externally linked libs via the FFM API passing along said
| managed memory.
| fniephaus wrote:
| BTW: We (the GraalVM team) maintain a full-blown LLVM bitcode
| runtime that can be embedded in Spring or any other JVM
| application and compiled to native:
| https://github.com/oracle/graal/tree/master/sulong
| gavinray wrote:
| May as well throw the Native Image C API for FFM-like
| capabilities out there too
|
| https://www.graalvm.org/latest/reference-manual/native-
| image...
|
| One of the neatest things I've been able to do is compile a
| .dll library "plugin" for an application which loads plug-
| ins by invoking a special exported symbol name like "int
| plugin_main()" using GraalVM and @CEntryPoint
|
| The entrypoint function starts a Graal isolate via
| annotation params and no native code was needed
___________________________________________________________________
(page generated 2025-12-07 23:00 UTC)