0

I have a problem of different results produced by a simple C++ code compiled on macOS 11 with

clang version 14.0.6 Target: x86_64-apple-darwin20.6.0

The program is the following:

#include <stdio.h> 
template<typename T, typename U, typename O> inline static void add(T* l, size_t size, U r, O* o)
{
    for (size_t i = 0; i < size ; ++i)
    {
        o[i] = (O)l[i] + (O)r;
    }
}

int main()
{
    double x[4] = {-1,-1,-1,-1};
    unsigned char y = 127;
    unsigned char z[4];
    
    add(x, 4, y, z);
    
    for (size_t i = 0; i < 4; i++)
    {
        printf("z[%zu]=%u\n",i,z[i]);
    }
}

and the to different results are (with and without -O2 optimization):

$ clang -O2 add.cpp -o add; ./add
z[0]=0
z[1]=0
z[2]=0
z[3]=0

$ clang add.cpp -o add; ./add
z[0]=126
z[1]=126
z[2]=126
z[3]=126

correct result is the one without optimization. How can I have the correct results when using optimization ?

Stéphane Mottelet
  • 2,853
  • 1
  • 9
  • 26
  • 1
    Such thing is usually indication of Undefined Behavior in a code. – Marek R May 12 '23 at 13:16
  • does it actually matter whether `add` is a template? If not you could make it a plain function – 463035818_is_not_an_ai May 12 '23 at 13:19
  • Yes, I was also thinking about it, but what would be undefined behavior here besides casting operations or eventual integer overflow ? – Stéphane Mottelet May 12 '23 at 13:20
  • 5
    Clang with `-fsanitize=undefined` even tells you what it does not like about it: https://godbolt.org/z/jKMqKdcv6 – mch May 12 '23 at 13:21
  • Here is repro https://godbolt.org/z/dE7vr7o7z – Marek R May 12 '23 at 13:21
  • 2
    @StéphaneMottelet The UB would be the cast of -1.0 to unsigned char, since it cannot be represented by the destination type. "If the value cannot fit into the destination type, the behavior is undefined (even when the destination type is unsigned, modulo arithmetic does not apply)." – Dan Mašek May 12 '23 at 13:24
  • 3
    If you do `static_cast(static_cast(l[i]))` there will be no UB, since cast to `int` is fine then casting to unsigned value is well defined since it is done modulo 2^numberOfBits. https://godbolt.org/z/re5EG9s6c – Marek R May 12 '23 at 13:26
  • Here someone made nice list of UBs: https://stackoverflow.com/questions/367633/what-are-all-the-common-undefined-behaviours-that-a-c-programmer-should-know-a/367662#367662 – Marek R May 12 '23 at 13:33
  • Which ones of the two casts, both ? – Stéphane Mottelet May 12 '23 at 13:39
  • 1
    @StéphaneMottelet The one that actually does something (i.e. `(O)l[i]` -- `r` is already an `unsigned char`, so that's basically a noop). – Dan Mašek May 12 '23 at 13:50

0 Answers0