3 ms·
What would be, for example, the desirable failure mode of an use-after-free? Or a write past the end of an array? Or any of the many UBs that are extremely hard
by gpderetta 2y ago
What would be, for example, the desirable failure mode of an use-after-free? Or a write past the end of an array? Or any of the many UBs that are extremely hard to detect but have unbounded effect on the program?
Remember that the failure mode must be a) implementable, b) implementable at best minor loss of performance, c) not break the ABI.
I agree that C and C++ have way too much UB, but no UB is a pipe dream.
- lelanthran 2y ago> What would be, for example, the desirable failure mode of an use-after-free? Implementation defined > Or a write past the end of an array? Ditto > Remember that the failure mode must be a) implementable, b) implementable at best minor loss of performance, c) not break the ABI. And "Implementation defined" matches that for the two examples you gave. What it does is force the compiler vendor to say "If you do $THIS, we will do $THAT." At least then users can make informed choices, sort of like "Clang guarantees that dereferencing a NULL pointer results in an effective address of zero, but GCC makes no guarantees, therefore we'll be going with Clang". I mean, seriously, there's a lot of footguns in C only because the standards writers said "Undefined behaviour" and never expected that compiler authors will take that to mean "remove code, or anything else, including nasal demons".
- int_19h 2y ago"Implementation-defined" means that there's something consistent that happens for a given implementation that can be documented for it. What consistent behavior would you expect to see from a typical implementation on use-after-free or writing past the end of an array? Can you give an example of how that would be documented?
- lelanthran 2y ago> What consistent behavior would you expect to see from a typical implementation on use-after-free or writing past the end of an array? Can you give an example of how that would be documented? Well, yes. "Writing to memory that is not available or allocated will always cause the instructions for the write to be emitted. The statement, expression or function call that performs the write will not be eliminated." The definition above is better than UB, which boils down to "Writing to memory that is not available or allocated may or may not cause all past and future lines of code to be eliminated from the emitting stage of compilation." See the difference? The implementation-defined definition forces the compiler documentation to be honest: "We'll remove lines of code from your input" is better than "You broke the standards rules, so we get to do anything."
- gpderetta 2y ago> Writing to memory that is not available or allocated will always cause the instructions for the write to be emitted. The statement, expression or function call that performs the write will not be eliminated. That would be a significant constraint on the optimiser.
- lelanthran 2y ago> That would be a significant constraint on the optimiser. Maybe. Maybe you work on GCC (or on Clang) and know the dirty details of the code inside ... I don't. What I do know is that the performance in past C compilers which emitted every memory access unconditionally was considered "blazing fast". If this is a blow to performance, how significant is it? Can you compile two benchmarks with and without the 'perform every memory access unconditionally' flag? Is there a flag for this? If no one ever did this, how can we tell there would be an impact on performance, nevermind significant impact? Because I recall seeing benchmarks for things like eliminating `if (x+100 < x)` showing the performance impact to be not even a rounding error. I also recall seeing a benchmark somewhere for with and without the flag that removes null-checks, and once again that performance impact was, for all practical purposes, zero. In the era of speculative pipelined execution, in respect of a test/check that would be speculated in the pipeline well before it is needed, how exactly will the code run faster if the test/check is removed from the sources? Once again, I admit that I am not in the weeds in GCC or LLVM or CLang development, but I fail to see how eliminating an instruction that never slows the processor can have a noticeable impact on performance.
- gpderetta 2y agoI'm not a compiler writer, just a programmer that tries to understand the transformations the compiler is capable of. -O0 is close to what you want. For hand optimized code there isn't a huge difference between the various optimization levels, but for higher level code C++ code, the difference between -O0 and -O3 are sometimes one or two orders of magnitude. And yes, not all, or even most, optimizations apply to all programs. All loop optimizations are really most relevant for numerical, vectorizable code; branchy pointer-chasing code won't benefit from them. Branchy code can benefit from other optimizations though that can bring their own issues.