Live data from Hacker News

Clang vs. Clang

blog.cr.yp.to

101–110 of 405 posts

Re: Clang vs. Clang

#101

Earlier quoted context omitted.

Even stipulating that part of the argument, the author then goes on a tear about optimizations breaking constant-time evaluation, which doesn’t have anything to do with UB. The real argument seems to be that C compilers had it right when they really did embody C as portable assembly, and everything that’s made that mapping less predictable has been a regression.

But C never had been portable assembly. Which I think is somewhat the core of the problem. People treating things in C in ways they just are not. Weather that is C is portable assembly or C the "it's just bit's in memory" view of things (which often is double wrong ignoring stuff like hardware caching). Or stuff like writing const time code based on assuming that the compiler probably, hopefully can't figure out that…

> But C never had been portable assembly.

The ANSI C standards committee disagrees with you.

"Committee did not want to force programmers into writing portably, to preclude the use of C as a “high-level assembler:”

https://www.open-std.org/JTC1/SC22/WG14/www/docs/n897.pdf

p 2, line 39. (p10 of the PDF)

"C code can be portable. "

line 30

Re: Clang vs. Clang

#102

Earlier quoted context omitted.

It is, in fact, pretty hard as evidenced by how often programmers fail at it. The macho attitude of "it's not hard, just write good code" is divorced from observable reality.

Staying under the speed limit is, in fact, pretty hard as evidenced by how often drivers fail at it.

It's more complex than that for the example of car speed limits. Depending on where you live, the law also says that driving too slow is illegal because it creates an unsafe environment by forcing other drivers on i.e. the freeway to pass you.

But yeah, seeing how virtually everyone on every road is constantly speeding, that doesn't give me a lot of faith in my fellow programmers' ability to avoid UB...

Re: Clang vs. Clang

#103
post #72
post #12

Earlier quoted context omitted.

"Optimizing compilers that don't allow disabling __all__ optimizations"

It’s not well-defined what counts as an optimization. For example, should every single source-level read access of a memory location go through all cache levels down to main memory, instead of, for example, caching values in registers? That would be awfully slow. But that question is one reason for UB.

Or writing code that relies on inlining and/or tail call optimization to successfully run at all without running out of stack... We've got some code that doesn't run if compiled O0 due to that.

Re: Clang vs. Clang

#104
post #69
post #54

Earlier quoted context omitted.

The problem is that c and c++ have a ridiculous amount of undefined behavior, and it is extremely difficult to avoid all of it. One of the advantages of rust is it confines any potential UB to unsafe blocks. But even in rust, which has defined behavior in a lot of places that are UB in c, if you venture into unsafe code, it is remarkable easy to accidentally run into subtle UB issues.

It’s true that UB is not intuitive at first, but “ridiculous amount” and “difficult to avoid” is overstating it. You have to have a proof-writing mindset when coding, but you do get sensitized to the pitfalls once you read up on what the language constructs actually guarantee (and don’t guarantee), and it’s not that much more difficult than, say, avoiding panics in Rust.

So surely you know by hear the circa 200 use cases documented in ISO C, and the even greater list documented in ISO C++ standard documents.

Because, me despite knowing both since the 1990's, I rather leave that to static analysis tools.

Re: Clang vs. Clang

#105

Earlier quoted context omitted.

That was my thought reading this article. If you want to produce machine code that performs operations in constant time regardless of the branch taken, you need to use a language that supports expressing that, which C does not.

Heck, CPUs themselves aren't suitable for constant time operations. At any time, some new CPU can be released which changes how quick some operations are.

Or microcode updates to existing CPUs!

Re: Clang vs. Clang

#106
post #47

Earlier quoted context omitted.

"Unfortunately GCC’s codegen for GCC’s x86 intrinsics headers is really remarkably awful at -O0" - but that kind of seems to be what is asked for..

No. If I say (e.g.) _mm256_set_epi32(a,b,...,c) with constant arguments (which is the preferred way to make a vector constant), I expect to see 32 aligned bytes in the constant pool and a VMOVDQA in the code, not the mess of VPINSRDs that I’ll get at -O0 and that makes it essentially impossible to write decent vectorized code. The same way that I don’t expect to see a MUL in the assembly when I write sizeof(int) * CH…

Seems you want the compiler to do some optimization, to improve the generated code. Or?

Re: Clang vs. Clang

#107
post #17
post #3

Earlier quoted context omitted.

Optimizing compilers that don't allow disabling all optimizations makes it impossible to write secure code with them. Must do it with assembly.

If you have ub then you have a bug and there is some system that will show it. It isn't hard to write code without ub.

Only if developers act as grown ups and use all static analysers they can get hold of, instead of acting as they know better.

The tone of my answer is a reflection of what most surveys state, related to the actual use of such tooling.

Re: Clang vs. Clang

#108

Earlier quoted context omitted.

But C never had been portable assembly. Which I think is somewhat the core of the problem. People treating things in C in ways they just are not. Weather that is C is portable assembly or C the "it's just bit's in memory" view of things (which often is double wrong ignoring stuff like hardware caching). Or stuff like writing const time code based on assuming that the compiler probably, hopefully can't figure out that…

> But C never had been portable assembly. The ANSI C standards committee disagrees with you. "Committee did not want to force programmers into writing portably, to preclude the use of C as a “high-level assembler:” https://www.open-std.org/JTC1/SC22/WG14/www/docs/n897.pdf p 2, line 39. (p10 of the PDF) "C code can be portable. " line 30

Back in 1989, when C abstract machine semantics were closer to being a portable macro processor, and stuff like the register keyword was actually something compilers cared about.

Re: Clang vs. Clang

#109
post #93

Very interesting article and much-needed criticism of the current standard of heuristic optimization. Before reading this, I thought that a simple compiler could never usefully compete against optimizing compilers (which require more manpower to produce), but perhaps there is a niche use-case for a compiler with better facilities for manual optimization. This article has inspired me to make a simple compiler myself.

You don't need to get rid of all optimizations though, just the "unsafe" ones. And you could always make them opt-in instead of opt-out.

Now I'm definitely closer to a noob, but compilers already have flags like no-strict-overflow and no-delete-null-pointer-checks. I don't see why we can't make these the default options. It's already "undefined behavior" per the spec, so why not make it do something sensible. The only danger is that some pedant comes along and says that with these assumptions what you're now writing isn't "portable C" and relies on compiler-defined behavior, but in the real world if it does the correct thing I don't think anyone would care: just call your dialect "boringC" instead of C99 or something (borrowing Gavin Howard's term), and the issue disappears.

Re: Clang vs. Clang

#110
post #25
post #6

Why does the code need to rely on hacks to get around optimizations? Can't they be disabled per-unit by just compiling different files with different optimization flags?

You can’t realistically have a C compiler that doesn’t do any optimizations. For one thing, CPU caches are wonders of technology, but a C compiler that only uses registers for computations but stores all results in memory and issues a load for every read will be unbearingly slow. So, you need a register allocator and if you have that, you either need (A) an algorithm to spill data to memory if you run out of register…

That would bring us back to the days of 8 and 16 bit home computers, where the quality of C compilers outside UNIX, was hardly anything to be impressed about.
Post reply on HN