Live data from Hacker News

Hobby x86 kernel written with Zig

github.com

91–100 of 229 posts

Re: Hobby x86 kernel written with Zig

#91
post #64
post #22

Earlier quoted context omitted.

Thank you. I sure tried C too (also C++ for gui programming), though I wouldn't say I "know" it (which to me would imply at the very least one significant real-world experience with it), I do understand why some projects try to modernize "system" programming. I just want to evaluate alternatives, but I may very well go for C in the end...

C is portable assembly, I’d recommend learning it together with the assembly for the platform you’re learning on, so you can actually understand the stack, heap, calling conventions, etc.

C is portable assembly until it isn't:

- no access to carry/overflow flag in registers making it a chore to write bigint libraries

- no way to guarantee emitting add with carry or sub with borrow even though those are simple instructions and some architecture (6502) don't even provide a normal add/sub

- need to drop down to assembly to implement resumable function/fibers to avoid the syscall costs of ucontext/setjmp/longjmp

- no access to hardware counters like RDTSC

- no way to get the CPU frequency in a portable and reliable way

- no portable threadID function and then, those that are available (pthread_self, and Windows') rely on expensive syscalls.

- no way to do CPU feature detection (SSE, AVX, ARM Neon, ...)

- no way to control emission of common intrinsics like popcount, bit-scan-reverse, lowest bit isolation in a portable way.

The portable assembly narrative breaks down rapidly when you actually need specific code to be emitted.

Re: Hobby x86 kernel written with Zig

#92
post #79

Earlier quoted context omitted.

In general you'll find that zig is easier to read than Rust (see the first version of this project in Rust [0]) because it's a simpler language. For kernel programming this is even more so the case: * zig has native support for arbitrary sized integers. In Rust I used to do bitshifts, Now I just have a packed struct of u3/u5/u7 whatever (see `src/pci/pci.zig`). Of course Rust has a bitflags crate but I didn't find it…

It's true that for bare metal programming Rust is quite far from 1.0. You can't even do inline assembly in stable rust... Arbitrary sized integers do sound very convenient, although I guess the counterargument would be that they simply can't map to machine types so you'll have to have magic behind the scenes which may not make complete sense in low level languages. Still, C has bitfields in structs which are sort of…

I don't think bitfields are even that portable in C? You end up with a weird mix of bit-endianness and byte-endianness issues. The best approach AFAICT, if you need to model a binary format with arbitrary-sized integers, is to keep the binary structure opaque and have a function to "unpack" it to a record of machine-native types. Modern compilers can easily optimize the resulting code into shifts+bit operations, and it is a reasonably foolproof approach.

Re: Hobby x86 kernel written with Zig

#93
post #76

Earlier quoted context omitted.

comptime does not replace macros though...there is no way to manipulate AST in zig, nor will there ever be, according to the author.

Which is the right call really. It is valuable to have the code you're looking at actually be what it appears to be.

I don't agree.

For many domains, being able to implement a domain-specific language with a set of expressive rules for the domain is an incredible productivity boost and also prevents many mistakes because you cannot represent them.

Not being able to manipulate the AST means that you are restricted on the embedded DSLs that you can provide. And embedded DSLs encompasses code generation for:

- state machines

- parsing grammars (PEGs for example)

- shader languages

- numerical computing (i.e. having more math like syntax to manipulate indices)

- deep learning (neural network DSL)

- HTML templates / generators

- monad composition

There is a reason most people are not building in Assembly anymore, there is a right-level of abstraction for every domain. A language that provides building block for domain expert to build up to the right level of abstraction is very valuable.

Re: Hobby x86 kernel written with Zig

#94
post #93

Earlier quoted context omitted.

Which is the right call really. It is valuable to have the code you're looking at actually be what it appears to be.

I don't agree. For many domains, being able to implement a domain-specific language with a set of expressive rules for the domain is an incredible productivity boost and also prevents many mistakes because you cannot represent them. Not being able to manipulate the AST means that you are restricted on the embedded DSLs that you can provide. And embedded DSLs encompasses code generation for: - state machines - parsing…

[deleted]

Re: Hobby x86 kernel written with Zig

#95
post #93

Earlier quoted context omitted.

Which is the right call really. It is valuable to have the code you're looking at actually be what it appears to be.

I don't agree. For many domains, being able to implement a domain-specific language with a set of expressive rules for the domain is an incredible productivity boost and also prevents many mistakes because you cannot represent them. Not being able to manipulate the AST means that you are restricted on the embedded DSLs that you can provide. And embedded DSLs encompasses code generation for: - state machines - parsing…

The question is, as always, at what cost?

Low-level languages (aka "systems programming" languages) already suffer from various constraints that increase their accidental complexity. Is it really necessary to complicate those particular languages further to support embedded DSLs?

I don't think there's a universal right or wrong answer here, but there is certainly a big question.

Re: Hobby x86 kernel written with Zig

#96
post #82

Earlier quoted context omitted.

> including the vast majority of code editors by default Which ones exactly? Not really a problem I've encountered often except when someone tries to mix both spaces and tabs, and in general editors are built with the existence of this very common character in mind. People tend to hit the tab key to indent anyway, and one tab char meaning one level of indent is perfectly intuitive and allows users to individually con…

Many editors (including the venerable Vim and Emacs) indent and align with tabs by default, which means that if you want your code to look right you standardize tab width which in turn removes one of the only (meager) advantages tabs have over spaces: configuring the indentation width to match your personal taste. >Not really a problem I've encountered often except when someone tries to mix both spaces and tabs Mixin…

>Mixing spaces and tab for indentation and alignment is how it should be done if you want things to remain aligned when you change the tab width.

How it should be done is to never write code that needs alignment if you're using tabs for indentation.

Re: Hobby x86 kernel written with Zig

#97
post #86
post #76

Earlier quoted context omitted.

comptime does not replace macros though...there is no way to manipulate AST in zig, nor will there ever be, according to the author.

Well, it is intentionally weaker than macros (and I agree with Zig's designer that that's a very good thing, though it is a matter of taste), but it does replace many of the cases where in Rust you'd have to use macros (or the preprocessor in C/C++). So it replaces macros everywhere where it deems their usage reasonable.

It is a legit design choice, but it does detract from your comment about language complexity. Not having AST macros inherently adds complexity to a language by requiring features to be built into the compiler rather than be implemented as libraries.

Re: Hobby x86 kernel written with Zig

#98
post #76
post #44

Earlier quoted context omitted.

I wouldn't call Zig's comptime "C++-style." Unlike Rust, there's very little in Zig that is borrowed from C++. Zig's error reporting and comptime makes it easy to write arbitrary compile-time checks, so Zig uses a single construct and keyword, comptime, to replace all special instances of partial evaluation: generics, concepts/traits, value templates, macros and constexprs. The main difference between Zig and Rust is…

comptime does not replace macros though...there is no way to manipulate AST in zig, nor will there ever be, according to the author.

You can't manipulate ASTs (ie, Zig code), but nothing stops you from parsing string literals however you want.

For example, I wrote a PEG-like parser combinator library in Zig. Using it currently [looks like this](https://github.com/CurtisFenner/zsmol/blob/87de4c77dd8543011...). However, as a library, I ^could provide a function that looks like

    pub const UnionDefinition = comb.fromLiteral(
        \\ _: KeyUnion
        \\ union_name: TypeIden
        \\ generics: Generics?
        \\ implements: Implements?
        \\ _: PuncCurlyOpen
        \\ fields: Field*
        \\ members: FunctionDef*
        \\ _: PuncCurlyClose 
   );
etc. But, I find reading the code as it is good enough for now that I didn't want to spend time implementing such a library.

[^]: Being able to create brand new types at `comptime` isn't [yet implemented](https://github.com/ziglang/zig/issues/383), so this can't quite be done yet, though you could fake it with `get`/`set` methods instead of real fields

Re: Hobby x86 kernel written with Zig

#99
post #6

Earlier quoted context omitted.

> I used to do this in Rust but I switched to zig for maintainability and readability over Rust. Can you expand on this ? I'm asking out of curiosity because I want to learn a "system" programming language (for whatever definition there is to this term). So far I briefly tried Rust and Nim and found the former more difficult to read. I know nothing about Zig, how would you place it between these two ?

Bouncing on recently featured HN thread "Hello World": https://drewdevault.com/2020/01/04/Slow.html (and the HN thread: https://news.ycombinator.com/item?id=21954886 ) According to this resource, Zig produces code that is very close to the hand written assembly for the simple use case of outputing "hello world" on the stdout. This is to be taken with a grain of salt though, as of course, caring about the assembly out…

> ... caring about the assembly output is a spectacular case of premature optimization.

This is a hobgoblin. Assembly output of a compiler is the very baseline of performance. It requires no effort on behalf of the programmer (aside from learning a language).

Now, if you're skimming the assembly after every compilation and, say, fuzzing your implementation to coax the compiler to emit the best possible assembly... that's probably premature. If you're writing inline assembly before doing a higher level implementation, that's probably premature.

But choosing a language on the basis of its performance:effort ratio is downright pragmatic.

Re: Hobby x86 kernel written with Zig

#100
post #79

Earlier quoted context omitted.

In general you'll find that zig is easier to read than Rust (see the first version of this project in Rust [0]) because it's a simpler language. For kernel programming this is even more so the case: * zig has native support for arbitrary sized integers. In Rust I used to do bitshifts, Now I just have a packed struct of u3/u5/u7 whatever (see `src/pci/pci.zig`). Of course Rust has a bitflags crate but I didn't find it…

It's true that for bare metal programming Rust is quite far from 1.0. You can't even do inline assembly in stable rust... Arbitrary sized integers do sound very convenient, although I guess the counterargument would be that they simply can't map to machine types so you'll have to have magic behind the scenes which may not make complete sense in low level languages. Still, C has bitfields in structs which are sort of…

The problem with C-style bitfield values/arbitrary-sized integers is that they don't have addresses, so you can't take references to them. This means that you need special cases everywhere, and it increases the complexity of the language a lot. In Rust, integers often have methods that take self by reference, which demands an actual address. For these reasons, in Rust I think it's better that arbitrarily-sized integers aren't first class. They're never really first class anyway.
Post reply on HN