> Most real C implementations will go some distance beyond the standard(s), of course, but I have to draw the line somewhere.
You missed the guy's point, which was essentially that C code is munged and transformed by a good optimizing compiler, whereas assembly is left untouched by a good assembler.
No I don't think I did. I helped to write an optimized operating system kernel and virtual machine monitor for high performance computing. We absolutely treated C as high-level assembly. C provided a cost-saving measure in that we didn't have to run off customized asm blocks to do trivial one-liners that would be the same in the VMM implementation but slightly different in object code (two different opcodes). Not only is this common practice in academic and production systems development, but it also the recommended practice from most of my colleagues that added the intrinsics for their subsystems (Intel and Microsoft being my personal experiences).
You missed the guy's point, which was essentially that C code is munged and transformed by a good optimizing compiler, whereas assembly is left untouched by a good assembler.
That's irrelevant. They are both restricted on the basis of being functionally equivalent to the input code.
The fact that C can be relatively easily and predictably converted into reasonably efficient assembly doesn't make it an assembler.
All compiled languages are "restricted on the basis of being functionally equivalent to the input code", unless you meant something by that statement that I don't understand. The reason I'm uncertain is because I wouldn't presume that you are asserting that all deterministically compiled, correct compilers are high-level assembly languages.
I would add to what he writes in the article, however (and I did in a comment there): C doesn't model the von Neumann architecture very well. It lets you create data structures just fine, but the capability to create code, dynamically, at runtime, surely the defining characteristic of a shared instruction-data architecture, is all but completely absent.
Perhaps I've been unclear. C has characteristics of high-level assembly language because it allows you to directly integrate direct machine opcodes transformations directly into the syntax of the language itself instead of into the compiler/interpreter. In other words, C has an element of predictability in the opcodes it will generate for a given architecture. The exact opcodes it generates (SIMD, etc) aren't extremely important as long as this capability is preserved.
The fact that C can be relatively easily and predictably converted into reasonably efficient assembly doesn't make it an assembler.
You make some good points and there is one which I thought about but didn't touch on because I didn't think it was important.
Consider: most systems and kernel developers do not write C. That is, they do not simply target "the C programming language." Instead, they often target GCC or the Intel C compiler. Even more, they usually target an architecture. In theory one may have to deal with many of the complexities of different implementations, but in practice development is usually restricted (or sometimes duplicated). I think that you may be taking my position a bit too concretely: I don't support the statement that all opcode output is predictable, nor do I support the statement that no other language has elements of the same techniques which make C so useful for systems development.
Simply, view my post as a (possibly slightly exaggerated) relation of the systems C development process from one low-level kernel developer to another. From the birds' eye view (which is the feeling I get from the original post) C may look abstracted and neat, but my experience says that the opposite is in fact true and that things tend to get a lot more dirty in the details.
I don't think C is necessarily predictable in the opcodes it will produce for any given architecture. The code produced for a big switch statement will differ hugely between compilers for the same architecture; some use binary searches, some use hash tables, some use multiple indirect jumps, etc.
And conversely, there are many other languages that have a similar degree of predictability in the code they produce for a given architecture; Pascal, for one, which is almost isomorphic with C in most practical implementations.
Yes, I think the difference is that C allows for close interaction to the machine architecture. Normally I don't care what exact opcodes the compiler turns X language into, however C allows me to wrap direct hardware access into the syntax of the language itself. In essence, it mostly doesn't matter what exact opcodes the preceding for loop is turned into, it matters that in the next block I can take that result, move it to rax and rcx and use an intrinsic to execute a CPUID instruction.
> Most real C implementations will go some distance beyond the standard(s), of course, but I have to draw the line somewhere.
You missed the guy's point, which was essentially that C code is munged and transformed by a good optimizing compiler, whereas assembly is left untouched by a good assembler.