5 ms·
What do you mean by "these UB optimizations"? C is a low-level language; it's basically impossible for a compiler to reason about the code unless it makes certa
by ynik 5y ago
What do you mean by "these UB optimizations"? C is a low-level language; it's basically impossible for a compiler to reason about the code unless it makes certain assumptions.
It needs to assume the code is not self-modifying to do pretty much any code-generation more intelligent than a macro assembler. It needs to assume the code isn't messing with the stack frames/return addresses in order to inline functions. It needs to assume the code isn't using an out-of-bounds pointer to access a neighboring local variable, so that it can move local variables into registers.
"gcc -O0" is a good approximation for the performance you get if the compiler isn't allowed to optimize based on UB.
Yes, that means C without optimizing without UB is slower than Java. Optimizations need some form of reasoning about what's happening. For Java it's optimizing based on guarantees provided by the language (there's no raw pointers that could mess with the things listed above). But C doesn't provide any hard guarantees, so instead it needs to blindly assume that the code will behave sanely.
Also note that for many of the more manageable sources of UB, most compilers provide a choice (-fwrapv, -fno-strict-aliasing, ...). Yet few projects use these options, even when they use other gcc/clang-specific features. Doesn't that indicate that C programmers indeed want to sacrifice safety for optimizations?
- gpderetta 5y agoExactly. For example there were programs 30-40 years ago that relied on exact stack layouts. These days everybody would agree they are completely broken. The issue of course is that it is extremely hard to write programs that have no UB. It would be nice for compilers to have an option to automatically introduce assetions whenever they rely on some UB-derived axiom, basically as a sort of lightweight sanitizer. In fact if we had sanitizers 30-40 years ago probably things would be better today.
- MauranKilom 5y ago> It would be nice for compilers to have an option to automatically introduce assetions whenever they rely on some UB-derived axiom Modifying a value from a different thread without synchronization is UB. The compiler assumes this does not happen in order to e.g. move things into registers. Could you elaborate how (and how often) you would like to have this kind of UB-derived axiom ("this value remains the same from here to there") checked with assertions?
- gpderetta 5y agoObviously you wouldn't be able to catch many, or even most cases. Use-after-free is another case that would be very expensive to detect.
- pjmlp 5y agoI was using it in 2000, https://www.parasoft.com/products/parasoft-insure/ https://www.parasoft.com/products/parasoft-insure/ 21 years later it is still an uphill battle to adopt such technology in C and C++ projects.
- vyodaiken 5y agoThat's good example, because nobody would complain if stack layouts changed and those programs failed. But if the compiler chooses to "optimize away" checks on stack layout, that's a different thing altogether. Also note that if you use pthreads or Linux clone or you are writing an operating system you can need to rely on exact stack layouts even today.
- saagarjha 5y agoStack layouts are only really relevant at ABI boundaries. In these cases the layout is usually specified in extensions to C or in other ways, such as handwritten assembly.
- vyodaiken 5y agoLinux clone, pthreads, and os code commonly look at stack boundaries
- gpderetta 5y agoNot sure what you are referring to with stack boundaries. Of course the ABI imposes some minimal requirements at ABI visible points, but these days you can't even rely on the existence of frame pointers to traverse the stack and you have to use the DWARF unwind machinery. And the content of the stack frame itself is completely unspecified of course.
- vyodaiken 5y agoSo I create a thread with a custom stack which is an allocated buffer. At the top, I write a sequence of bytes in some order. Then I periodically read the top of the stack to see if the stack is getting close to overflow. Meanwhile, the thread code is also addressing the same store.
- pjmlp 5y agoWe had sanitizers since C exists, 1979 to be more exact. "Although the first edition of K&R described most of the rules that brought C's type structure to its present form, many programs written in the older, more relaxed style persisted, and so did compilers that tolerated it. To encourage people to pay more attention to the official language rules, to detect legal but suspicious constructions, and to help find interface mismatches undetectable with simple mechanisms for separate compilation, Steve Johnson adapted his pcc compiler to produce lint [Johnson 79b], which scanned a set of files and remarked on dubious constructions. " -- https://www.bell-labs.com/usr/dmr/www/chist.html https://www.bell-labs.com/usr/dmr/www/chist.html
- gpderetta 5y agoI was going to add "in open source compilers" to hedge my statement :). That seems to be a static analysis tool though (which generally have not been great). Did it also inject runtime checks?
- deleted 5y ago[deleted]
- pjmlp 5y agoNo, but still not even that gets the love it deserve. And a famous commercial variant of it has been PC-lint from https://www.gimpel.com/ https://www.gimpel.com/. Being available on open source compilers does little to change the culture, as per latest surveys only 11% of developers care to use any kind of tooling for improving their code quality in C and C++. At CppCon a couple of years ago, only about 1% of the audience answered positively to Herb Sutters' question.
- vyodaiken 5y agoFor your last point, the extent of UB driven changes to semantics is still not widely known in the programmer community. Programmers don't read the standard - they read K&R, and K&R is right now describing a different language. We've had 15 years of programmers repeatedly filing bug reports to be told that the expected, tested, relied on, behavior was ephemeral. Only very sophisticated projects figure out about UB. Of course compilers have to make assumptions. The debate is (a) over what assumptions it is proper to make and (b) what are the permissible behaviors. The false dichotomy: either do without any optimizations at all or accept whatever UB gives you, is not a useful approach.
- MauranKilom 5y agoSo what optimizations do you mean with "these UB optimizations" then? And would it change your mind to see a benchmark proving the usefulness of that particular UB optimization?
- vyodaiken 5y agoe.g. assuming UB can't happen: deleting overflow or null pointer checks, deleting comparisons between pointers that are assumed to point at different objects, ...
- Dylan16807 5y agoI'm surprised you don't think deleting a bunch of checks will improve performance.
- a1369209993 5y ago> So what optimizations do you mean with "these UB optimizations" then? Inferring any propositional statement about the program (eg "this pointer is not null") from the fact that its negation would imply undefined behaviour.
- rectang 5y ago> most compilers provide a choice (-fwrapv, -fno-strict-aliasing, ...). Yet few projects use these options, Even if you opt into those (and the projects I've been involved with have), the precedent is established: when new compiler optimizations are introduced with disruptive semantics, they are opt-out, not opt-in — a fail-dangerous failure mode. > Doesn't that indicate that C programmers indeed want to sacrifice safety for optimizations? Maybe so. Which is one reason I wouldn't call myself a "C programmer" any more. The demands to program responsibly in C are absurdly high.
- account42 5y ago> Even if you opt into those (and the projects I've been involved with have), the precedent is established: when new compiler optimizations are introduced with disruptive semantics, they are opt-out, not opt-in — a fail-dangerous failure mode. Actually, the default for gcc is -O0. You are opting in with -O2 etc.
- pjmlp 5y agoThat is exactly how C should have kept being used, a portable macro assembler, while leaving everything else on the IT stack for more saner languages. BCPL was anyway designed to Bootstrap CPL, nothing else.