News for hackers. Stuff that matters.
Re:Undefined behavior is often a thing for C programmers (2011) by pornel on Thursday September 03, 2020 @10:57AM (#24364646)
Compilers use UB for optimization, because programmers de-facto demand it.

You'd hate a compiler where this has silly overhead:

    for(int i=0; i < n; i++) arr[i];
That's because 64-bit addressing modes don't overflow the same way as signed 32-bit ints. But signed overflow being UB gives compilers license to ignore the difference and generate code that makes sense.

The real problem is that C is too bare-bones and lax to be able to contain or avoid UB in day to day programs. For the aforementioned overflow gotcha, there's no way to enforce use of size_t for indexing. There's no `checked_mul(a,b)` to easily avoid signed UB. You have to make the check yourself, and if you're not careful, that check will be UB too. malloc() makes you multiply numbers often, exactly where it's dangerous. And on top of that implicit integer conversions can make unsigned arithmetic surprisingly signed.

Re:Undefined behavior is often a thing for C programmers (2011) by esrauch on Thursday September 03, 2020 @11:25AM (#24364925)
Something that I haven't seen discussed is why it wouldn't be a better solution would be for the code you listed to be a warnings or errors; get the user to write size_t then it is efficient without needing to go to UB. Another common example is a null-check after a deref; it seems like that should also just be a compiler error since the UB optimization is based on "you wrote code that the compiler can just directly tell is wrong, so it's going to pretend you didn't do that".

There's surely cases where it would be hard to suggest a "equally efficient without UB" replacement (eg when it wants to fuse two i64 into one), but it feels like the current position is too far on the axis of "provided with suspicious code, the compiler should just make that garbage run as fast as possible and call it a day"

Re:Undefined behavior is often a thing for C programmers (2011) by dooglius on Thursday September 03, 2020 @11:11AM (#24364789)
Presumably you meant to do something with arr[i] or the whole loop could be elided. I'm not sure I understand your point, what is the type of n? If it's something bigger than int that's a bug and should result in a warning.
Re:Undefined behavior is often a thing for C programmers (2011) by aw1621107 on Thursday September 03, 2020 @11:41AM (#24365095)
From what I understand, the type of n isn't really the important part here; it's the type of the indexing variable (i) that makes a difference. On platforms where sizeof(int) is not the size of a register (e.g., x86_64 with 32-bit ints), UB signed overflow allows the compiler to not have to emit code to deal with the possibility of 32-bit overflow, which probably results in "sensible" assembly.

This article [0] goes into a bit more detail.

[0]: https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759...

Re:Undefined behavior is often a thing for C programmers (2011) by dooglius on Thursday September 03, 2020 @12:05PM (#24365445)
The article you linked says that compilers can prove a lack of overflow in this example, so UB is not necessary to assume here. It also claims that the compiler cannot prove this in more complex examples (but gives no explicit examples). What I'm saying is if the compiler cannot prove a lack of overflow in this way, that should be a warning.
Re:Undefined behavior is often a thing for C programmers (2011) by aw1621107 on Thursday September 03, 2020 @01:37PM (#24366475)
> The article you linked says that compilers can prove a lack of overflow in this example, so UB is not necessary to assume here.

It's trivial to prove in that particular example because it's assumed that count is also an int (both i and count are in 32-bit registers).

> It also claims that the compiler cannot prove this in more complex examples (but gives no explicit examples).

The example doesn't even have to be that complex; you just need sizeof(n) > sizeof(i) and i to be signed (godbolt example at [0]). In that example, if the compiler can assume signed overflow is UB, then you get fairly sensible code generation. If the compiler cannot make that assumption (add the -fwrapv flag), then you get that movsxd instruction in the middle of the loop body. If you use -O2 instead of -O1, then -fwrapv prevents loop unrolling.

> What I'm saying is if the compiler cannot prove a lack of overflow in this way, that should be a warning.

I'm curious how frequently this warning might fire in real-life codebases, or when it does fire how frequently it's something the programmer can actually fix. Clang does have -Rpass-missed to emit some diagnostics when an optimization cannot be performed, but it doesn't emit anything in this particular case (though I'm not sure whether it's supposed to).

[0]: https://godbolt.org/z/abq3n9