Live data from Hacker News

The Problem with Friendly C

blog.regehr.org

81–90 of 174 posts

Re: The Problem with Friendly C

#81
post #53

I think this is a fundamental problem with C. Both of the language itself, but also of the C programming culture of insisting on very low levels of abstractions. Bugs and undefined behaviour is too easy for programmers to write when the level of abstraction is low. Too much boilerplate to get wrong. But too low a level is also bad for an optimizer. It cant assume it understand the programmers intention with the code.…

I don't think C is bad for optimizers. What language consistently generates faster machine code than C?

Fortran, but Fortran is also quite low-level. It can do some optimizations that C can't because it disallows pointer aliasing.

Re: The Problem with Friendly C

#82
post #69
post #23

Earlier quoted context omitted.

But the standard already permits this. I'm just suggesting that rather than have compiler writers use these liberties to allow their compiler to remove code or make it do something peculiar, they could just allow it to do the natural thing for the target platform. (This is explicitly allowed by the standard. Consult its definition of undefined behaviour.) Why not create a compiler that does what your users want? Why…

Because the overwhelming majority of users of C compilers are compiling someone else's code that isn't narrowly targeted to the platform+compiler in question. So they get no benefit at all from specifying undefined behavior, but they do get a benefit from the performance gained by assuming away undefined behavior as dead cases.

they do get a benefit from the performance gained by assuming away undefined behavior as dead cases

I think this is a highly suspicious assumption. Programmers don't write code for no reason; they have a mental model of the current state of parameters and variables, and establish invariants as they write loops and conditionals. If you're going to start throwing out chunks of code the programmer wrote, you need to be certain - not merely unsure, but certain - that the code is dead. Infectious undefined values that propagate through data flow to break code flow isn't a sane programming environment.

Re: The Problem with Friendly C

#83
post #79

Earlier quoted context omitted.

> But why can't it just produce something different on each system? It could. That is indeed how Rust, for example, defines it. But in C "unspecified values" have a way of turning into undefined behavior really quickly. Here's an actual bug we have in Rust [1]: let index = 1.04E+17 as u8; let array = vec![1, 2, 3, 4, 5]; println!("{}", array[index as usize]); // segfault Why does that segfault instead of emitting a s…

The problem here is, as you point out, the conversion of double to unsigned byte. More specifically, the problem is that processing this operation is not producing error; it instead propagates the problem and produces invalid code. That's a situation where nobody wins. I think we'd be better off if many undefined behaviours were instead implementation defined. If, instead, LLVM had converted the double to some intege…

> On strict aliasing, I'm against it without explicit opt-in over a delimited subset of source code. I understand that using & is going to harm the performance of my code; I think that's an acceptable tradeoff for more predictable behaviour.

I believe that you and others think that's an acceptable tradeoff. At the end of the day, though, most people want C compilers to produce the fastest code possible. Compiler authors are just responding to what users want. The example below is actually an excellent example of this.

> If it harms the performance of C++ vector iterators, for example, I don't care: strict aliasing rules are a worse cure than the disease of C++'s poorly thought out abstraction tools.

It's not just C++. Consider (from Chris Lattner's blog post):

    float *array;
    void zero_out() {
        for (int i = 0; i 
People saw compilers missing the optimization to compile this to a memset, and filed bugs against them. As a matter of fact, though, that is an illegal optimization to make unless the compiler is able to take advantage of strict aliasing. That's because, absent strict aliasing, array[0] could legally be a type-casted pointer back to "array" itself, even though almost nobody would actually write that code.

The reason why compilers implemented strict aliasing rules is that their users demanded that they fix "optimizer bugs" like the one above. And, honestly, I can't blame either the compiler developers or the users. Strict aliasing ends up being one of the things that's often important for performance. The problem, if anything, lies with the C language.

Re: The Problem with Friendly C

#84
post #65

Earlier quoted context omitted.

That would work if most applications spent all their time in a few hot spots. But, contrary to popular wisdom, that's usually not the case. Most applications have flat profiles (to steal a quote from DannyBee—but it matches my experience as well). They have flat profiles because people have spent a lot of time optimizing them. In this context—which is the norm—eliminating optimizations to save compile time and deferr…

I agree optimization is important. So important that it should be pushed down into the hardware. Binaries should look almost like source code. But that's just my vision for what it's worth.

Hardware already does insane amounts of optimization. The modern superscalar out of order processor basically does it's own JIT from X86 into their own internal micro-ops. Reordering instructions on the go etc. That's another 2-10x speed difference on modern computers.

Re: The Problem with Friendly C

#85
post #61

Since, according to Chandler Carruth, the aim for Clang is to not do optimizations based on undefined behaviour without a corresponding instrument in ubsan[0], I don't see much traction on this well-defined/boring C effort. You know, in my experience, things would be great if people actually turned on warnings. I write all my new code with -Weverything with a few noise categories turned off. Everybody should build co…

For new code and for actively maintained old code you are perfectly correct. But there is a huge amount of C code we still rely on that isn't getting enough attention and I don't want that getting broken either.

Re: The Problem with Friendly C

#86
post #63

Earlier quoted context omitted.

IMHO aggressive optimization at compile time is an example of premature optimization. Let the hardware have access to a straightforward representation. Once the run-time hot-spots are identified, the hardware (firmware, VM, whatever) can rewrite the binary code to execute faster. Excessive compiler optimization makes this difficult or impossible (too much information thrown away.) Compilers should be designed for fas…

It doesn't really seem like this solves the problem of optimizers introducing bugs and vulnerabilities. Take the canonical optimizer-created security hole: the hardware optimizer replaces a constant-time compare (which doesn't leak timing information) with a variable-time compare (which does). I don't think this solves the problem we're setting out to solve, ie. the optimizer introducing bugs.

Maybe we are all better served by faster compilers that create straightforward binaries (and less bugs overall both in the compiler and application code.) Optimization researchers could focus on source-to-source transformation tools with intelligent human-in-the-loop guidance. Or else they can work at the hardware/JIT level if they prefer.

Right now compiler writers are playing in a kind of local minimum (premature optimization as I said.) This may produce 3x-5x faster binary code today but also forces CPU manufacturers to retain backward compatibility causing them to also stay stuck in this local well. Eventually a new architecture is created (with 10x the registers etc.) and the cycle continues.

Re: The Problem with Friendly C

#87
post #61

Since, according to Chandler Carruth, the aim for Clang is to not do optimizations based on undefined behaviour without a corresponding instrument in ubsan[0], I don't see much traction on this well-defined/boring C effort. You know, in my experience, things would be great if people actually turned on warnings. I write all my new code with -Weverything with a few noise categories turned off. Everybody should build co…

Also we're all waiting for the strict aliasing checker.

Re: The Problem with Friendly C

#88
post #79

Earlier quoted context omitted.

The problem here is, as you point out, the conversion of double to unsigned byte. More specifically, the problem is that processing this operation is not producing error; it instead propagates the problem and produces invalid code. That's a situation where nobody wins. I think we'd be better off if many undefined behaviours were instead implementation defined. If, instead, LLVM had converted the double to some intege…

> On strict aliasing, I'm against it without explicit opt-in over a delimited subset of source code. I understand that using & is going to harm the performance of my code; I think that's an acceptable tradeoff for more predictable behaviour. I believe that you and others think that's an acceptable tradeoff. At the end of the day, though, most people want C compilers to produce the fastest code possible. Compiler auth…

I'm not sure that's the best example. In that case, the knowledge that the 'array' global never has its address taken allows you to perform the optimization. You can also rewrite it by copying 'array' to a separate local that doesn't have its address taken, and ordinary SSA construction solves the problem.

I feel like a new systems language needs to come and make aliasing explicit in a way, so that it doesn't surprise programmers but still allows them to get the optimizations that they want. Tracking uniqueness like Rust is a good first step, but it doesn't work for unsafe code or anything with more subtle aliasing relationships.

Re: The Problem with Friendly C

#89
post #72
post #62

Earlier quoted context omitted.

GCC has an intermediate representation. It's just not exercised as a separate project (like LLVM). It's far from "single pass"

You're right; to be clear, what I was talking about above was whether the passes are decoupled at an abstract level, not so much whether the implementation uses multiple phases. You can take LLVM "bitcode" as the product of clang, move it to another system with a different arch, and finish compiling the IR to a native binary on that system. This implies that the first step and second step don't share any state other…

LLVM IR isn't architecture-neutral. It encodes type layout / alignment, as well as ABI details. You can construct an LLVM target that is itself compilable on multiple architectures (see Portable Native Client), but it requires a nonstandard ABI.

Re: The Problem with Friendly C

#90
post #61

Since, according to Chandler Carruth, the aim for Clang is to not do optimizations based on undefined behaviour without a corresponding instrument in ubsan[0], I don't see much traction on this well-defined/boring C effort. You know, in my experience, things would be great if people actually turned on warnings. I write all my new code with -Weverything with a few noise categories turned off. Everybody should build co…

At work, we migrated a small codebase this year to VS2008. It was "painful". This week I put it through asan and found five buffer overflows or similar with about a half hour of work, but the thing still spits out a hundred warnings or so when you compile, and that's with default settings. This project is about 1% of the code, by line count, that my team is responsible for.

At home, I turn on -Wall -Wextra for starters, enable about a dozen more for good measure, with -Werror as the pièce de réisistance.

Post reply on HN