Hacker News new | ask | show | jobs
by j16sdiz 1033 days ago
That's what the author meant when he said "The shift of the C language from “portable assembly” to “high-level programming language without the safety of high-level programming languages”"

Back in the 1980s, C was expected to do what hardware does. There was no "the C abstract machine".

The abstract machine idea was introduced much later.

> The arguments in this blogpost are fundamentally flawed.

The "fundamentally flawed" comment is revisionist idea.

4 comments

This turns out to be contentious. There are two histories of the C language and which one you get told is true depends on who you ask.

1/ a way to emit specific assembly with a compiler dealing with register allocation and instruction selection

2/ an abstract machine specification that permits optimisations and also happens to lower well defined code to some architectures

My working theory is that the language standardisation effort invented the latter. So when people say C was always like this, they mean since ansi c89, and there was no language before that. And when people say C used to be typed/convenient assembly language, they're referring to the language that was called C that existed in reality prior to that standards document.

The WG14 mailing list was insistent (in correspondence to me) that C was always like this, some of whom were presumably around at the time. A partial counterargument is the semi-infamous message from Dennis Richie copied in various places, e.g. https://www.lysator.liu.se/c/dmr-on-noalias.html

An out of context quote from that email to encourage people to read said context and ideally reply here with more information on this historical assessment

"The fundamental problem is that it is not possible to write real programs using the X3J11 definition of C. The committee has created an unreal language that no one can or will actually use."

Regards

> My working theory is that the language standardisation effort invented the latter. So when people say C was always like this, they mean since ansi c89, and there was no language before that. And when people say C used to be typed/convenient assembly language, they're referring to the language that was called C that existed in reality prior to that standards document.

But the committee has always had a lot of C compiler developers in it. The people who wrote the C89 standard were the same people who developed many of the C compilers in use before C89. The people who created the reality prior to C89 created the reality after C89. Any perception of "portable assembly" probably stemmed simply from the fact that optimizers were much less sophisticated.

> Back in the 1980s, C was expected to do what hardware does. There was no "the C abstract machine".

There was also a huge variety of compilers that were buggy and incomplete each in their own ways, often with mutually-incompatible extensions, not to mention prone to generating pretty awful code.

Indeed, those two statements are the same thing.

If you want a correct compiler it has to be correct according to a model, which means it can't handle things outside that model, and now you have "undefined behavior".

People want compilers to limit how much they transform UB, but that's not possible unless it gets defined. Which you can do, of course, but it's more limiting than it looks.

How does C do what hardware does and store things in registers when it can?
It doesn't, it is up to the compiler and optimizer to decide how to go at it.

Vector instructions, replacing library functions with compiler intrisics, splitting structs across registers and stack, unrolling loops are all examples absent from the language standard.

Two ways. One is the platform ABI sometimes says specific arguments are passed in specific registers. The second is (essentially) assigning local variables offsets on a machine stack where some offsets are stored in registers.
To the best of my recollection the “abstract machine” is a C++ism that unfortunately crept into C.
From C89 document:

> 2.1.2.3 Program execution

> The semantic descriptions in this Standard describe the behavior of an abstract machine in which issues of optimization are irrelevant

[...]

> Alternatively, an implementation might perform various optimizations within each translation unit, such that the actual semantics would agree with the abstract semantics only when making function calls across translation unit boundaries. In such an implementation, at the time of each function entry and function return where the calling function and the called function are in different translation units, the values of all externally linked objects and of all objects accessible via pointers therein would agree with the abstract semantics. Furthermore, at the time of each such function entry the values of the parameters of the called function and of all objects accessible via pointers therein would agree with the abstract semantics.

The "abstract machine" is present in the first C standard, published in 1989.