← Back to context

Comment by sxzygz

3 days ago

This article is really about the inline assembly syntax developed for the author's programming language Odin (and definitely nothing about TALs, typed assembly languages). There are a lot of interesting ideas here.

One of my criticisms, however, is simply pointing to how similar mainstream general purpose CPU architectures have become; they are all C machines. This radically simplifies the complexity on the compiler front where, it seems, the author is targeting amd64 and aarch64. Extending the compiler to rv64 will probably be straightforward.

I don't know anything about Odin, or its compiler implementation, but I imagine the language adheres to a view of the machine that matches the C machine model. Imagine a more esoteric language, the compiler would probably need an intermediate language matching the C machine model and in which the inline assembly would have to have survive some idempotent lowering to the intermediate representation before being further lowered to the object code. These details are what I am really curious about and probably the most intellectually stimulating.

The most interesting possibility is if the Odin compiler is itself written wholly in Odin. If this were the case, it would really show the power of the inline assembly syntax. As far as I am aware no optimizing compiler has really pushed this angle whilst targeting multiple instruction architectures. If I recall correctly, even the Plan9 C compiler moved some basic optimization to their genericized assembler, and I've not kept up with it as it's evolved into the current Go compiler.

Very interesting work as I have often though about inline assembly syntax in a high-level language. Keep it up gingerbill.

No, modern CPUs are not at all C machines, they are about as far of C machines as one could imagine, because they now implement in hardware hundreds of instructions that were unheard of in a DEC PDP-11.

The C language has only 2 kinds of integer data types, signed and unsigned, of various sizes. Moreover, the implicit conversions between them are erroneously defined and lead to data corruption, unless the programmer is extremely careful.

Modern CPUs, like those implementing the Intel/AMD x86-64 ISA or the Arm Aarch64 ISA, have 8 different kinds of integer data types, all of various sizes. For all these different data types the CPUs have dedicated instructions that implement in hardware various operations with them.

It is impossible to access in the right way from C all these data types. Only in C++ one can define custom data types and implement appropriate operations for them using inline assembly or separate assembly source files.

Those 8 data types are signed integers where overflow causes an exception, signed integers where overflow causes saturation, non-negative integers where overflow causes an exception, non-negative integers where overflow causes saturation, integer residues a.k.a. modular integers, bit strings, binary polynomials and binary polynomial residues (i.e. elements of a Galois field).

Unfortunately, most programming languages have not gone beyond the level of C, so they do not allow the efficient use of modern CPUs otherwise than by using inline assembly or compiler intrinsics.

Thus there is a great mismatch between most high-level programming languages and modern CPUs, the opposite of what the poster above said.

The mainstream CPUs have become very similar between themselves, but very different from the C machine model inherited by most modern programming languages.

  • > so they do not allow the efficient use of modern CPUs otherwise than by using inline assembly or compiler intrinsics.

    When one provides the full effect of the operation in the source code, a properly ported compiler backend should be able to spot the pattern and emit the instructions with matching non-C semantics. (Typically, saturated operations are a very low hanging fruit.)

    That doesn't always work because the optimization passes targeting local optimum break these patterns while "optimizing" them, so they arrive to the instruction emitter unrecognizable. And these passes, living in the generic "good-for-all" area of the compiler core, cannot be made aware of what a particular target does or doesn't support. So ironically, such instructions appear more when the optimizations are disabled.

    Intrinsics emit internal representation forms that optimization passes don't dare to touch.

  • Most architectures perform best in combination with a compiler of a statically typed language. SPARC at least still had rudimentary support for tagged data types.

> The most interesting possibility is if the Odin compiler is itself written wholly in Odin.

Currently, it is not (C++, mostly C style). As far as I can remember, Bill has previously said that a self-hosted version of the compiler might be a possibility, _after_ the 1.0 release and when the full spec of the language has been written.