Earlier quoted context omitted.
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…
Actually, there's a even worse version: struct foo { ...; bar_t bar[NBAR]; }; struct foo* p = ...; bar_t* q = &p->bar[0]; // add rq, rp, #foo_bar_offs // other declarations if(!p) return NOPE; // optimized out // use p and q Not even any dereferencing, just pointer arithmetic.
Undefined behavior in C is a reading error
441–450 of 503 posts
Re: Undefined behavior in C is a reading error
#442Earlier quoted context omitted.
> To do UB "optimizations", the compiler first needs to figure out that there is an UB it can "optimize" anyway. That's not how compwillrs work. In fact in the general case it is impossible to figure out at compile time that "there is an UB". The compiler instead assumes as an axiom that no UB can ever happen and uses the axioms to prove properties of the code. These days if you want to catch UB, compile with -fsanit…
> These days if you want to catch UB, compile with -fsanitize=undefined-behaviour. The program wll then trap if UB is actually detected at runtime. So, let me get this straight, someone wants to make sure pointer p is not null (in the wrong way), and codes something like the examples in posts above like if (!p) ... and if that doesn't trigger calls use(*p), but compiler decides p can never be null because that would…
if (!p) {
use(*p);
}
(given no previous knowledge about p) no compiler will remove the "if (!p)" part.What people are complaining about is the opposite case:
use(*p);
/* The compiler reasons that if p == NULL, the program would have crashed by now,
so if we got here, p != NULL must hold. */
if (!p) { // the compiler can remove this branch
report_error();
}Re: Undefined behavior in C is a reading error
#443Earlier quoted context omitted.
> 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. I am asking programmers to understand and avoid UB, but I am not asking them to look into the future. Future compilers will still implement the same semantics- that's, again, the point of having a spec! I don't disagree tha…
What indicates the the current semantics won't change next week? After all, it is completely up to the compiler, no?
The semantics for non-UB code are completely fixed across optimization levels and compiler versions. Only code that invokes UB can break on these changes, and only because this code never had any specified semantics to begin with.
Re: Undefined behavior in C is a reading error
#444Earlier quoted context omitted.
The world doesn't end, but in the "int" case you get nice vector code and in the "unsigned int" case you get much less nice scalar code: https://gcc.godbolt.org/z/cje6naYP4
Clang uses vectors for both. https://gcc.godbolt.org/z/G997Ge9KT
Since you've posted a lot along the lines of "these optimizations don't even make a difference", you might want to see if Clang's safer-looking version is as fast as GCC's.
Re: Undefined behavior in C is a reading error
#445Earlier quoted context omitted.
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.
I don't mean to be snide, but there's no paper on it because it's pretty much something you can learn in compiler 101. Without being able to assume that UB doesn't happen, useful optimizations become impossible very quickly.
Re: Undefined behavior in C is a reading error
#446Earlier quoted context omitted.
Personally, I think that arguing that those who define and implement a standard don’t understand one of the most fundamental aspects of said standard is going to be an uphill battle. You could argue that they’ve lost their way, and the article flirts with this, but the path forward is the hard part, and IMHO rings a bit hollow: it’s asserted that these rules aren’t needed for performance, but no evidence is given, an…
Wang et al tried this an experiment and found no serious wins.
> To understand how disabling these optimizations may impact performance, we ran SPECint 2006 with GCC and Clang, respectively, and measured the slowdown when compiling the programs with all the three -fno-* [-fno-strict-overflow, -fno-delete-null-pointer-checks, and -fno-strict-aliasing] options shown in Figure 9. The experiments were conducted on a 64-bit Ubuntu Linux machine with an Intel Core i7-980 3.3 GHz CPU and 24 GB of memory. We noticed slowdown for 2 out of the 12 programs, as detailed next.
> 456.hmmer slows down 7.2% with GCC and 9.0% with Clang. The first reason is that the code uses an int array index, which is 32 bits on x86-64, as shown below.
int k;
int *ic, *is;
...
for (k = 1; k
> As allowed by the C standard, the compiler assumes that the signed addition k++ cannot overflow, and rewrites the loop us- ing a 64-bit loop variable. Without the optimization, however, the compiler has to keep k as 32 bits and generate extra in- structions to sign-extend the index k to 64 bits for array access. This is also observed by LLVM developers [14].> Surprisingly, by running OProfile we found that the most time-consuming instruction was not the sign extension but loading the array base address is[] from the stack in each iteration. We suspect that the reason is that the generated code consumes one more register for loop variables (i.e., both 32 and 64 bits) due to sign extension, and thus spills is[] on the stack.
> If we change the type of k to size_t, then we no longer observe any slowdown with the workaround options. 462.libquantum slows down 6.3% with GCC and 11.8% with Clang. The core loop is shown below.
quantum_reg *reg;
...
// reg->size: int
// reg->node[i].state: unsigned long long for (i = 0; i size; i++)
reg->node[i].state = ...;
> With strict aliasing, the compiler is able to conclude that updating reg->node[i].state does not change reg->size, since they have different types, and thus moves the load of reg->size out of the loop. Without the optimization, however, the compiler has to generate code that reloads reg->size in each iteration.
If we add a variable to hold reg->size before entering the loop, then we no longer observe any slowdown with the workaround options.> While we observed only moderate performance degradation on two SPECint programs with these workaround options, some previous reports suggest that using them would lead to a nearly 50% drop [6], and that re-enabling strict aliasing would bring a noticeable speed-up [24].
[0]: https://pdos.csail.mit.edu/papers/ub:apsys12.pdf
[6]: https://lists.gnu.org/archive/html/autoconf-patches/2006-12/...
[24]: (dead link, doesn't appear to be available on the Wayback Machine) https://www.linaro.org/blog/compiler-flags-used-to-speed-up-...
Re: Undefined behavior in C is a reading error
#447Earlier quoted context omitted.
> And yet the world doesn't end and the sun will still rise tomorrow... No, you just get much slower, non-vectorized code because the compiler is forced to forgo an optimization if you use unsigned int as the loop bound (EDIT: tom_mellior's reply illustrates this extremely well: https://gcc.godbolt.org/z/cje6naYP4 ) Which is precisely the point: forcing a bunch of existing code with int loop bounds, which currently e…
And switch the i to a size_t and get vector code without the possibility of writing to random memory because your int overflows and GCC wants to pretend it cannot. This is a poorly written loop. C design model is that if it is not critical, we don't care, and if it is, the programmer should fix it so optimization can work. https://gcc.godbolt.org/z/ErMP4cn6s
Re: Undefined behavior in C is a reading error
#448Earlier quoted context omitted.
> Well-defined integer overflow would not preclude loop unrolling in this case. One simple alternative would be for the compiler to emit a guard, skipping unrolling in the case that (offset+16) overflows. To what end? for(i = offset; i like most loops in most programs isn't designed to overflow. The program isn't any more correct for emitting two translations of the loop, one unrolled and one which is purely a bugged…
It's not designed to overflow, and automobiles are not designed to crash, but airbags are good engineering anyways
That’s not an airbag, that’s a crumple zone that rams the steering column through your sternum to avoid the engine crushing your legs. You’re still dead, we’ve just uselessly shuffled the details around.
Re: Undefined behavior in C is a reading error
#449Earlier quoted context omitted.
Clang uses vectors for both. https://gcc.godbolt.org/z/G997Ge9KT
Yes, with lots of extra ceremony around it. (More than is needed, since it doesn't seem to realize that it will always process exactly 16 loop iterations.) Since you've posted a lot along the lines of "these optimizations don't even make a difference", you might want to see if Clang's safer-looking version is as fast as GCC's.
Re: Undefined behavior in C is a reading error
#450Earlier quoted context omitted.
And switch the i to a size_t and get vector code without the possibility of writing to random memory because your int overflows and GCC wants to pretend it cannot. This is a poorly written loop. C design model is that if it is not critical, we don't care, and if it is, the programmer should fix it so optimization can work. https://gcc.godbolt.org/z/ErMP4cn6s
You changed writes to indices offset..offset+15 to writes to indices 0..15.