C Is Not a Low-Level Language (2018)

tosh 93 points 91 comments September 07, 2026
queue.acm.org · View on Hacker News

Discussion Highlights (17 comments)

actionfromafar

If anyone was thinking, but in practice it is a low level language, behold Fil-C.

legobmw99

I’ve been a fan of this article for years, though it does often make me think that there really aren’t any true low level languages for our super scalar modern CPUs. Does anyone know of any?

veqq

This is one of my favorite papers; it stole about a year and a half of my time. I still pine for Lisp processors although array languages can now self-host on GPUs, which, APL-pilled, I now feel is better. It'd be so cool (...for compiler writers) to be able to control precisely which kernels stay in which cache levels etc.

EGreg

It's just a matter of personal definitions, it seems. Here is an example: https://ulanguage.org What level would you say this language was? Is it a low-level systems language, or is it also usable for writing web sites?

applfanboysbgon

This article is so blatantly fallacious I can't even get past the first couple of paragraphs. Perhaps it makes a stronger case later in the article, but the early claims it makes invoke Meltdown/Spectre, eg. speculative execution, and your CPU being more advanced than a PDP-11, and that C doesn't expose modern CPU features like speculative execution, therefore C is not low-level. But assembly doesn't either. You could attempt to make the claim that assembly is no longer a low-level language, but the article explicitly does not do this, instead listing assembly as the low-level extreme that C is being compared against. This is embarrassingly bad.

glouwbug

Maybe not then, but we basically have our own poor man's template system now: #define array(T, N) struct array##T##N { T value[N]; } void copy(array(int, 32)* x, array(int, 32)* y) { *x = *y; } int main() { array(int, 32) x; array(int, 32) y = { 1, 2, 3, 4 }; copy(&x, &y); } With (rumors of) lambdas and defer on the way, C is going the way of classic WoW. https://en.wikipedia.org/wiki/C29_(C_standard_revision)

fsckboy

> and even the pre- and post-increment operators cleanly lined up with the PDP-11 addressing modes. pre- and post- increment operators cleanly lined up with... the programmer's conceptualization and objectives--the index is/was frequently used in other contexts than loop bounds and indexing. if that's not your conceptualization, don't use that operator. whether you are on a PDP-11 makes no difference.

blastonico

In this sense, not even assembly is a low-level language because an instruction may hide what the microcode is actually doing. IMHO, C is the lowest level a procedural programming language can get.

bee_rider

“Low level language” is one of those terms like “VLSI” (very large scale integration) where they defined it in the 70’s or something, so the academic definition is out-of-sync with what most people would expect. This is fine, it’s a term of art and those don’t need to be immediately obvious. I don’t like the title of this article for that reason, though. Really a better title would be something like “a modern x86 processor is not a PDP-11.” The subtitle is perfect basically. Edit: also IMO it is not really fair to beat up on C for this, the problem is not really one of low-level-ness. A language that actually exposed the complexity of speculative execution and all that could be pretty high level. It would just be harder to read in a linear text editor, right? We’d be better off drawing the dependency graph or something.

Peteragain

Okay. I like this article and I've thought about it regularly since it last made the rounds here. 1) C is a low level language for a PDP11, or for a single core on a GPU. 2) But what would a low level language look like for an FPGA? Probably verilog. 3) The point worth pursuing however is whether there might be a Hardware agnostic "low level language". 4) yep Haskel by the looks of things. If only I could find the reference.. :-/ There's a set of slides from a crew in Edinburgh doing the history of functional languages. Does any one remember something similar?

jrhey

I’d say assembly is the lowest level programming language we have. You have to balance the abstraction of hardware instructions with being human readable to also qualify as a programming language I don’t think byte code qualifies as human readable but it is closer to the metal obviously

weitendorf

This is such a pedantic point IMO. C is low level because it makes it very easy to work with machine language/assembly and do stuff like this (LLM assisted example follows): int main() { __m512i vecA = _mm512_setr_epi32(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); __m512i vecB = _mm512_setr_epi32(0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75); unsigned short mask = 0; __asm__ ( "vp2intersectd %[B], %[A], %%k2" : "=@cck2" (mask) : [A] "v" (vecA), [B] "v" (vecB) : "k3" ); printf("Intersection Mask: 0x%04X\n", mask); return 0; } This is something "low level" programmers use very often to realize the benefits of a high-level language while exercising explicit control over using specific hardware instructions (vp2intersectd being an AVX-512 instruction used in highly optimized search algorithm impls). Obviously if you rely on implicit behavior from the compiler to optimize your code you are no longer "low level". But if you can quickly and easily drop into machine-level instructions to provide explicit implementation semantics, and the language indeed makes that relatively simple and easy to do, that sure seems "low level" to me

serbuvlad

C is a low-level language for the current ISAs we have, though not for Itanium. So the question is if we really want lower level ISAs. Probably not? There are many ways in which our current ISAs are actually thoughtfully optimized for superscalar out-of-order processors. Just look at all of the big differences from 32 bit arm to 64 bit arm, which all exist to make execution faster on superscalar processors. And yet they are still perfectly implementable in cheap microcontrollers. The Cortex-A53, available in boards for a little over $15, is a simple 2-wide perfectly in-order design, without a physical register page beyond the ISA register. Basically, it is a simple Pentium-type chip. The Apple M chips are some of the most impressive feats of out-of-order superscalar micro-engineering ever. And yet both of these can run the same software with the same ISA. This is enormously valuable. I fail to see how any sort of much lower level access to the machine would be portable across price ranges and microarchitecture generations. I also fail to see how it would provide a non-trivial speedup over C code pattern recommendations and targeted extensions (eg. vector extensions).

p0w3n3d

C is not a low level language. It's a macro assembler

WillPostForFood

From the preface of first edition The C Programming Language. Just interesting to note the authors never claimed it was low level, just not "very high level." --- C is a general-purpose programming language with features economy of expression, modern flow control and data structures, and a rich set of operators. C is not a "very high level" language , nor a "big" one, and is not specialized to any particular area of application. But its absence of restrictions and its generality make it more convenient and effective for many tasks than supposedly more powerful languages.

spaintech

I enjoy this article showing up here once in a while. It makes me think about the stack of abstractions we actually live in… CPU -> microcode -> ISA -> firmware/BIOS -> OS + drivers -> C abstract machine -> your app. ( I left our virtualization purposely thinking of a bare metal stack ) Current ISAs have so much machinery underneath that it’s hard to tell when you’re talking to the iron and when you’re talking to the microcode You can still argue that Forth on a Forth CPU is a genuinely low-level language. :)

melodyogonna

Well, C does give you the control when it matters, even if they feel bolted on for more modern features. I actually believe Mojo is the only modern language not designed to pretend every computer is a PDP-11. C has been so successful that many succeeding languages just did C things as a matter of course. In Mojo, everything is designed with the complexity of the modern computer in mind, and at every stage the programmer has complete control of outcomes. You decide what gets inlined, what gets passed in registers, what gets unrolled, etc. The language has excellent ... ney, probably the best portable SIMD support there is; all integers are built on top of SIMD, and the scalar integers are just SIMD with length of 1. You get complete control of what gets compiled as well due to powerful compile-time programming that is similar to, but more powerful than Zig's (imo, because you can supply a lot more information). While C and Rust allow inline asm, Mojo goes further by letting you supply inline MLIR and LLVM as well, so in situations that warrant it, you can tell the compiler to compile to a specific LLVM intrinsic. The language also does not assume you're compiling to run on just one machine; every modern computer is heterogeneous by nature and may contain multiple programmable units, so the compilation pipeline is designed to allow compiling certain parts of code for one target and other parts for other targets... as one compilation unit.

Semantic search powered by Rivestack pgvector
5,804 stories · 52,625 chunks indexed