Hacker News new | past | comments | ask | show | jobs | submit login

The author is not a moron but he is also not correct. Whether or not he thinks this is the best practice, ANSI C is not what is written in most of the important C systems applications. GCC intrinsics, Intel intrinsics and extensions, Nvidia extensions, inline assembler -- these are all things which you will find in most (if not all) of the very performant and useful C systems programs. Non-ANSI C is almost the de facto implementation in systems development and is mostly what people think of when they say "C is high level assembly." Moreover, people have to realize that this will never change unless intrinsics fall out of favor. For example, how would you begin to define a VMENTER intrinsic in the ANSI C standard? The author spends a lot of time railing against undefined behavior, but there is a very big difference between undefined behavior and implementation defined behavior. Simply because someone whips out GCC and says "see? GCC does it correctly" doesn't mean they're wrong unless they're only talking about undefined behavior.

So I guess my perspective is that there are two different types of people using C: people who are writing systems applications and people using ANSI C. Personally, I would guess that the first group is much, much larger.




Sigh...pedantry at its finest. From TFA:

> 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.

Never said it was.


@barrkel

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.


> They are both restricted on the basis of being functionally equivalent to the input code.

But that goes for any compiled language, unless the compiler contains a bug.


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.


Worse, you can leave out 'compiled'. According to that logic, captain Picard programs in assembly whenever he starts a sentence with "Computer, "




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: