Live data from Hacker News

Why undefined behavior may call a never-called function

kristerw.blogspot.com

61–70 of 183 posts

Re: Why undefined behavior may call a never-called function

#61

Earlier quoted context omitted.

The original ANSI C committee had no idea about modern optimization pipelines. If people had continually pushed back against undefined behavior back then, there's a good chance that by 2017 the result would have been that C would be dead , replaced by a language that allows for modern optimization techniques.

It would have been much more prudent if the committee defined the behavior in a way amenable to optimization, rather than asking for a blank check.

They did. "Undefined" doesn't mean "unconsidered by the committee", it means "do what you must for optimization".

Re: Why undefined behavior may call a never-called function

#62
post #19

Earlier quoted context omitted.

This, just so much this! I've been longing for a C compiler with a "sane optimizations only"-switch like forever. I'd gladly give up on the additional couple of per cent speed improvement obsessive compulsive compiler writers managed to eke out by ignoring the source codes' obvious intentions and defending it with "but technically it's undefined behaviour"!

Everyone posts a comment like this every time undefined behavior surprises someone. But the reality is that the reason why C remains alive is that compiler writers have managed to make it fast. It is not "a couple of percent": these kinds of optimizations can make an enormous difference when, for example, they're the difference between vectorizing a loop and not doing that. Compiler writers are not "obsessive compuls…

C has been pretty fast for basically ever at this point. All these optimizations are just masturbation. Bonus RISC processors are mostly dead at this point having been replaced with pipelined superscalar machines. Which means most of these optimizations are totally pointless.

Best comments I've read is one what notes that when you profile modern programs most of the code paths are dead. Yet you have intense hotpots which often turn out to be hand rolled assembly. The second is that you get real speed up carefully choosing your algorithms and memory layout.

Re: Why undefined behavior may call a never-called function

#63
post #14

Earlier quoted context omitted.

If it's run as an unprivileged user rather than root, it won't be able to delete the whole system. I'm pretty sure that means it won't do anything, but I don't feel like trying it.

>I'm pretty sure that means it won't do anything Assuming an rm without the "-preserve-root" default, it would remove everything that it had permission to. So, eventually, for example, it would wipe your home directory. I suspect this to be the case for OSX, Alpine Linux (or other distros that use busybox), probably some of the BSD distributions, etc.

  $ uname -sr
  FreeBSD 11.1-RELEASE

  $ rm -rf /
  rm: "/" may not be removed
No idea about other BSDs.

Busybox's rm is indeed happy to nuke /.

Re: Why undefined behavior may call a never-called function

#64
post #40
post #30

Earlier quoted context omitted.

Exactly. This isn't "fuck the programmer if he fucks up," it's "let's try to do really good optimizations." It's really nice to be able to use abstractions that cost nothing because the compiler is smart. In this particular case, you might have a function pointer that exists for future expansion, but which currently only ever holds one value. In a case like that, it's really nice if the compiler can remove the indire…

> It's really nice to be able to use abstractions that cost nothing because the compiler is smart. But the compiler is not smart. It's screwing up in certain cases. In this example if it was smart it would have figured out that the value never was initialized. > In this particular case, you might have a function pointer that exists for future expansion, but which currently only ever holds one value. Then define it as…

> if it was smart it would have figured out that the value never was initialized.

But that's false, which just goes to show that the compiler writers know way more about this than you do. There's nothing stopping this from being linked into a binary which doesn't even call main, or which calls NeverCalled, etc. And I bet you will also insist stamping your feet that of course programmers should be able to construct function pointers - to functions like, y'know, Never called - from arbitrary bit patterns. You know nothing, but you're convinced you know so much more than those stupid compiler writers.

Re: Why undefined behavior may call a never-called function

#65
post #49

Earlier quoted context omitted.

You're the last person I'd have expected to make that sound like a bad thing. While benchmark games played a part in the modern ‘undefined behavior’, I'm not so sure that it would made much difference to adoption of the language. Consider Linus Torvald's well-known rant as a point in the opposite direction. In the universe where ‘undefined behaviour’ had been clearly specified as ‘what you write is what you get’, C m…

It's not just benchmark games. It's people's real-world code. Look at how often folks on HN complain that the Web platform is useless because JS is slow. C without optimizations is just as slow if not slower. Compiler optimizations are so good that people don't realize how much they rely on them. Linus was wrong when he complained about the compiler treating null dereference as undefined behavior. This is a very impo…

But sometimes these checks just seem to end up entirely removed, and that is just not OK: I have been a developer working on performance constrained system software in low-level programming languages (including heavily optimized games written in C++) and this undefined behavior idea has gone way way too far. I can always make code faster by removing checks I don't need manually: trying to compare the small gains here with "let's just use node.js lol" is dishonest.

C++ states stuff like references must not be NULL and "this" must not be NULL, but in the real world it is possible for a NULL pointer to be dereferenced into a reference and for a method to be called on that reference and for the method to complete execution without the app crashing. Yet, some C++ compilers are now insisting that "this == NULL" checks (which is the most hilarious case, but simple "&ref == NULL" are the same) and all the dependent code be entirely removed, hamstringing runtime safety and sanity checks.

What works for me is when the compiler says "for this to happen the code would have had to crash"; but what does not work for me is "for this to happen the code would have to be violating the specification" as the entire point of NULL checks in a program was always to check for invalid execution and mitigate its effects :/ and yet since this code has never crashed on any reasonable C++ compiler the only way to check for it is to add comparisons that are now being removed under some misguided assumption that the code would fail at runtime.

Re: Why undefined behavior may call a never-called function

#67
post #57

rm -rf / Using this type of code to show unintended consequences is itself a hotbed of unintended consequences! Just put in code that prints "pwned" instead of running code that would delete someone's system or home folder.

Do any up-to-date operating systems actually run this without the --no-preserve-root flag?

Re: Why undefined behavior may call a never-called function

#68
post #19
post #11

I have become convinced that the current screw-the-programmer interpretation of ‘undefined behaviour’ was not intended, or even imagined, by the original ANSI C committee. Within the committee's mandate to ‘standardize existing practice’, it was simply an acknowledgement that C compilers translated straightforward C code into straightforward machine code without adding safety checks, and that simple code might —​ in…

This, just so much this! I've been longing for a C compiler with a "sane optimizations only"-switch like forever. I'd gladly give up on the additional couple of per cent speed improvement obsessive compulsive compiler writers managed to eke out by ignoring the source codes' obvious intentions and defending it with "but technically it's undefined behaviour"!

How do you know it would only be a few percent? What if such a switch made your code ten times slower? Without actually reading the clang code and seeing what all the optimization passes do, I don't see any a priori reason for assuming it would be a few percent rather than 10x.

Re: Why undefined behavior may call a never-called function

#69
post #43
post #41

Earlier quoted context omitted.

> You'd think that decades of security breaches caused by buffer overflows would make people think, "You know, it's 2017. Maybe array dereferencing without bounds checks is a bad idea even if it does let my code run a little faster." Alas. And there are dozens of languages that will let you sacrifice that bit of speed for some safety.

If you meant to imply that this is not a problem because there are other languages one can use, I disagree. C holds a unique position in the computing world. There is an enormous corpus of C source code out there, and more is being written all the time notwithstanding that C as currently specified in not a sane language. So what C compilers do matters whether you like it or not.

And why do you think that is the case?

Re: Why undefined behavior may call a never-called function

#70
post #11

I have become convinced that the current screw-the-programmer interpretation of ‘undefined behaviour’ was not intended, or even imagined, by the original ANSI C committee. Within the committee's mandate to ‘standardize existing practice’, it was simply an acknowledgement that C compilers translated straightforward C code into straightforward machine code without adding safety checks, and that simple code might —​ in…

AFAICT, the main rationale for undefined behavior in the original instance boils down to traps: any operation that could trap is undefined. In particular, this seems to motivate why signed integer overflow is undefined as opposed to implementation-defined. It should be pretty clear why it's a good thing that potentially-trapping instructions have undefined semantics in those instances (or, perhaps more accurately, ha…

Not just traps... systems level code often relies on what is by the standard "Undefined Behavior" but is in fact well defined for that implementation, something the standard allows for very good reason. At the end of the day interacting with hardware sometimes requires doing things that "No Sane Programmer" would ever do normally. Most of undefined behavior is a nice way to say that the standard is not going to try to define something that is probably an artifact of the implementation. Compiler writers can however use and abuse the fact that there is no constraints. MSVC will prune code with undefined behavior. Clang may call the never-called (only if not static or in an anonymous namespace), etc. Undefined behavior is only undefined by the standard... in most cases it has very well defined results.. for a specific implementation and hardware set.
Post reply on HN