Live data from Hacker News

The Problem with Friendly C

blog.regehr.org

21–30 of 174 posts

Re: The Problem with Friendly C

#21
post #16

Target a virtual architecture (LLVM IR). Run the original code in an emulator for the target system (QEMU); give warnings where the two differ, with a switch for the virtual architecture to mimic the behavior of whatever architecture you need. You get friendly C, a new way to think about warnings, and your old code can still be compatible with the new virtual architecture. Next up, world hunger.

This solution relies on some undecidable problems as being decidable

How so? I don't see the connection to undeciability.

Re: The Problem with Friendly C

#22
post #18

Target a virtual architecture (LLVM IR). Run the original code in an emulator for the target system (QEMU); give warnings where the two differ, with a switch for the virtual architecture to mimic the behavior of whatever architecture you need. You get friendly C, a new way to think about warnings, and your old code can still be compatible with the new virtual architecture. Next up, world hunger.

You can't simply run code for all possible inputs.

No, but you can certainly exercise it well. It might be a degree of confidence measure. Combined with property verification (so, formal verification), you'd get a damn good idea they'd be close.

Re: The Problem with Friendly C

#23
post #9
post #3

If ((uint32_t)x But why can't it just produce something different on each system? Allow me to call it as I see it: the modern interpretation of undefined behaviour is bullshit. What compilers do today should be the recourse of absolute last resort, and the sort of thing that makes its authors feel bad. But it seems to be treated as a matter of course. I don't know what to say. Mandatory reading: http://robertoconcert…

Probably because it would go in the same direction as multiple-dialects proposal. If we can have bcc-x86 that returns x ans bcc-arm that returns 0, then we'll likely end up with bcc-x86-fullshifting-nounaligned-nullcheckremoveok-otheroptions and 10 dialects that switch some of those behaviours. If you're designing a well-defined language, then why make it well-defined-per-architecture?

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 spend your time creating a stick to beat them with?

Re: The Problem with Friendly C

#24
post #3

If ((uint32_t)x But why can't it just produce something different on each system? Allow me to call it as I see it: the modern interpretation of undefined behaviour is bullshit. What compilers do today should be the recourse of absolute last resort, and the sort of thing that makes its authors feel bad. But it seems to be treated as a matter of course. I don't know what to say. Mandatory reading: http://robertoconcert…

I think the problem is that some of these optimizations aren't just compiler makers being greedy, they're actually a huge benefit. IMO what's missing is the ability to mark areas "unsafe" -- IE, tell the compiler "it's ok to take advantage of certain optimizations here" while marking other areas "please don't goof with this" (ie, security critical code). You can kind of do this with pragmas, but not really.

Here's a good example from the llvm blog: ( http://blog.llvm.org/2011/05/what-every-c-programmer-should-... )

> Signed integer overflow: If arithmetic on an 'int' type (for example) overflows, the result is undefined. One example is that "INT_MAX+1" is not guaranteed to be INT_MIN. This behavior enables certain classes of optimizations that are important for some code. For example, knowing that INT_MAX+1 is undefined allows optimizing "X+1 > X" to "true". Knowing the multiplication "cannot" overflow (because doing so would be undefined) allows optimizing "X*2/2" to "X". While these may seem trivial, these sorts of things are commonly exposed by inlining and macro expansion. A more important optimization that this allows is for "> for (i = 0; i > In this loop, the compiler can assume that the loop will iterate exactly N+1 times if "i" is undefined on overflow, which allows a broad range of loop optimizations to kick in. On the other hand, if the variable is defined to wrap around on overflow, then the compiler must assume that the loop is possibly infinite (which happens if N is INT_MAX) - which then disables these important loop optimizations.

Re: The Problem with Friendly C

#25
post #17
post #13

Earlier quoted context omitted.

What modern interpretation? As long as C has been standardized, undefined behaviour has meant nasal demons. There's an argument to be had that some undefined behaviour should rather be unspecified or implementation-defined, but compilers making use of it for aggressive optimization? That's by design. Quoting another article by John Regehr: My view is that exploiting undefined behavior can sometimes be OK if a good de…

Undefined behaviour has meant undefined behaviour. That doesn't mean nasal demons. That means this: Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the i…

the Standard imposes no requirements on undefined behaviour. The list you quoted is informative, to give us a basic idea what to possibly expect, not a list of things we may rely upon.

Rejecting translation outright aside, undefined behaviour is the strongest language the standard uses for illegal constructs. I do not see it as undefined in the sense of lacking a common definition (eg is 0 a natural number, or not) but something that is undefined the way 0/0 is undefined in maths.

In a way, the criticized behaviour of aggressively opimizing compilers is a consequence of ignoring the situation completely with unpredictable results. It's the same line of reasoning that makes restrict useful, just on steroids.

Re: The Problem with Friendly C

#26
post #5

How exactly would performance degrade by defining a virtual machine for C programs to run in where assurances are given that all of the standard behavior is fully defined -this operation either fails, or gives THIS result-? It sounds like it could be massively useful, at least for non-realtime applications.

Creating a C virtual machine might work for most user space applications, but what about operating systems and system libraries? Most operating systems and low level libraries are implemented in C, or at least partially, and implementing a C virtual machine that you then want to run kernel code on top of, seems daunting. Performance wise I would assume a C virtual machine would be on par with java (JVM).

So I would say a C virtual machine wouldn't be a very fruitful endeavor, Most user space applications can be written in a Memory safe language, while the most incumbent C code, kernel and system libraries probably can't or at least shouldn't be executed in a virtual machine.

Re: The Problem with Friendly C

#27
Tone: I do not mean this as sarcasm or merely chasing fashion, I'm quite serious. As both theory and practice are showing, you're never going to be able to get the consensus you want out of C. There's no "saving" C... not because that's somehow mathematically impossible, but simply because the project is too staggeringly large for us to even wrap our heads around. It would literally be easier to get people to start using another language...

... so, why not do that then? We have, for perhaps the first time in 40 years, a candidate for a systems language that can truly replace C, that has truly different semantics (i.e., not C++, which is still profoundly C with a lot of stuff bodged on the side). I'd suggest trying to use Rust, and working with that team to nail down whatever remaining issues may yet be undefined in Rust that may cause trouble in the future. Whatever remaining practical problems there may be (and my impression is that that isn't really a long list), work on resolving them.

Again, I am not being sarcastic or cynical; it is truly my estimate that it would be easier to get people behind that than to fix C at this point. Probably by a good two or more orders of magnitude. Obviously we're not going to rewrite all existing code. Obviously there's a lot of code still to be written that is so deeply embedded in C that there's no practical alternative to adding more C to it, even in a world of FFI support and such. But if the people who care about the idea of BoringC or Friendly C start getting behind Rust, getting their hands dirty with it, and doing what the Mozilla project is doing to find places where they can start slotting subsystems in cleanly to existing code bases, you may just be able to start creeping out of the mess we're in now.

And... who knows. If this becomes acceptable, then generally considered a good idea, then best practice, then perhaps even something you need to do unless you want people to think poorly of you... perhaps real change will prove to be less intractable than we thought. People consistently overestimate change in the short term but underestimate in the long term. It is, perhaps, not too much to hope for that huge swathes of our fundamental systems could be running on Rust in 20 years, instead of C.

So I'll reemphasize once again... yes, I know I'm proposing a staggeringly enormous change. The only thing that it has going for it is that I still think it's easier than the staggeringly-enormous-squared other choice.

Something's gonna happen. After all... what's the alternative? That C is still the foundational language of the entire computing world in 2035?

Seriously?

Re: The Problem with Friendly C

#28
post #20
post #14

Earlier quoted context omitted.

Apparently a (uint32_t) shifted by 32 is license to become completely insane. int main(int argc, char **argv) { uint32_t x = (uint32_t)0x12345678

I am almost LOLing at the segmentation fault. Where the fuck does a segmentation fault come from? It's shifting a value . Utterly mystifying.

Maybe it's a hardware trap and not a regular null reference segmentation fault?

Re: The Problem with Friendly C

#29
post #16

Earlier quoted context omitted.

This solution relies on some undecidable problems as being decidable

How so? I don't see the connection to undeciability.

If you have undefined behaviour after an infinite loop that breaks on some condition, then that undefined behaviour will not necessarily have been triggered. Thus, no warning.

Re: The Problem with Friendly C

#30
post #3

If ((uint32_t)x But why can't it just produce something different on each system? Allow me to call it as I see it: the modern interpretation of undefined behaviour is bullshit. What compilers do today should be the recourse of absolute last resort, and the sort of thing that makes its authors feel bad. But it seems to be treated as a matter of course. I don't know what to say. Mandatory reading: http://robertoconcert…

I think the problem is that some of these optimizations aren't just compiler makers being greedy, they're actually a huge benefit. IMO what's missing is the ability to mark areas "unsafe" -- IE, tell the compiler "it's ok to take advantage of certain optimizations here" while marking other areas "please don't goof with this" (ie, security critical code). You can kind of do this with pragmas, but not really. Here's a…

For example, knowing that INT_MAX+1 is undefined allows optimizing "X+1 > X" to "true".

If a programmer writes "X+1 > X", chances are this is an overflow check. Doing this is perfectly defined by the standard if X is an unsigned integer, but not if it's signed. That's what doesn't make sense, since they could've made the unsigned case undefined as well.

which allows a broad range of loop optimizations to kick in

What sort of optimisations exactly, and just how significant are they? I don't believe C should be a language where the compiler does all sorts of high-level optimisation; it should be a straightforward "do what I say" type of language where you get almost exactly what you write, and the only optimisations should be at the level of things like instruction selection --- the optimisations that a programmer would not be able to do at the source level.

Post reply on HN