| ▲ | C Is Not a Low-Level Language (2018)(queue.acm.org) |
| 82 points by tosh 5 hours ago | 77 comments |
| |
|
| ▲ | weitendorf 2 hours ago | parent | next [-] |
| 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 |
| |
| ▲ | II2II an hour ago | parent | next [-] | | There are many reasons why C is not a low level language. Take the example: while `asm()` blocks are a common extension to C compilers, anything within the block is (a) compiler dependent and (b) architecture dependent. To choose an extreme counter example: you may as well claim that versions of BASIC with the POKE keyword are low level languages simply because you can POKE machine code directly into memory. Yet one of the more interesting reasons, in my mind, is that C adds a tonne of abstractions. The roster of data types is one of those abstractions. Processors have a very weak notion of data types, and memory has absolutely no notion of memory types at all. For example: casting a `float` to an `int` has a very specific definition in C, and that definition involves altering the pattern of bits. While you can create a float and force the C compiler to regard that memory location as an int (via casting pointers), it isn't how the language is meant to be used (outside of rare cases). If I recall correctly, some of the direct predecessors of C were typeless, which is closer to how the CPU and RAM treat data. | | |
| ▲ | tremon 35 minutes ago | parent | next [-] | | > Processors have a very weak notion of data types This is absolutely not true, unless you mean to say that processors should somehow support composite (aka C struct) data types as an instruction primitive. Processor operations have to be strongly typed, by definition. For example, these are the data types supported by operations in the modern x86 instruction set (ignoring vector extensions): - signed and unsigned integers of 8, 16, 32 and 64 bits - floating-point decimals of 32, 64 and 80 bits (and 128 via sse) - nul-terminated byte strings > For example: casting a `float` to an `int` has a very specific definition in C, and that definition involves altering the pattern of bits I don't understand this example. Casting a float to an int also has a very specific definition in IEEE-754 and is pretty much universally implemented as a hardware instruction. It has been in the x86 family since its inception: https://www.felixcloutier.com/x86/fisttp | | |
| ▲ | Pannoniae 15 minutes ago | parent [-] | | I wholly agree, processors are strongly typed, even if there are holes like using integer instructions on floating-point values in XMM regs, very insightful comment:) btw a bit of nitpick: to be fair basically no one uses x87 anymore, it's https://www.felixcloutier.com/x86/cvttss2si and friends but yes :) | | |
| |
| ▲ | weitendorf 44 minutes ago | parent | prev [-] | | That's fair. C is very old and used for almost all hardware so I think while you can make the argument that "only clang and gcc extensions asm blocks available like that, and intrinsics are only available through vendor-specific headers" and be right, by that same logic literally nothing except binary machine code for hardware without any kind of microcode can be low-level, and even then it's probably always hardware dependent (because if it's not fully bijective to the actual hardware it's implemented on top of, the semantics leak). Practically speaking, we have a word for the kind of "abstractionless" model you're describing: machine code. I mean, even assembler is a bunch of abstractions about 'registers' and 'instructions' that are really just specific portions of the hardware or opcodes! So we either descend endlessly into pedantry arguing that cosmic rays and electron tunnelling represent inexcusable deviations from the overly abstracted semantics that hardware vendors expose in their products or maybe we draw the line somewhere else. You may not agree with mine, that "practical and simple interop with machine-level language impls across a high-level language interface is sufficiently close to the hardware as to be low level" but there has to be a limit somewhere between that and "technically the hardware's operating temperature is part of its logical semantics because if it exceeds a certain value for long enough it starts to degrade and yield incorrect results or terminate execution". I think eventually it just becomes unproductive nerd sniping, personally |
| |
| ▲ | torginus an hour ago | parent | prev | next [-] | | I think a reasonable definition of 'lower-level' is getting the programmer to take over some tasks from the compiler. Mechanically expanding a block of code into intrinsics isn't really that. What makes 'C' not really low level by a reasonable definition, is that the register allocation decisions are not yet made. Which, depending on how it works out, can effect ordering, inlining, unrolling etc, so the compiler can pretty much go to town on your code and create something unrecognizable. Since registers aren't really allocated here, this is basically on the level of C code, and all that stuff can happen here, so this really isn't much lower level than C. Not being elitist, it's just worth knowing what's going on under the hood of compilers, and the nature of the contract they uphold. | |
| ▲ | senfiaj 43 minutes ago | parent | prev | next [-] | | Yeah, and also not every idiomatic C/C++ code is portable. Hardcoded structure sizes, assumptions about the byte order in integers or assumptions about alignments in certain data structures might cause headaches with porting the code to another CPU. Truly high level languages hide these details. | |
| ▲ | stackghost an hour ago | parent | prev | next [-] | | Isn't the point that x86 instructions are themselves no longer a good mental model for what the processor is actually doing under the hood, and thus C which was long billed as a thin layer over top of assembly is itself a higher abstraction? There is AFAIK no way to express or interact with speculative execution/branch prediction, for example. | | |
| ▲ | weitendorf an hour ago | parent [-] | | Sure, but then you're really arguing that the ISA no longer maintains 1:1 instruction-level implementation and that this is the definitive quality of whether or not something is low level, to the point that any deviation from that model makes it not officially "low level". To me that's just a very tedious pedantic argument that simply fails to capture the actual meaning behind why/when we might call something low level. TFA famously argues that Spectre/Meltdown et al break that abstraction. But note that they are quite literally exceptions to the rule: the only reason we know/care about them is that the "magic under the hood" that was supposed to make CPUs faster while maintaining that abstraction introduced a bug that caused the implementation details to leak to the end users. Similarly even vp2intersectd took multiple cycles in its original Intel impl and even in the performant AMD Zen5 impl it still takes >1 cycle with 6 levels of pipelining or somesuch. Ok. If literally not even a chip's ISA is "low level" then the term is effectively meaningless. The only way you could define a "low level" language capable of exercising that hardware's capabilities fully would be to have some kind of per-cycle, pipeline-aware annotation layer over the actual machine code... which really seems like quite a lot of noise/cruft you'd not typically want to add on top of everything, all in the name of still technically being low-level according to some dubiously pedantic criteria nobody would event want in practice. |
| |
| ▲ | dismalaf 39 minutes ago | parent | prev [-] | | Common Lisp allows you to define VOPs (compiler instructions) in user code. Smalltalk allows you to write inline assembly. Are those also low level languages? |
|
|
| ▲ | bee_rider 2 hours ago | parent | prev | next [-] |
| “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. |
|
| ▲ | melodyogonna 29 minutes ago | parent | prev | next [-] |
| 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. |
|
| ▲ | legobmw99 3 hours ago | parent | prev | next [-] |
| 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? |
| |
| ▲ | melodyogonna 11 minutes ago | parent | next [-] | | The thing is, you can only program what is programmable. If a CPU has some capability that isn't programmable, I don't know what C or any other language is expected to do. | |
| ▲ | aDyslecticCrow 3 hours ago | parent | prev | next [-] | | The article does make an example quite early; > GPUs achieve very high performance without any of this logic, at the expense of requiring explicitly parallel programs. GPU cores are in some ways closer to "PDP-11", they're either acting as thousands of parallel simple processors, or expose pretty raw instructions for very parallel use-cases. | | |
| ▲ | legobmw99 2 hours ago | parent [-] | | That seems fair, CUDA kernels and shader code do feel like they're at a similar level of abstraction over the hardware as C was to the PDP-11. But I do think there isn't really an equivalent for modern CPU ISAs | | |
| ▲ | aDyslecticCrow 2 hours ago | parent [-] | | Mabie hand-rolling LLVM IR representations would count. | | |
| ▲ | jpollock 14 minutes ago | parent [-] | | Doesn't assembly allow devs to ignore speculative execution as well? You can place LFENCE(x86)/CSDB(arm) around code blocks, but you can do that in C too. |
|
|
| |
| ▲ | giancarlostoro 3 hours ago | parent | prev | next [-] | | Probably Mojo, it doesnt just talk to your CPU it also will talk to your GPU bypassing the need for CUDA. Its early days, but I see strong potential in Mojo. Currently its primary focus is GPUs for AI inference, but give it a year or two and it will be really interesting for more than just that. | | |
| ▲ | kllrnohj 41 minutes ago | parent | next [-] | | In the case of this article, even assembly isn't low level. It's not possible at all to write low level code for a modern superscalar CPU. So no, Mojo wouldn't be low level. It can't be. | | | |
| ▲ | huijzer 2 hours ago | parent | prev | next [-] | | Mojo to me seems like a high level language with some additional support for low level control especially around GPUs. A bit like Rust or C but with more streamlined Python integration and more low level GPU (matrices) support. | |
| ▲ | poly2it 2 hours ago | parent | prev [-] | | But Mojo is a high level language? |
| |
| ▲ | ferguess_k 3 hours ago | parent | prev | next [-] | | Wondering can we write microcode? That's definitely closer to the metal. | | |
| ▲ | wat10000 an hour ago | parent [-] | | That was kind of the original idea of RISC. Expose simple instructions that could be implemented without microcode. Push the complexity into the program instead of the microcode. Instead of writing a memory-to-memory add instruction that decomposes into load, load, add, store microcode, you directly write the load, load, add, store. This didn’t quite work out in the long term since hardware evolves faster than ISAs. Today’s “maps directly to the hardware” instruction is tomorrow’s “we add more hardware and play tricks to make this faster.” You explode all of the physical registers as logical registers, then a few years later you double the physical registers count and do clever mapping to extract more speed. My favorite is the MIPS branch delay slot. Instead of complicated branch prediction to hide latency, expose the pipeline directly to the programmer. And then a couple of hardware generations down the line, the pipeline becomes much longer and more complicated and the CPU is back to playing tricks to hide latency, and the weird branch delay slot remains as essentially a vestige of bygone days. |
| |
| ▲ | jjtheblunt 2 hours ago | parent | prev | next [-] | | > really aren’t any true low level languages for our super scalar modern CPUs do you mean low level but higher level than assembly language for those processors (like MIPS assembly for an R10k, for example) ? | |
| ▲ | MrBuddyCasino 3 hours ago | parent | prev [-] | | In what way would exposing the true microcoded out-of-order etc nature of the beast benefit certain tasks? | | |
| ▲ | legobmw99 2 hours ago | parent | next [-] | | Better control over the async nature of the hardware is part of what makes GPU kernels efficient, but I'm not terribly sure the same thing would be the case on the other side of the PCIe bus. But even before you get to out-of-order/speculative execution, I think most languages lack good (i.e. non-intrinsic-based) support for wide registers or anything SIMD related. I know C++ and Rust are both working on this | |
| ▲ | 12_throw_away 2 hours ago | parent | prev [-] | | It's a good and interesting question, why is it important whether or not it will "benefit certain tasks"? And how would we even know if we haven't tried it? | | |
|
|
|
| ▲ | glouwbug 3 hours ago | parent | prev | next [-] |
| 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) |
| |
| ▲ | leptons 2 hours ago | parent | next [-] | | >C is going the way of classic WoW What does this mean? | | |
| ▲ | omani 2 hours ago | parent [-] | | it means C is going the way of classic World of Warcraft. | | |
| ▲ | mid-kid 2 hours ago | parent [-] | | What does that mean? | | |
| ▲ | glouwbug 2 hours ago | parent [-] | | WoW released a steady stream of expansions from 2004 to present day. At some point players wanted the old game, so Blizzard released WoW 2004, known as classic, in 2018, and reran the stream of expansions, stopping before any mass enshittification. You can draw the same parallels with C and C++, where C began picking the best from C++ in 1999, 2011, 2023, and soon to be 2029, and carving its own "classic" path. We even have our Herb Sutter: Jens Gustedt | | |
| ▲ | kllrnohj 30 minutes ago | parent [-] | | Except people aren't leaving C++ for C, if they're leaving it at all they're likely leaving it for Rust or some other memory safe language. | | |
| ▲ | uecker 23 minutes ago | parent [-] | | Well, I was leaving C++ for C. I found this unburdened my mind from quite a lot of distracting complexity. For this reason, I am also not really tempted by Rust. |
|
|
|
|
| |
| ▲ | warmwaffles 2 hours ago | parent | prev [-] | | I remember `defer` being up for consideration for the last consortium but it got yanked. Lambdas would definitely be nice to have. | | |
| ▲ | glouwbug 2 hours ago | parent [-] | | Seems like its in C29, but who knows. I've waited since 2009 for just about anything | | |
| ▲ | warmwaffles 2 hours ago | parent [-] | | C29 is shaping up to be some quality of life changes. And it looks like clang has a lot of it implemented already. GCC seems to be implementing some of them. `countof(thing)` seems handy so I don't have to define some constant and use them in both places. | | |
| ▲ | glouwbug 2 hours ago | parent | next [-] | | Not having to write #define len(x) sizeof(x) / sizeof(*x)
Is arguably the feature I've waited for for 20 years | |
| ▲ | uecker an hour ago | parent | prev [-] | | GCC is usually ahead in terms of new C features. |
|
|
|
|
|
| ▲ | WillPostForFood an hour ago | parent | prev | next [-] |
| 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 an hour ago | parent | prev | next [-] |
| 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. :) |
|
| ▲ | veqq 3 hours ago | parent | prev | next [-] |
| 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. |
| |
|
| ▲ | jrhey 2 hours ago | parent | prev | next [-] |
| 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 |
| |
| ▲ | pornel 27 minutes ago | parent [-] | | Modern CPUs hide so much logic that the assembly language is an abstraction itself. CPUs have many times more registers than their assembly language, execute instructions speculatively and out of order. Multiple layers of caches are synchronized in complex ways. There's an invisible complex work scheduling algorithm that can even make one CPU core interleave work of two (hyperthreading). GPUs can expose more of their internals thanks to shader compilation. They don't have to emulate previous-gen chip, and instead every chip can expose exactly what it supports and rely on software being recompiled for it. |
|
|
| ▲ | serbuvlad 2 hours ago | parent | prev | next [-] |
| 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). |
| |
| ▲ | pornel 20 minutes ago | parent [-] | | > I fail to see how any sort of much lower level access to the machine would be portable across price ranges and microarchitecture generations That's the assumption that can be removed. GPUs don't have stable ISAs, and their assembly-like code gets recompiled for each microarchitecture. In the Intel's world of prebaked machine code adoption of a wider set of SIMD instructions takes a decade+. In GPUs it's just a driver update. | | |
| ▲ | serbuvlad 3 minutes ago | parent [-] | | Sure, but SIMD extensions and base ISA solve different problems. As for GPUs, while the ISA is not constant, it's STILL C-ish running over a dynamic hardware scheduling layer. Edit: To clarify, I have nothing against a closed ISA, I just don't see how making that ISA non-C-ish is valuable. |
|
|
|
| ▲ | Peteragain 2 hours ago | parent | prev | next [-] |
| 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? |
| |
| ▲ | stephen_cagle 2 hours ago | parent | next [-] | | I've never done verilog professionally but I did "Digital Design and Computer Architecture, RISC-V Edition: RISC-V Edition" as an exercise 2 years ago. I would say Verilog is very much NOT a low level language. Metaphorically, it feels closer to SQL to me. I mean this in that you theoretically tell the system what it should do, and it builds it into the messy real world. However, the reality is that the planner (sql) or linker/placer/router/whatever (verilog) are very good, but you often end up needing to actually fully understand the problem anyway when things don't work in the abstract. I know there is https://clash-lang.org/ for Verilog design, which sounds a little like what you are talking about (never really looked at it myself). | |
| ▲ | mathisfun123 2 hours ago | parent | prev [-] | | > what would a low level language look like for an FPGA? Probably verilog Verilog is not a programming language (because FPGAs are not programmed) it's a hardware description language. It's also very lossy (every vendor has reams of coding guides for using it just right with their synthetizer). |
|
|
| ▲ | p0w3n3d an hour ago | parent | prev | next [-] |
| C is not a low level language. It's a macro assembler |
|
| ▲ | blastonico 3 hours ago | parent | prev | next [-] |
| 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. |
| |
|
| ▲ | actionfromafar 3 hours ago | parent | prev | next [-] |
| If anyone was thinking, but in practice it is a low level language, behold Fil-C. |
|
| ▲ | EGreg 3 hours ago | parent | prev | next [-] |
| 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? |
| |
| ▲ | rfgplk 2 hours ago | parent [-] | | Unique language! To me the definition of low-level vs high-level strictly comes from the indirection the language runtime provides for you. If the language compiles down to asm, it's low level. It literally does _not_ matter what it looks like. The only other constraint is possibly whether you can manipulate low-level CPU level constructors like memory, albeit it's not necessary. You can take python and write an LLVM frontend for it and it would instantly become a low-level language. | | |
| ▲ | bee_rider 2 hours ago | parent [-] | | I think that would be confusing. Intuitively “low level language” describes the language. Your definition actually describes the nature of the compiler implementation. |
|
|
|
| ▲ | applfanboysbgon 3 hours ago | parent | prev | next [-] |
| 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. |
| |
| ▲ | mustache_kimono 2 hours ago | parent | next [-] | | > 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. The article mentions assembly once. But it's not an argument about how assembly is "low level" and C isn't, although it may sound like that, upon a first reading, given the article's contentious tone. The article is really an argument about how C programmers believe, and constantly state, that they are programming "close to the metal", but what they are really programming is a very fast PDP-11 emulator with lots of implicit behavior. Implicit behavior like speculative execution and asynchronous execution and lots and lots of caching. | | |
| ▲ | applfanboysbgon 2 hours ago | parent [-] | | > The article mentions assembly once. But it's not an argument about how assembly is "low level" It is, though: > Think of programming languages as belonging on a continuum, with assembly at one end The article explicitly states that assembly is the end of the continuum, that it is the lowest of the low-level. Therefore, it is not making the argument that assembly is not low-level. But the exact same arguments it makes to distinguish C as not low-level can be applied to assembly. The entire article is based on a fundamental logical error. | | |
| ▲ | mustache_kimono 2 hours ago | parent [-] | | > The article explicitly states Again -- I think your impression is the result of the contentious tone of the article. Yes, the article explicitly states: "Think of programming languages as belonging on a continuum, with assembly at one end and the interface to the Starship Enterprise’s computer at the other. Low-level languages are “close to the metal,” whereas high-level languages are closer to how humans think."
But then spends the rest of the article debunking this commonly held notion, specifically and explicitly re: C, but also implicitly re: assembly.See the very next section "FAST PDP-11 EMULATORS" "The root cause of the Spectre and Meltdown vulnerabilities was that processor architects were trying to build not just fast processors, but fast processors that expose the same abstract machine as a PDP-11. This is essential because it allows C programmers to continue in the belief that their language is close to the underlying hardware."
The author obviously knows that assembly suffers from the same abstraction penalty. The author is saying, because C and processor design has been so tightly intertwined, we cannot program "close to the metal" because "the machine" is actually a very fast PDP-11 emulator.See also the section "IMAGINING A NON-C PROCESSOR", where the author explicitly discusses alternative processor designs (which would of course require new assembly languages!). The author is actually trying something like a reductio on your mental model. When the author states "Think of programming languages as belonging on a continuum", the author is really saying "This is everyone's impression, but ... when you look a little deeper you see the cracks (which are actually contradictions)." | | |
| ▲ | applfanboysbgon 2 hours ago | parent [-] | | At my most charitable, I would give the author credit for knowing that assembly is not low-level by his own definition, but nonetheless intentionally misleads readers who do not know that assembly does not expose speculative execution instructions any more than C does. If the author knew this, and his central premise were that no low-level programming language existed anymore, the article would be titled differently and he wouldn't be making the arguments against C specifically. Really, that would be an entirely different article if written honestly. But this is essentially packaged as clickbait that takes advantage of ignorant readers to make the author sound smarter and more insightful than he really is, and still contains logical errors perpetuated as misinformation to the readers who don't know better. After writing out my charitable interpretation of the author's capabilities, this interpretation only leaves me more disgusted with the article as a writing output. | | |
| ▲ | mustache_kimono an hour ago | parent [-] | | > nonetheless intentionally misleads readers I'm really not certain that's the idea, and it certainly does not feel very charitable. Perhaps you are holding on a little too tightly to this high vs. low level distinction (the simple mental model being attacked)? I compared the author's argument to a reductio. A reductio is not intentionally misleading? This paper reminds me of "It's Time for Operating Systems to Rediscover Hardware". See: https://www.youtube.com/watch?v=36myc8wQhLo There, an argument is made that our simple model of "the machine" is also wrong. There, the speaker points out much of the software that is running on our complex SoCs, with multiple cores, is firmware. To which, I'd imagine you might argue: "But that firmware is not the OS?! This talk is misleading!" > If the author knew this, and his central premise were that no low-level programming language existed anymore, the article would be titled differently and he wouldn't be making the arguments against C specifically. I am not sure. I believe the reason C is targeted specifically is because C communities are where this myth, and its religiosity (!), is the strongest. > But this is essentially packaged as clickbait I'd agree that the article is provocative, but it would seem to have good reason to be. Lots and lots of people think both C and our processors must work one way. That there is or was some level of naturalism/determinism at play. The author is simply pointing out -- not so much. > After writing out my charitable interpretation of the author's capabilities, this interpretation only leaves me more disgusted with the article as a writing output. Yes, it was designed to make you mad. But if you were forced to write a rebuttal to the entire article, from a charitable POV, I think you'd see there is some value to the reader in realizing this tight coupling (C and processor design) is not a necessary condition. | | |
| ▲ | uecker an hour ago | parent | next [-] | | The idea that processor evolution is blocked by the need to run C code is wrong. We had many alternative designs, both for languages and also for processors. They were simply not successful. I still remember the pain of segmented memory in 286. Also much of the criticism in the article would apply to von Neumann / Harvard architectures in general, and is not specific to PDP-11 and C. Then also, where new programming methodologies such as CUDA are invented to allow new processor designs, it turns out that they can be quite successful despite moving away from C. But then it also turns out that this programming model was actually not that great to program in, and people try hard to move back again by making the hardware more capable. | |
| ▲ | applfanboysbgon an hour ago | parent | prev [-] | | > I believe the reason C is targeted specifically is because C communities are where this myth, and its religiosity (!), is the strongest. C being a low-level language is not a myth if, as normal people do, you consider assembly languages to be low-level languages. The article itself offers assembly as the low-level language and makes no effort to redress this later. If you want to argue that there are no low-level languages when it comes to programming modern CPUs, you are free to do so, but that is a different argument. And it is arguably not a fruitful one because then the terminology loses all meaning. Okay, you've defined assembly as a high-level language. Now what have you accomplished other than making it harder for people to articulate and categorise classes of languages? We'll still need an adjective for distinguishing between such wildly distinct languages assembly and Python, so we have to come up with something else to replace "low-level" and you haven't accomplished much of anything at all. If you think low-level programming has gotten too far from the bare metal, or if you think processors are designed for C and that's a problem, just argue that directly instead of this really torturous detour singling out C and how people use terminology to communicate useful concepts. | | |
| ▲ | mustache_kimono 31 minutes ago | parent [-] | | > C being a low-level language is not a myth if, as normal people do, you consider assembly languages to be low-level languages. I'd suggest you're holding that stick too tight! > If you want to argue that there are no low-level languages when it comes to programming modern CPUs, you are free to do so, but that is a different argument. Not if you read the whole article? > And it is arguably not a fruitful one because then the terminology loses all meaning. Okay, you've defined assembly as a high-level language. Again, what if the terminology isn't very important? I'd argue the distinction between high and low level languages is not an important distinction, because it is so crude. For example, C/C++/Rust have all been described as high level languages at one time or another. C people seem to be the only ones that take real offense to this. |
|
|
|
|
|
| |
| ▲ | rfgplk 2 hours ago | parent | prev | next [-] | | > C doesn't expose modern CPU features like speculative execution, therefore C is not low-level This is a 100% skill issue of the author, as is always the case. C does expose it fully, except it's implicitly implied by your code rather than explicitly declared. Same with all of the other arguments that always plague these type of articles. | | |
| ▲ | jact 2 hours ago | parent [-] | | Can you elaborate? It’s “implicitly implied” by your code? How is that “exposing it fully” except in the sense that speculative execution is “implicitly implied” in all code targeting the relevant hardware? | | |
| ▲ | Joel_Mckay an hour ago | parent [-] | | Some C compilers include special interpretations of static and volatile to change how both the code is compiled and linked. It can be "low-level", but it depends how you define what that means in the age of microcode abstractions. Most people shouldn't write in C, but that is because their skills belong in Application user space. =3 |
|
| |
| ▲ | aDyslecticCrow 2 hours ago | parent | prev | next [-] | | > You could attempt to make the claim that assembly is no longer a low-level language Assembly expose instructions that C was never meant to work with. Compilers force C to do so anyway. If you had a compiler that converted 8086 x86 assembly to modern x86 or CUDA bytecode; I'd consider that pretty equivalent. LLMV intermediate representation is probably more low level (closer to the real compute model it runs on) than that theoretical 8086 x86 compiler. | |
| ▲ | nizmow 3 hours ago | parent | prev [-] | | I think that’s the point. |
|
|
| ▲ | fsckboy 3 hours ago | parent | prev [-] |
| >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. |