Hacker News new | ask | show | jobs
by Too 1640 days ago
Being under-specified towards the hardware is a feature, to allow optimization.

Now I’m not arguing all the UB in C is great, in fact the opposite. But that should be prohibited on the language layer and not on the machine layer. See rust for a better solution.

1 comments

100% specification would be a mis-feature. But you can't easily use the C language to tell the compiler that your ternary assignment has a 50/50 probability for each branch, meaning it probably should issue an instruction like cmov on x86. You can use non-standard builtins to say it's 99/1 so it should just branch predict, and those builtins are so ubiquitous it's not such a big deal, but still not technically possible with just the standard C language.

In a PDP-11 world, there was no need for such things. In the superscalar predictive world of today, there are a lot of such things you need to specify with non-standard builtins and inline assembly. C gets the job done, but basically ever since the Pentium, it has relied more and more on those non-standard escape hatches, in order to be the best portable assembler. It's still the best compared to other languages, but in absolute terms, it is less good at that task with every hardware generation. Rust doesn't improve on this by the way.

That's the obvious stuff. Compiler writers using UB footguns as one of the most powerful optimization tools is another problem on top of that. You might have to use signed indices into some bytes in order to let the compiler assume your index increment has no overflow (that would be UB), again just to be able to issue cmovs. That's an awkwardly indirect way to do that. Arguably C would be better off specifying addition operators or inline functions for unsigned integers that the programmer promises will never overflow. I don't use Rust, but my understanding is all integer types panic on overflow in debug, and wrap in release -- at least there is a non-stable unchecked_add.

Now I see what you mean by under-specified in the case of branch hints. But does the solution need to involve giving more direct machine access? Similar benefits can be given with higher layer abstractions as well, without adding more UB, in fact the examples below remove UB. Take restrict keyword in C, which theoretically could be automatically inferred in Rust (not sure if they actually do nowadays). Iterators, or as you say better addition operators, can hide the details of array indexing and overflows. Hinting the probability of a certain branch certainly sounds like a higher layer construct as well, not something to expose a direct machine instruction for.
I’m not proposing machine specific additions to C. One of my complaints is that the C language doesn’t have enough features, so I end up using machine specific assembly or compiler specific builtins. And I’m complaining about C pushing developers onto the knife edge of UB or performant code — I don’t want more UB either.

I want higher level constructs in the C language (restrict isn’t a great feature, but it’s high level, so like that) that can map to the actual feature set found on actual machines since 2004 (and compiler writers can do that mapping per machine). But C is stuck with a simplified model of the machine that ignores what almost all hardware can do these days.

I think I’ll always have to dip into assembly/intrinsics sometimes, so I’m not looking for super advanced/rich features. Actually, I think the real benefit would be giving compiler writers ways to improve performance without pushing devs onto the UB knife edge.