This is the real problem. We have reached a situation where a small number of compilers dominate the space, but yet do not charge the users and so do not treat them as customers. The C standard is a product of an era where you would pay for your tools and so would demand a refund from any compiler vendor that would treat undefined behavior in an absurd manner.
Undefined behavior in C is a reading error
11–20 of 503 posts
Re: Undefined behavior in C is a reading error
#12If C is just a portable assembler then what if the assembly itself has undefined behaviour. :)
This exists, but the effect of undefined behavior in CPU architectures is a little bit more forgiving than the interpretation of UB in C to mean "literally the entire program has no meaning". Instead, usually the program will execute correctly up to the invalid instruction, and then something happens, and then the CPU will continue executing from that state. It's actually fairly difficult to build an instruction with…
That is not quite what the interpretation of UB in C is, AFAIK. UB in C is generally interpreted as meaning that any path which would trigger an UB is invalid, because if it will be invalid once the UB is reached, well, if we know for sure we're going to UB for the instruction before it, we can say that we're already in UB.
Whole-program invalidity can occur when the compiler manages to infer that no execution path is UB-free, in which case yes the program is meaningless. More generally, programs will go off the rail as far ahead of the UB as the compiler managed to determine that the UB would unfailingly be reached.
And it's usually because the compiler works backwards: if a path would trigger an UB, that path can not be legally taken, therefore it can be deleted. That's why e.g. `if (a > a + 1)` gets deleted, that expression makes sense if you assume signed integers can overflow, but the compiler assumes signed integers can't overflow, therefore this expression can never be true, therefore it's dead code.
This is important, because many such UBs get generated from macro expansion and optimisations (mainly inlining), so the assumption of UB-impossibility (and thus dead code) enables not just specific optimisations but a fair amount of DCE, which reduces function size, which triggers further inlining, and thus the optimisations build upon one another.
Re: Undefined behavior in C is a reading error
#13Re: Undefined behavior in C is a reading error
#14You would sound crazy for complaining that “a syntax error in like 4325 make the whole program uncompileable.”
It’s not like running C programs have any idea that they are hitting undefined behavior. It’s just that the generated assembly is allowed to assume that you know what you’re doing.
i
Can just generate the shift without worry that it will blow up and the propagate the knowledge up that runtime_val’s domain is [0,31] for the optimizer.Re: Undefined behavior in C is a reading error
#15We have the benefits of a standard, portability and multiple compilers. But it also comes with duties.
BTW.
The C++ people seem to tackle some of the issues around widespread 'undefined behavior' and defined a lot of behavior:
1) If a side effect on a scalar object is unsequenced relative to another side effect on the same scalar object, the behavior is undefined.
i = ++i + 2; // undefined behavior until C++11
i = i++ + 2; // undefined behavior until C++17
f(i = -2, i = -2); // undefined behavior until C++17
f(++i, ++i); // undefined behavior until C++17, unspecified after C++17
i = ++i + i++; // undefined behavior
2) If a side effect on a scalar object is unsequenced relative to a value computation using the value of the same scalar object, the behavior is undefined.
cout
Source: https://en.cppreference.com/w/cpp/language/eval_orderThey replaced the entires "Sequence Point Rules" by "Sequenced-before rules". Compiler implementers cannot choose what to do (implementation defined) or neglect the issue (undefined behavior) - the must act well defined now in many situations.
Re: Undefined behavior in C is a reading error
#16Compilers have become more powerful (opening up new ways to exploit undefined behavior) and the primary C compilers are free software with corporate sponsors, not programmer customers (or else perhaps Andrew Pinski would not have been so blithe about ignoring his customer Felix-gcc in the GCC bug report cited above). This is the real problem. We have reached a situation where a small number of compilers dominate the…
Re: Undefined behavior in C is a reading error
#17If C is just a portable assembler then what if the assembly itself has undefined behaviour. :)
Re: Undefined behavior in C is a reading error
#18Earlier quoted context omitted.
This exists, but the effect of undefined behavior in CPU architectures is a little bit more forgiving than the interpretation of UB in C to mean "literally the entire program has no meaning". Instead, usually the program will execute correctly up to the invalid instruction, and then something happens, and then the CPU will continue executing from that state. It's actually fairly difficult to build an instruction with…
Wow! Interesting to see hints that meltdown exists years before it was officially published.
https://en.wikipedia.org/wiki/Meltdown_(security_vulnerabili...
Re: Undefined behavior in C is a reading error
#19Compilers have become more powerful (opening up new ways to exploit undefined behavior) and the primary C compilers are free software with corporate sponsors, not programmer customers (or else perhaps Andrew Pinski would not have been so blithe about ignoring his customer Felix-gcc in the GCC bug report cited above). This is the real problem. We have reached a situation where a small number of compilers dominate the…
Re: Undefined behavior in C is a reading error
#20Instead, if you dislike undefined behavior, I challenge you to come up with workable semantics for non-gratuitous scenarios, of which I'll lay out three. Remember, if it's not undefined, then there are some defined semantics that need to be preserved, even if implementation-defined, so you should be able to explain those semantics. If you can't find such semantics, then maybe undefined behavior isn't such a bad thing after all.
The first case is the compiler hint. A function marked _Noreturn returns. Or you alias two pointers marked restrict. Remember that the entire point of these hints is to permit the compiler to not generate code to check for these scenarios.
The second case is uninitialized memory. You've probably already come up with such semantics, so I'll point out an optimization scenario that you probably don't object to that your semantics didn't cover:
static int y; /* uninitialized */
_Bool cond = f();
int x = cond ? 2 : y; /* Is it legal to fold this to int x = 2; ? */
Hopefully, you'll agree that that is a reasonable optimization. Now consider this code: static int y; /* uninitialized */
_Bool cond1 = f(), cond2 = g();
int x1 = cond1 ? 2 : y; /* So int x1 = 2; */
int x2 = cond2 ? 3 : y; /* So int x2 = 3; */
if (!cond1 && !cond2) {
assert(x1 == x2); /* Uh... */
}
This is just scraping the tip of the surface of uninitialized values. Developing sane semantics around these sorts of values that also allow reasonable optimizations is challenging. You can look at the very lengthy saga that is undef, poison, and freeze in LLVM (still ongoing!) to see what it looks like in practice.The third category is traps. Let's pick an easy example: what happens if you dereference a null pointer? Now let's consider the consequences of those semantics on some more code examples:
int *x = NULL;
int *y = &*x; /* Can this be lowered to int *y = x; ? */
size_t offset = (size_t)&((struct foo *)NULL)->field; /* Remember, x->a is actually (*x).a */
int *z = get_pointer();
*z; /* No one uses the result of the load, can I delete it? */
for (int i = 0; i
Note that all of the optimizations I'm alluding to here are ones that would have existed all the way back in the 1980s when C was being standardized, and these are pretty basic, pedestrian optimizations that you will cover in Compilers 101.