Live data from Hacker News

Undefined behavior in C is a reading error

yodaiken.com

231–240 of 503 posts

Re: Undefined behavior in C is a reading error

#231
post #163

Earlier quoted context omitted.

The current situation is not good for compiler writers either. But nobody has ever shown that either C programmers want to sacrifice safety for "optimizations", or that these UB optimizations actually improve performance of anything.

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 n…

For 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.

Re: Undefined behavior in C is a reading error

#232
post #31

The author suggests that the text following the definition of "undefined behavior", listing the permitted or possible range of undefined behavior, should be read to restrict the consequences. But the first possibility listed is "ignoring the situation completely with unpredictable results". Surely that covers any possible consequences. The author also says: > Returning a pointer to indeterminate value data, surely a…

> For example, if signed integer overflow yielded an unspecified result rather than causing undefined behavior, I wonder if any implementations would be adversely affected. I suspect so - makes it harder to reason about loop counts because the compiler can't necessarily guarantee that an incremented loop counter won't become negative and thus the loop needs to iterate more. E.g. something like for (int i=param; i Tha…

That's the exact reason why this rule was introduced into the standard: it was so C compilers could compete with Fortran compilers (Fortran has similar rules and at the time they were beating C compilers on equivalent scientific codes by 2-3x).

Fortran has even more restrictive aliasing rules than C: a function is allowed to assume that any two array arguments passed as arguments do not overlap. If they do, the behavior is undefined.

Re: Undefined behavior in C is a reading error

#233
post #174

Earlier quoted context omitted.

> meticulously written, previously fine programs With relatively few exceptions, if your program hits undefined behavior, then your program was already doing something pretty wrong to begin with. Signed overflow is a poignant example: in how many contexts is INT_MAX + 1 overflowing to INT_MIN actually sane semantics? Unless you're immediately attempting to check the result to see if it overflowed (which is extremely…

I recall a bug-report discussion that I sadly have never been able to find. It contains a pretty bad side-effect of this. It had code like: int *p; // lots of code if (p != NULL) return 1; // use p Then a later refactor wrongly added a single line before the if statement: int *p; // lots of code int a = *p; if (p != NULL) return 1; // use p This meant the null check was optimized away, since de referencing a null poi…

This was in the Linux kernel, which is compiled with special kernel flags which make dereferencing null pointers legal. In the context of that code, dereferencing a pointer and later checking it for null was absoutely meaningful. Optimizing the later null check was a compiler bug that was acknowledged and fixed. The compiler here didn't respect the semantics it had promised to kernel code. This was entirely uncontroversial; it was not a case of "unwanted optimization based on undefined behavior", it was a case of "compiler bug breaking well-defined code". Again, all this in a kernel context.

In user code GCC will still happily remove the null check because in user code this is an actual bug in the user's code.

Re: Undefined behavior in C is a reading error

#234
post #214
post #211

Earlier quoted context omitted.

from ISO/IEC 9899:2011 "Programming Languages -- C" 3.4.3 1 undefined behavior behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements 2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner charac…

This quote is the topic of the original article and the article goes into detail about how it believes the quote should be interpreted.

...and yet the article completely ignores the "with unpredictable results" part and instead spends a lot of time discussing all the other valid consequences (which are also only mentioned as examples, at least in the common understanding of "from ... to ...").

Downthread commenters go into more detail regarding the "ignoring e.g. the possibility of signed overflow may mean to assume that it never happens" reading, so I won't elaborate on it here.

Re: Undefined behavior in C is a reading error

#235

Earlier quoted context omitted.

> 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. Since current compilers aren’t out to do anything malicious with UB, but instead simply treat it as “assume this can’t happen and proceed accordingly”, it’s not clear at all to me what you think paid compilers would do here instead: r…

I see this argument a lot, but it's silly. I don't have to care whether gcc changed the settled semantics without notice or documentation out of sincere belief that they were helping or out of a desire to beat a benchmark that doesn't matter to me, or out of habit. It's not the intent of the compiler authors that matters, but their disregard of the needs of application programmers.

What are they to do, exactly?

Optimization is a very 'generic' process and application programmers want optimization. The only sensible thing to do is to assume UB cannot occur and optimize accordingly.

What else is there?

I can already predict that whatever you suggest will very shortly end up in Halting Problem territory or will mean: No optimization. There are a lot of UBs that (if defined) would require run-time checking to define. That wouldn't inhibit optimization per se, but it would ultimately mean slower execution.

Re: Undefined behavior in C is a reading error

#236
post #155

Earlier quoted context omitted.

> that means the compiler can assume there's no overflow and generate code that always executes the loop body exactly 16 times Right. That's what I said. And just to be super-precise about the wording, the standard doesn't say "ignore the behavior 'with unpredictable results'" it says "Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results". Nitpicky, but the former wo…

And my point is that as far as the language is concerned, there is no guaranteed loop count under any circumstances. (An implementation is allowed, but not required, to define the behavior for that implementation.)

The two of you are not disagreeing except insofar as you're both using the word "guaranteed" to mean completely different things. _kst_, you're using it to mean "the programmer can rely on it". msbarnett, you're using it to mean "the compiler can rely on it".

Re: Undefined behavior in C is a reading error

#237
post #182

Earlier quoted context omitted.

One issue is that under the prevailing interpretation, the existing semantics is not reliable. You do not know when or if the compilers will take advantage of UB to completely change the semantics they are providing. That's not tenable.

That's not how it works. Taking advantage of UB doesn't change the semantics, it just exposes which behaviors were never in the semantics to begin with. Barring compiler or spec bugs, we do in principle know exactly when the compiler may take advantage of UB. That's the point of a document like the standard- it describes the semantics in a precise way. To be fair, the existing semantics are certainly complex and ofte…

The net result of your argument is the language has no semantics. I write and test with -O0 and show that f(k)=m. Then I run with -O3 and f(k)=random. Am I required to be an expert on C Standard and compiler development in order to know that, with no warning, my code has always been wrong? What about if f(k)=m under Gcc 10, but now under Gcc 10.1 that whole section of code is skipped? What you are asking programmers to do is to both master the fine points of UB (which is impractical) and look into the future to see what changes may be invisibly committed to the compiler code.

Re: Undefined behavior in C is a reading error

#238

It's easy to pick on undefined behavior in C when you focus on the more gratuitous undefined behaviors such as signed overflow or oversized shifts. I'm not certain why these are undefined behavior instead of implementation-defined, but my suspicion is that these caused traps on some processors, and traps are inherently undefined behavior. Instead, if you dislike undefined behavior, I challenge you to come up with wor…

I am not a compiler dev or a high skill coder so my opinion might not matter much but I'll still lay them out here. > The first case is the compiler hint... The compiler should refuse to compile if it comes upon such a case. Those hints are as much used by programmers as they are by the compiler. It should emit a warning and necessary checks + abort code if those hints can neither be proven nor disproven statically.…

As a rule of thumb, yes, compilers should issue warnings where undefined behavior is obviously occurring. (And there is something to be said for compiling with -Werror). However, that's not going to always work, and there are two reasons for this.

The first reason is that undefined behavior is ultimately a statement about dynamic execution of the program. The set of expressions that could potentially cause undefined behavior is essentially all of them--every signed arithmetic expression, every pointer dereference, hell, almost all function calls in C++--and figuring out whether or not they actually do cause undefined behavior is effectively impossible at the compiler level. This is why sanitizers were developed, and also why sanitizers only work as a dynamic property.

For a concrete example, consider the following code:

  extern void do_something(int * restrict a, int * restrict b, size_t n);

  void my_function(int *y, int *z, size_t x, size_t n) {
    if (x > n)
      do_something(y, z, n);
  }
This code could produce undefined behavior. Or it could not. It depends on whether or not y and z overlaps. Maybe the check of the if statement is sufficient to guarantee it. Maybe it's not. It's hard to advocate that the compiler should warn, let alone error, about this kind of code.

The second issue to be aware of is that there is a general separation of concerns between the part of the compiler that gives warnings and errors (the frontend), and the part that is actually optimizing the code. It is difficult, if not impossible, to give any kind of useful warning or error message in the guts of the optimizer; by the time code reaches that stage, it is often incredibly transformed from the original source code, to the point that its correlation with the original can be difficult to divine.

So I once came across some really weird code that broke an optimization pass I was working on. It looked roughly like this (approximate C translation of the actual IR):

  if (nullptr != nullptr) {
    int *x = nullptr;
    do {
      /* do some stuff with x */
      x++;
    } while (x != nullptr);
  }
What hideous code creates a loop that iterates a pointer through all of memory? Why, this (after reducing the test case):

  void foo() {
    std::vector> x;
    x.emplace_back();
  }
So the crazy code was generated from the compiler very heavily inlining the entire details of the STL, and the original code was a more natural iteration from a start to an end value. The compiler figured out enough to realize that the start and end values were both null pointers, but didn't quite manage to actually fully elide the original loop in that case. Warning the user about the resulting undefined behavior in this case is completely counterproductive; it's not arising from anything they did, and there isn't much they can to do to silence that warning.

Re: Undefined behavior in C is a reading error

#239

Earlier quoted context omitted.

I see this argument a lot, but it's silly. I don't have to care whether gcc changed the settled semantics without notice or documentation out of sincere belief that they were helping or out of a desire to beat a benchmark that doesn't matter to me, or out of habit. It's not the intent of the compiler authors that matters, but their disregard of the needs of application programmers.

What are they to do, exactly? Optimization is a very 'generic' process and application programmers want optimization. The only sensible thing to do is to assume UB cannot occur and optimize accordingly. What else is there? I can already predict that whatever you suggest will very shortly end up in Halting Problem territory or will mean: No optimization. There are a lot of UBs that (if defined) would require run-time…

C programmers prefer control to "optimization". And if you assume UB cannot occur, you should not generate code that makes it happen. Radical UB is not required for optimization: in fact it appears to mostly do nothing positive. There is not a single paper or study showing significant better performance for substantial C code that depends on assuming UB can't happen - just a bunch of hand waving.

Re: Undefined behavior in C is a reading error

#240
post #163

Earlier quoted context omitted.

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 n…

For 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. O…

So 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?
Post reply on HN