Zig is now self–hosted by default
github.com
github.com
* stage1 is when zig is built from C++ code[1] (the "bootstrap" compiler) using system C/C++ compiler toolchain.
* stage2 is when zig is built from Zig code[2] using stage1.
* stage3 is when zig is rebuilt from the same Zig code[2] using stage2.
Before today, zig would give you stage1 by default, and you could opt in to stage2 using `-fno-stage1`. After today, zig gives you stage3 by default, and you can opt in to stage1 using `-fstage1`.
In all three cases, LLVM is being used. Although Zig has started to fully self-host by providing backends that have no dependency on LLVM, none of these fully self-hosted backends are complete.
Why bother self-hosting? Because:
* Zig produces smaller, faster binaries than C++ that use less memory[4]. Case in point: the new self-hosted compiler is 1.5x faster than the C++ implementation and uses 3x less peak RAM.
* Development velocity in Zig is much faster than C++, and debugging is a breeze in comparison due to Zig being much safer than C++ and having better debug tooling. Similarly, comptime features let us add more assertions and debug checks that are not possible in C++.
* Zig compiles much faster than C++.
* Zig cross compiles better than C++ making it easier to create builds of the compiler for every target.
* The self-hosted compiler does not need a softfloat library dependency to support f16 or f128 operations.
* Zig's std lib data structures are incredibly useful, especially compared to the C++ STL.
There are still many known bugs; not every project will be able to upgrade immediately. See the full upgrade guide [3] for help deciding when and how to upgrade.
[1]: https://github.com/ziglang/zig/tree/master/src/stage1
[2]: https://github.com/ziglang/zig/tree/master/src
[3]: https://github.com/ziglang/zig/wiki/Self-Hosted-Compiler-Upg...
[4]: https://media.handmade-seattle.com/practical-data-oriented-d...
I am not sure it is comparable: the self-hosted compiler uses a different architecture [1].
(Also, you're replying to the primary author of Zig and presenter of that talk, if you weren't aware.)
https://docs.google.com/spreadsheets/d/14_ljFHGFXY5NhBhlfjgk...
What debug tooling are you referring to? It seems like people just use lldb to debug zig programs?
I'm honestly interested, and searching doesn't turn up good tools as far as I can tell.
* safety checks (equivalent to UBSAN) when triggered, print stack traces that include source code lines and point to the relevant line/column
* one of the safety checks is using the wrong field of an untagged union. This one is so underappreciated. Wrong union field access costs so much time to debug in C++ but it is a breeze in zig, you get a crash explaining the problem with a stack trace immediately, not a corrupt value that causes problems down the road. untagged unions are core part of the strategy that makes zig fast & have a small memory footprint.
* valgrind client request integration[1]. valgrind "just works" better with Zig than C/C++ because zig emits communicates more information about memory regions that are "undefined".
* error return tracing [2]
* segfaults print stack traces
* std.debug has features to collect stack traces in a compact manner and then dump them at a relevant time [3]
* std.heap.GeneralPurposeAllocator detects leaks and prevents memory corruption from Use-After-Free/Double-Free, making debugging easier
[1]: https://valgrind.org/docs/manual/manual-core-adv.html#manual...
[2]: https://ziglang.org/documentation/master/#Error-Return-Trace...
[3]: https://github.com/ziglang/zig/blob/4a98385b0aa3808ab05a1ebf...
Because C++ defaults are notoriously all wrong, in programming language design you could usually do worse than consider, "Is there an alternative to what C++ does here by default?" and if there is one, choose that alternative as your default because it's more likely to be correct.
Sometimes C++ helps you out, it might have a keyword "do_it_right" and you can just default to that, if you feel people might want the C++ default behaviour, feel free to reserve "do_it_wrong" in case people wanted that, but in many cases they will never ask you to implement the C++ behaviour because it's just wrong. Examples: "const" in C++ doesn't mean "constant" it means "immutable", so, make immutable your default and offer "mutable" as the option. "explicit" in C++ makes constructors need an explicit cast to be used for conversion, once again of course you want that by default but feel free to reserve "implicit" in case some people are sure they need the C++ behaviour.
Those that care know where the knobs are to re-enable those proper defaults, given that for some domains, C++ is going to stay around for a while, regardless of the alternatives.
I think elaborating on this could make for a very interesting article. :-)
(I have a guess already at one item, which is MultiArrayList, something that kind of blew my mind when I first learned about it, and is in my opinion a very impressive testament to both the power and ease of use of Zig's approach to compile-time computation.)
https://github.com/ziglang/zig/blob/master/lib/std/multi_arr...
Zig's still got it beat handily in simplicity though -- I think what's really cool about Zig's idea of comp-time evaluation is you get something like the power of C++ with something like the simplicity of C.
This seems fairly significant to me and makes me wonder how this is possible with all the effort that has gone into optimizing C++
While zig can work use modern techniques without such issues
Which is used all over the world?
What's the difference between the stage2 and stage3 binary? Does stage1 produce different binaries for the same input compared to stage2/stage3?
Pretty impressive.
I suspect the difference mainly comes down to the fact that Zig compiles everything into a single compilation unit. As you can see, this has tradeoffs. It produces better code (single-compilation-unit is what "LTO" approximates) but it requires more memory and more code needs to be rebuilt on changes.
You either have to set up an ever increasing chain of compilers, as the complexity of the language and the features required to build the compiler grows, or rely on pre-existing binaries. Either way, it seems like a nightmare compared to keeping it in plain old C and building with any compiler of your choice. Just Imagine if any project did this, like, building Firefox now requires an existing Firefox installation.
Ruby and Python are scripting language, as such seldom bootstraped.
Except for of course a c compiler.
But this is exactly the same for C! Building C compiler requires an existing C compiler. C compilers are admittedly more widely available at the moment, but that probably won't be the case forever. Languages like zig aim to supplant C. How are they supposed to do that when they're directly dependent on C?
[I watched a few hours of Jon doing this, but annoyingly I can't find a recording of Jon actually finding and fixing the subtle Heisenbug he realises during those hours must be in his Jai code - he resolves to investigate "later" and I can't find "later". I think this would be insightful to watch because this is exactly the sort of bug Jon says he doesn't have much trouble with in his C++ and so doesn't need to prevent in Jai... so if it's five minutes to fix that shows Jon is correct, if it's hours of difficult searching or eventually was resolved by just unrelated changes to the code not so much]
One consequence is that pointer bugs in the compiler have become extremely rare.
Feels good.
[1] Atmels cheap USB programmer would lose it's mind if the programming pins aren't all connected[2]. Tech had to reset it manually by unplugging the USB cable. If it happened a couple of times in a row, windows would disable the USB port.
[2] Board house liked to put their inspection stamp on top of the programming port pads.
Would you prefer if Zig not only didn't self-host, but didn't have any self-hosting dependencies? For your ideal compiler, would a full build from scratch require making a platform-specific assembler from machine code, then a few stages of successively higher-level languages?
A great outcome of bootstrapped compilers is that most contributors can use the language they known instead of two, and better it kills the myth that C is the only game in town for writing compilers.
A compiler is a pretty pure, high-level operation. Makes no sense to use a low-level language for it.
Hence why stuff like MaximeVM, Jikes and now Graal are so relevant.
I wish that all language devs would provide a way of compiling the build tools with something that nearly everybody already has, like C or C++. Having language support baked into a commonly used compiler like gcc is also nice. I think there are good efforts to get a rust compiler into gcc, and I believe it already works for Go and D and a few others. Such things make adoption outside the standard Windows/Linux world much, much easier.
[1]: https://en.m.wikipedia.org/wiki/Eating_your_own_dog_food
Because of this, Virgil has no dependencies on other compilers or languages, other than for testing (bash and a little C).
Is there a reason we should weigh the Bootstrappable Builds project's opinion highly here?
There's nothing on their benefits page that is subverted by an auto-generated compiler that's hand maintained.
Actually, there's no mention of auto-generated code in their best-practices section. Do you have a link to where they even say this?
You're right, it also requires a Linux kernel, and of course, you also have to trust the hardware you're running it on. Still, it reduces the amount of stuff we have to take for granted as trusted, which I think is a good thing. (I'm not involved in the project, just an admirer).
[1]: https://github.com/oriansj/bootstrap-seeds/blob/b09a8b8cbcb6...
It also makes me nostalgic for the first time I compiled a program (on the c64).
Awesome work guys. I am so looking forward to this language!
Based on this issue [1] I have a feeling JetBrains is keeping their eye on Zig for an official plugin.
Zig + high powered IDE support would be magical.
Also imagine it's the same on Windows. Anything that can manage a PDB file should work
Can't wait to see what comes down the pipeline for such a fantastic language!
At the same time, the reason this milestone seems so significant is because the language is already quite complex. It seems likely to get even more complex over time. Some metrics of complexity that jump out are the amount of compiler source code, compiler compilation time and required resources (cpu/memory). There are other languages that have simpler and more efficient bootstrap compilers.
Ideally, a self hosting compiler contains only the minimum amount of information that is strictly necessary to compile itself. It is possible to write one for a simple, imperative, javascript style language in at most a few thousand lines of code in pretty much any reasonable turing complete language (something like lisp with barely any syntax can be done even more succinctly). Because the simple language is so small, the implementation can also be written in a high level language that targets that same high level language, i.e. the bootstrap compiler could be a transpiler to javascript written in javascript. There is little point in compiling to native assembly during the bootstrap process because the language is so small that a good javascript implementation should be capable of compiling the bootstrap source in under 200ms on a decent laptop. Once the compiler can compile itself, then one can add more features to it, such as a more robust type system, a native backend or automatic memory management without gc. A positive feedback loop emerges because these features are implemented as optimizations to the compiler itself. It gets faster as more features are added to it but it is always fast because it was fast from the beginning. With good benchmarking in place, it should never get slower at compiling itself.
Such a compiler/language would be very minimal by design but a more batteries included language can be built on top of it, much like how an os kernel is extended by user space programs.
A historical example of this kind of self-hosting compiler is Forth. It is easy to bootstrap (see e.g. JonesForth). Derived programs are written by extending the compiler with a new vocabulary specific to the problem at hand. Development is often done in a REPL environment for fast feedback. REPL sessions can be saved as source files once the program works as expected. Forth syntax is unfortunately inscrutable to most and the stack based design is not great for every problem so I wouldn't recommend actually using it, but it contains important ideas that can be adopted into more modern languages.
What the zig team has done strikes me as very, very difficult, so I tip my cap for the effort it must have required. In the long run though, it feels almost inevitable that a simpler language with the more desirable high level properties described above will eat its lunch. As painful as it would be, I believe the best thing the zig team could do to ensure the language's long term survival would be to completely rewrite the language from scratch (zag?) using the knowledge that they've gained during their initial bootstrap process to distill zig to its essence.