Brian Lovin
/
Hacker News
Daily Digest email

Get the top HN stories in your inbox every day.

mmastrac

LLVM is a trap. You bootstrap extra fast, you get all sorts of optimization passes and platforms for free, but you lose out on the ability to tune the final optimization passes and performance of the linking stages.

I think we'll see cranelift take off in Rust quite soon, though I also think it wouldn't be the juggernaut of a language if they hadn't stuck with LLVM those early years.

Go seems to have made the choice long ago to eschew outsourcing of codegen and linking and done well for it.

nikic

> LLVM is a trap.

Is it? I think Rust is a great showcase for why it isn't. Of course it depends somewhat on your compiler implementation approach, but actual codegen-to-LLVM tends to only be a tiny part of the compiler, and it is not particularly hard to replace it with codegen-to-something-else if you so desire. Which is why there is now codegen_cranelift, codegen_gcc, etc.

The main "vendor lock-in" LLVM has is if you are exposing the tens of thousands of vendor SIMD intrinsics, but I think that's inherent to the problem space.

Of course, whether you're going to find another codegen backend (or are willing to write one yourself) that provides similar capabilities to LLVM is another question...

> You bootstrap extra fast, you get all sorts of optimization passes and platforms for free, but you lose out on the ability to tune the final optimization passes and performance of the linking stages.

You can tune the pass pipeline when using LLVM. If your language is C/C++ "like", the default pipeline is good enough that many such users of LLVM don't bother, but languages that differ more substantially will usually use fully custom pipelines.

> I think we'll see cranelift take off in Rust quite soon, though I also think it wouldn't be the juggernaut of a language if they hadn't stuck with LLVM those early years.

I'd expect that most (compiled) languages do well to start with an LLVM backend. Having a tightly integrated custom backend can certainly be worthwhile (and Go is a great success story in that space), but it's usually not the defining the feature of the language, and there is a great opportunity cost to implementing one.

crawshaw

It is a “trap” in the sense that it limits the design space for your language by forcing some of the choices implicit in C++. Rust went with slow build times like C++ and so was not held back in this dimension by LLVM.

Nothing about LLVM is a trap for C++ as that is what it was designed for.

phplovesong

Both Go and Ocaml have really, really fast compilers. They did the works from the get go, and now wont have to pay the price.

I cant see myself ever again working on a system with compile times that take over a minute or so (prod build not counting).

I wish more projects would have they own "dev" compiler that would not do all the shit llvm does, and only use llvm for the final prod build.

pjmlp

They show a return to how fast compilers used to be, before C and C++ took over the zeitgeist of compilation times.

Eiffel, Common Lisp, Java and .NET (C#, F#, VB) are other examples, where we can enjoy fast development loops.

By combining JIT and AOT workflows, you can get best of both worlds.

I think the main reason this isn't as common is the effort that it takes to keep everything going.

I am on the same camp regarding build times, as I keep looking for my Delphi experience when not using it.

pjmlp

As I can't update my comment, here goes some info out of Turbo Pascal 5.5 marketing brochure,

> Fast! Compiles 34 000 lines of code per minute

This was measured on a IBM PS/2 Model 60.

So lets put this into perspective, Turbo Pascal 5.5 was released in 1989.

IBM PS/2 Model 60 is from 1987, with a 80286 running at 10 MHz, limited by 640 KB, with luck one would expad it up to 1 MB and use HMA, in what concerns using it with MS-DOS.

Now projecting this to 2025, there is no reason that compiled languages, when using a limited set of optimizations like TP 5.5 on their -O0, can't be flying in their compilation times, as seen in good examples like D and Delphi, to use two examples of expressive languages with rich type systems.

citrin_ru

> be, before C and C++ took over the zeitgeist of compilation times.

I wouldn’t put them together. C compilation is not the fastest but fast enough to not be a big problem. C++ is a completely different story: not only it orders of magnitude slower (10x slower probably not the limit) on some codebases compiler needs a few Gb RAM (so you have to set -j below the number of CPU cores to avoid OOM).

foobarbaz33

I have first hand experience of painfully slow C# compile times. Sprinkle in a few extra slow things like EDMX generated files (not C# but part of the MS ecosystem) and it has no business being in a list of fast compiling languages.

debugnik

As much as I love F#, I wouldn't add it to a list of fast compilers; although I really appreciate that it supports actual file-scoped optimizations and features for compile-time inlining.

swiftcoder

> Both Go and Ocaml have really, really fast compilers. They did the works from the get go, and now wont have to pay the price.

People tend to forget that LLVM was pretty much that for the C/C++ world. Clang was worlds ahead of GCC when first released (both in speed and in quality of error messages), and Clang was explicitly built from the ground up to take advantage of LLVM.

undefined

[deleted]

baby

Yet Ocaml is unusable. Focus on user experience before focusing on speed. Golang did both tbh.

EasyMark

I can always see myself working somewhere the money leads me. I see languages as a tool and not a "this not that" or "ewww" . Sure I have my preferences but they all keep a Rivian over my head and a nice italian leather couch to lay my head on.

pjmlp

Additionally any language that wants to be a C++ replacement, keeps a dependency on C++ as long as LLVM is part of its tooling infrastructure.

For me beyond the initial adoption hump, programming languages should bootstrap themselves, if nothing else, reduces the urban myth that C or C++ have to always be part of the equation.

Not at all, in most cases it is a convenience, writing usable compilers takes time, and it is an easy way to get everything else going, especially when it comes to porting across multiple platforms.

However that doesn't make them magical tools, without which is impossible to write compilers.

That is one area where I am fully on board with Go team's decisions.

whatshisface

A trap? New language projects would never be able to develop the architecture support and optimizer. There's nothing new in the linking stage you couldn't submit as a patch to LLVM upstream.

EasyMark

Seems more like a tr-ade off than a tr-ap. Everything has a price. I think LLVM is well worth the tradeoffs for all the stuff that it gives you. I'm sure that some better tech will replace it but calling it a trap seems like an exaggeration, it seems to work well enough for even the biggest companies and countries in the world.

dbtc

How is it a trap if languages are able to move away?

pjmlp

Not all do, some stick with their decision to live with LLVM, e.g. Swift, Julia, Mojo,...

akkad33

But isn't there a reason why so many start with LLVM ? It's highly optimised. I think mojo is not LLVM but mlir

bfrog

I hope cranelift continues to grow too. LLVM tragically has become fork hell. Every cpu vendor now seems to have a fork with enhancements for their cpu arch in some closed source package. It’s hellish as soon as you want to use another language frontend or find the inevitable compiler bug.

pjmlp

That is the magic of MIT like licenses.

How much do you think the BSDs get back from Apple and Sony?

undefined

[deleted]

geokon

Trying to understand the Zig goals a bit better..

If you care about compilation speed b/c it's slowing down development - wouldn't it make sense to work on an interpreter? Maybe I'm naiive, but it seems the simpler option

Compiling for executable-speed seems inherently orthogonal to compilation time

With an interpreter you have the potential of lots of extra development tools as you can instrument the code easily and you control the runtime.

Sure, in some corner cases people need to only be debugging their full-optimization-RELEASE binary and for them working on a interpreter, or even a DEBUG build just doesn't makes sense. But that's a tiny minority. Even there, you're usually optimizing a hot loop that's going to be compiling instantly anyway

weinzierl

"Trying to understand the Zig goals a bit better.. If you care about compilation speed b/c it's slowing down development - wouldn't it make sense to work on an interpreter?"

I've heard Rust classified as Safety, Performance, Usability in that order and Zig as Performance, Usability, Safety. From that perspective the build speed-ups make sense while an interpreter would only fit a triple where usability comes first.

ModernMech

I don't think it's right to say Zig is more performance-focused than Rust. Based on their designs, they should both be able to generate similar machine code. Indeed, benchmarks show that Rust and Zig perform very similarly: https://programming-language-benchmarks.vercel.app/rust-vs-z...

This is why the value proposition of Zig is a question for people, because if you got measurably better performance out of Zig in exchange for worse safety that would be one thing. But Rust is just as performant, so with Zig you're exchanging safety for expressivity, which is a much less appealing tradeoff; the learning from C is that expressivity without safety encourages programmers to create a minefield of exploitable bugs.

toshinoriyagi

I don't think Zig is about expressivity over safety. Many people liken Zig to C, like with Rust to C++.

Zig is a lot safer than C by nature. It does not aim to be as safe as Rust by default. But the language is a lot simpler. It also has excellent cross-compiling, much faster compilation (which will improve drastically as incremental compilation is finished), and a better experience interfacing with/using C code.

So there are situations where one may prefer Zig, but if memory-safe performance is the most important thing, Rust is the better tool since that is what it was designed for.

LexiMax

The value proposition in Zig is that it has far and away the best C interoperability I've seen out of a language that wasn't also a near-superset of it. This makes migrations away from C towards Zig a lot more palatable than towards Rust unless security is your primary concern.

logicchains

It's much easier to write ultra low latency code in Zig because doing so requires using local bump-a-pointer allocators, and the whole Zig standard library is built around that (everything that allocates must take the allocator as a param). Rust on the other hand doesn't make custom allocators easy to use, partially because proving they're safe to the borrow checker is tricky.

epolanski

Zig is a modern safer and more approachable C, that's the value proposition.

It's value proposition is aiming for the next generation of system programmers that aren't interested in Rust like languages but C like ones.

Current system devs working on C won't find Zig or Odin's value propositions to matter enough or indeed as you point out, to offer enough of a different solution like Rust does.

But I'm 100% positive that Zig will be a huge competitor for Rust in the next decade because it's very appealing to people willing to get into system programming, but not interested in Rust.

quotemstr

Excellent way to put it.

> expressivity without safety encourages programmers to create a minefield of exploitable bugs.

Also, psychologically, the combination creates a powerful and dangerously false sensation of mastery in practitioners, especially less experienced ones. Part of Zig's allure, like C's bizarre continued relevance, is the extreme-sport nature of the user experience. It's exhilarating to do something dangerous and get away with it.

Quitschquat

> wouldn't it make sense to work on an interpreter

I like the Julia approach. Full on interactivity, the “interpreter” just compiles and runs your code.

SBCL, my favorite Common Lisp, works the same way I think

geokon

thanks for bringing up Julia! I totally forgot it exists. Its really dropped off the HNniverse. Wonder why.. havent seen it mentioned in like a couple of years

delusional

> Compiling for executable-speed seems inherently orthogonal to compilation time

That true at the limit. As is often the case there's a vast space in the middle, between the extremes of ultimate executable speed and ultimate compiler speed, where you can make optimizations that don't trade off against each other. Where you can make the compiler faster while producing the exact same bytecode.

Ygg2

Limits are more important than implied. Language benchmarks, computer game performance.

IshKebab

I wouldn't say games are a corner case.

dontlaugh

For games you have to compile release builds almost all the time anyway, otherwise performance wouldn’t be playable.

chongli

This is why games ship interpreters and include a lot of code scripting the gameplay (e.g in Lua) rather than having it all written in C++. You get fast release builds with a decent framerate and the ability to iterate on a lot of the “business logic” from within the game itself.

flohofwoe

That's only true for badly written game code though ;)

Even with C++ and heavy stdlib usage it's possible to have debug builds that are only around 3..5 times slower than release builds in C++. And you need that wiggle room anyway to account for lower end CPUs which your game should better be able to run on.

stephc_int13

The gold standard for compiler speed so far is TCC. (Fabrice Bellard)

It is not even super optimized (single thread, no fancy tricks) but it is so far unbeaten by a large margin. Of course I use Clang for releases, but the codegen of tcc is not even awful.

pjmlp

I would rather vouch for Delphi, as a much more expressive, and safer language, while having blazing fast compile times.

pkphilip

True. Incremental compilation in Delphi is quite something else.

Rochus

The Go compiler is also pretty fast.

levodelellis

I'd call it not terribly slow. IIRC it didn't hit 100k lines on an M2, while I saw over two million on my own compiler (I used multiple instances of tcc since I didn't want to deal with implementing debug info on mac)

c0balt

What specifically are those 100k lines though? Golang code that, e. G., uses generics is significantly heavier than code that doesn't.

klysm

Maybe typescript will catch up

satvikpendem

It's being rewritten in Go so yes it will

brabel

I thought DMD which compiles both C and D was the gold standard!?

teo_zero

> The gold standard for compiler speed so far is TCC.

I only wish it supported C23...

oguz-ismail

Any big projects that doesn't compile without C23 support?

flohofwoe

Not yet AFAIK, but unlike the 'inbetween' versions since C99, C23 actually has a number of really useful features, so I expect that a couple of projects will start using C23 features. The main problem is as always MSVC, but then maybe its time to ditch MSVC support since Microsoft also seems to have abandondend it (not just the C compiler, but also the C++ compiler isn't seeing lots of updates since everybody working on Visual Studio seems to have been reassigned to work in the AI salt mines).

1718627440

Ladybird.

levodelellis

Yes. I once was thinking I should write a x86-64 codegen for my own compiler, but using tcc I was able to reach 3 million lines in a second (using multi-threading and multiple instances of tcc) so I called it a day. I never did check out if the linking was in parallel

burnt-resistor

There is no singular "gold standard".

vlang is really fast, recompiling itself entirely within a couple of seconds.

And Go's compiler is pretty fast for what it does too.

No one platform has a unique monopoly on build efficiency.

And also there are build caching tools and techniques that obviate the need for recompliation altogether.

von_lohengramm

> vlang is really fast, recompiling itself entirely within a couple of seconds.

Does V still just output C and use TCC under the hood?

giancarlostoro

So in theory, I can make Nim do the same?

stephc_int13

We are talking about compilation speed, not the harness that you could use around it.

I never tried vlang but I would say this is pretty niche, while C is a standard.

AFAIK tcc is unbeaten, and if we want to be serious about compilation speed comparison, I’d say tcc is a good baseline.

Kranar

Vlang uses tcc though...

xnickb

Build caching is nice until you have to disable it because it broke something somewhere. At which point it becomes very hard to turn it back on.

In other words, one needs to have absolute trust in such tools to be able to rely on them.

gertop

I've never had an issue with ccache and I've been using it forever. Are you using it over a networked filesystem or something like that for it to become confused about what needs recompiling?

pkphilip

V Lang is under appreciated. If you want to really see its simplicity and expressiveness, one look at its ORM will be enough. It is a thing of beauty:

https://modules.vlang.io/orm.html

codr7

There they go, making the same stupid mistakes as most other ORMs out there; by 1:1-mapping structs to tables as well as inventing their own SQL-dialect.

koeng

I've really appreciated the incremental builds with zig - I really like having a single static binary, and I have an application which uses a few different libraries like SQLite and luau and it compiles at nearly Go speeds.

That said, there are definitely still bugs to their self hosted compiler. For example, for SQLite I have to use llvm - https://github.com/vrischmann/zig-sqlite/issues/195 - which kinda sucks.

dcsommer

Is there a straightforward path to building Zig with polyglot build systems like Bazel and Buck2? I'm worried Zig's reliance on Turing complete build scripts will make building (and caching) such code difficult in those deterministic systems. In Rust, libraries that eschew build.rs are far preferable for this reason. Do Zig libraries typically have a lot of custom build setup?

esjeon

FYI, build scripts are completely optional. Zig can build and run individual source code files regardless of build scripts (`build.zig`). You may need to decipher the build script to extract flags, but that's pretty much it. You can integrate Zig into any workflow that accepts GCC and Clang. (Note: `zig` is also a drop-in replacement C compiler[1])

[1]: https://andrewkelley.me/post/zig-cc-powerful-drop-in-replace...

onetom

Great.

It's finally as snappy as recompiling the "Borland Pascal version of Turbo Vision for DOS" was on an Intel 486 in 1995, when I graduated from high school...

They C version of Turbo Vision was 5-10x slower to compile at that time too.

Turbo Vision is a TUI windowing framework, which was used for developing the Borland Pascal and C++ IDEs. Kinda like a character mode JetBrains IDE in 10 MB instead of 1000 MB...

https://en.m.wikipedia.org/wiki/Turbo_Vision

commandersaki

Tangential: I've been writing a Java library that is pretty much a JNI shim to a C library. Looking at building a dynamic library for each of the platform / architecture combo is looking like a huge pain in the arse; I'm toying with the idea of using the zig compiler to do the compile / cross compiles and simply be done with it.

mbrock

do yourself a favor and try it, Zig ships with headers and libc sources and its own LLVM distribution and makes cross compilation so easy it's just ridiculous, you literally don't have to do anything

metaltyphoon

I've been building a shared lib for both Android and iOS/iPadOS and using jni and jin_fn crate in Rust has been a God sent. Using cargo-ndk simplifies the building process so much.

pjmlp

If it is a plain shim, and you can use modern Java, I would rather make use of Panama and jextract.

Ygg2

It's a bold strategy - let's see if it works out for them.

But from what I remember, this still uses the LLVM backend, right? Sure, you can beat LLVM on compilation speed and number of platforms supported, but when it comes to emitting great assembly, it is almost unbeatable.

mitchellh

It only uses the LLVM backend for release builds. For debug builds Zig now defaults to self-hosted (on supported platforms, only x86_64 right now). Debug builds are directly in the hot path of testing your software (this includes building test binaries), and the number of iterations you do on a debug build are multiple orders of magnitude higher than release builds, so this is a good tradeoff.

brabel

The post says the llvm is used in all builds they measured since the self hosted backend still can’t compile ghostty.

dundarious

Incorrect, the last build is substantially faster and is using the "native" debug backend.

undefined

[deleted]

bsder

> but when it comes to emitting great assembly, it is almost unbeatable.

"Proebsting's Law: Compiler Advances Double Computing Power Every 18 Years"

The implication is that doing the easy, obvious and fast optimizations is Good Enough(tm).

Even if LLVM is God Tier(tm) at optimizing, the cost for those optimizations swings against them very quickly.

measurablefunc

I don't really get the obsession w/ compiler performance. At the end of the day you are trading performance optimizations (inlining, dead code elimination, partial evaluation, etc) for compile times. If you want fast compile times then use a compiler w/ no optimization passes, done. The compile times are now linear w/ respect to lines of code & there is provably no further improvement you can make on that b/c any further passes will add linear or superlinear amount of overhead depending on the complexity of the optimization.

pfg_

The reason to care about compile time is because it affects your iteration speed. You can iterate much faster on a program that takes 1 second to compile vs 1 minute.

Time complexity may be O(lines), but a compiler can be faster or slower based on how long it takes. And for incremental updates, compilers can do significantly better than O(lines).

In debug mode, zig uses llvm with no optimization passes. On linux x86_64, it uses its own native backend. This backend can be significantly faster to compile (2x or more) than llvm.

Zig's own native backend is designed for incremental compilation. This means, after the initial build, there will be very little work that has to be done for the next emit. It needs to rebuild the affected function, potentially rebuild other functions which depend on it, and then directly update the one part of the output binary that changed. This will be significantly faster than O(n) for edits.

timschmidt

> The reason to care about compile time is because it affects your iteration speed. You can iterate much faster on a program that takes 1 second to compile vs 1 minute.

Color me skeptical. I've only got 30 years of development under the belt, but even a 1 minute compile time is dwarfed by the time it takes to write and reason about code, run tests, work with version control, etc.

Further, using Rust as an example, even a project which takes 5 minutes to build cold only takes a second or two on a hot build thanks to caching of already-built artifacts.

Which leaves any compile time improvements to the very first time the project is cloned and built.

Consequently, faster compile times would not alter my development practices, nor allow me to iterate any faster.

dustbunny

Any delay between writing code and seeing the result is empty space in my mind where distractions can enter.

WD-42

When you are writing and thinking about code, you are making progress. You are building your mental model and increasing understanding. Compile time is often an excuse to check hacker news while you wait for the artifact to confirm your assumptions. Of course faster compile times are important, even if dwarfed relatively by writing the code. It’s not the same kind of time.

tom_

But if those passes (or indeed any other step of the process) are inefficiently implemented, they could be improved. The compiler output would be the same, but now it would be produced more quickly, and the iteration time would be shorter.

OtomotO

I think it highly depends on what you're doing.

For some work I tend to take a pen and paper and think about a solution, before I write code. For these problems compile time isn't an issue.

For UI work on the other hand, it's invaluable to have fast iteration cycles to nail the design, because it's such an artistic and creative activity

rudedogg

> If you want fast compile times then use a compiler w/ no optimization passes, done. The compile times are now linear w/ respect to lines of code & there is provably no further improvement you can make on that b/c any further passes will add linear or superlinear amount of overhead depending on the complexity of the optimization.

Umm this is completely wrong. Compiling involves a lot of stuff, and the language design, as well as compiler design can make or break them. Parsing is relatively easy to make fast and linear, but the other stuff (semantic analysis) is not. Hence why we have a huge range of compile times across programming languages that are (mostly) the same.

ben-schaaf

clang -O0 is significantly slower than tcc. So the problem is clearly not (just) the optimisation passes.

undefined

[deleted]

ComputerGuru

Are their ghostty builds statically linked by default or does zig follow in the footsteps of the C/C++ world and dynamically link everything? If statically linking (like rust) then a huge part of the incremental build times for apps with external dependencies is the lack of incremental linking in any of the major linkers. Wild linker is working on becoming the answer to this: https://github.com/davidlattimore/wild

alexrp

Zig supports both linking modes but has a strong preference for static linking - it's the default in most contexts.

tredre3

ghostty statically links all its zig dependencies. The only dynamic libraries I can see are the usual suspects (libc, X11, Wayland, OpenGL, GTK, glib, libadwaita, etc).

lerno

While LLVM certainly is a problem, consuming over 90% of compile times both for C3 and Odin, it should be noted that both languages compile MUCH faster than Zig, achieving in general sub second compile times while using LLVM and without incremental compilation. So the question could be asked: ”what is making Zig so slow to compile” as both languages have similar feature sets compared to that of Zig.

lerno

I should add that there are a few candidates:

1. Zig uses three(!) frontend IRs, Odin and C3 only one.

2. Zig relies heavily on comptime to provide most of its language features. C3, which has a similar set of features, doesn’t encourage excessive use of compile time and offers more features built into the language (for example format checking is a builtin, whereas in Zig it’s userland, even if C3 has the capability to do it like Zig). Although note that Zig only checks code directly traced, whereas C3 will check all code, and so will - esp for simple code - check much more code than Zig does.

3. Zig generates A LOT of code for even just a ”safe” Hello World.

On top of that, maybe there are just some other inefficiencies that hasn’t been addressed yet by the Zig team?

Even though I have stakes in the game I hope Zig gets faster. We need to move away from the illusion that Swift, C++ and Rust compilation speeds are anywhere near acceptable.

Simran-B

How does Zig's compilation and codegen compare to TPDE? The claim is that it's 10-20x faster than LLVM with -O0 but with limitations.

ozgrakkurt

Speed feels even faster than 20x and debug mode code runs ok. Don’t know about assembly quality difference

62

[dead]

teo_zero

Anybody knows what exactly prevents Ghostty from being built using the self-hosted x86_64 backend?

mitchellh

Bugs. The Zig compiler crashes. It’s new and complex, I’m sure they’ll work it out soon.

anonymoushn

well, most of the users are aarch64

Daily Digest email

Get the top HN stories in your inbox every day.