Comment by benj111

3 years ago

>No, if you never do the calculation it's not going to be UB.

  int8_t x = some_input();
  if (x > 10) return bad_value;
  else x *= 10

In this simple case yes. But what if you don't know what you're going to multiply by? What if you can't say that X is a bad value?

If you have: Long long x = ?; Long long y = ?;

  If (????); x *= y;

I don't know the answer to this. I've looked online and the answers invoke UB. The best I can think of is a LUT of safe / unsafe combinations, but that isn't faster, and when you're at that point you may as well give up on the MUL hardware in your cpu, I'm not even sure how to safely calculate the LUT, I suppose you could iterate with additions subbing the current total from int_max and checking if that's bigger than the number you're about to add. But that's frankly stupid. And again you are basically checking if something is going to be UB which can't happen the compiler is therefore free to remove the check. Or do you roll your own data type with unsigned ints and a sign bit? But but then what's the point of having signed ints, and what happens to Cs speed. Or is there some bit twiddling you can do?

>No - that's the problem. The compiler doesn't know that the code is UB

Ok I should properly have said, code it can't prove isn't UB.

If it can't say X + y isn't an overflow it shouldn't just assume it can't.

If y is 1 and X is probably 9 it wouldn't be reasonable to assume the sum is 10.

>C) Assume that the programmer has inserted these checks where they are needed, and omitted them where they are not. You get performance, but in exchange for that you are responsible for avoiding UB

You get the performance by avoiding option B. I'm not even sure the programmer is responsible for avoiding UB? UB just doesn't give guarantees about what will happen. You should still be able to invoke it, and I would contend, expect the compiler to do something reasonable.

It is tedious but possible to check for overflow before multiplying signed integers.

    long long x = (...);
    long long y = (...);
    long long z;
    
    
    // Portable
    bool ok = x == 0 || y == 0;
    if (!ok) {
        long long a = x > 0 ? x : -x;
        long long b = y < 0 ? y : -y;
        if ((x > 0) == (y > 0))
            ok = -LONG_LONG_MAX / a <= b;
        else
            ok = LONG_LONG_MIN / a <= b;
    }
    if (ok)
        z = x * y;
    
    
    // Compiler-specific
    bool ok = !__builtin_smulll_overflow(x, y, &z);

https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins...

  • Thanks for that.

    Slightly worrying that I didn't come across this or a variation in my searches