Discussion:
Undefined compiler behaviour or a compiler bug?
Hans Petter Selasky
2021-04-29 21:06:42 UTC
Permalink
Hi,

Can someone please explain what C-compiler flag I'm missing, to make
this simple C-program terminate?

I have a function _abs() which at some point is equal to one, but the
compiler thinks it is not required to emit the test for the while() at
all, looking at the assembly code! A printf() clearly verifies that the
_abs() function has returned one.

I even added an own if() check, but apparently the compiler is lost!

cat << EOF > test.c
#include <stdio.h>
#include <stdint.h>

typedef struct {
int32_t x;
int32_t y;
} c32_t;

static c32_t
_mul(c32_t a, c32_t b)
{
return (c32_t){
a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x
};
}

static uint32_t
_abs(c32_t a)
{
return (a.x * a.x + a.y * a.y);
}

int
main()
{
c32_t a = {4, 1};
c32_t b = {};
uint32_t l1 = 0;

while (_abs(a) != 1) {
uint32_t l0 = _abs(a) & ~1;

/* figure out least significant bit */
l0 &= ~(l0 - 1);

if (l0 == 0 || l0 > l1) {
l1 = l0;
if (_abs(a) == 1)
printf("VALUE IS ONE\n");
printf("Y:0x%08x ABS:0x%08x\n", a.y, _abs(a));
}
a.y += 2;
}

size_t count = 0;

do {
count++;
b = _mul(b, a);
} while (b.x != 1 || b.y != 0);

printf("%d,%d,0x%x COUNT=%zd\n", a.x, a.y, _abs(a), count);

return (0);
}
EOF

clang11 -O2 test.c && ./a.out

--HPS
Hans Petter Selasky
2021-04-29 21:11:37 UTC
Permalink
Post by Hans Petter Selasky
Hi,
Can someone please explain what C-compiler flag I'm missing, to make
this simple C-program terminate?
I have a function _abs() which at some point is equal to one, but the
compiler thinks it is not required to emit the test for the while() at
all, looking at the assembly code! A printf() clearly verifies that the
_abs() function has returned one.
clang11 -O2 test.c && ./a.out
Looks like -fwrapv fixes the issue.

Sorry for the noise.

--HPS
Dimitry Andric
2021-04-29 22:13:52 UTC
Permalink
Post by Hans Petter Selasky
Hi,
Can someone please explain what C-compiler flag I'm missing, to make this simple C-program terminate?
I have a function _abs() which at some point is equal to one, but the compiler thinks it is not required to emit the test for the while() at all, looking at the assembly code! A printf() clearly verifies that the _abs() function has returned one.
clang11 -O2 test.c && ./a.out
Looks like -fwrapv fixes the issue.
Indeed, in this case UBSan can help you diagnose the issue. E.g.:

% clang -fsanitize=undefined test.c -o test
% ./test
Y:0x00000001 ABS:0x00000011
Y:0x00000007 ABS:0x00000041
Y:0x00000019 ABS:0x00000281
Y:0x00000027 ABS:0x00000601
Y:0x000000d9 ABS:0x0000b801
Y:0x00000327 ABS:0x0009f001
Y:0x000004d9 ABS:0x00178001
Y:0x00003b27 ABS:0x0dab0001
Y:0x000044d9 ABS:0x12840001
test.c:20:26: runtime error: signed integer overflow: 46341 * 46341 cannot be represented in type 'int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior test.c:20:26 in
Y:0x0001bb27 ABS:0xff200001
Y:0x000e44d9 ABS:0x9a400001
Y:0x0011bb27 ABS:0x64000001
Y:0x01ee44d9 ABS:0xc8000001
^C

-Dimitry
Hans Petter Selasky
2021-04-30 07:42:50 UTC
Permalink
Post by Dimitry Andric
% clang -fsanitize=undefined test.c -o test
Thank you! Didn't know about that flag.

--HPS

Mehmet Erol Sanliturk
2021-04-29 22:30:23 UTC
Permalink
Post by Hans Petter Selasky
Hi,
Can someone please explain what C-compiler flag I'm missing, to make
this simple C-program terminate?
I have a function _abs() which at some point is equal to one, but the
compiler thinks it is not required to emit the test for the while() at
all, looking at the assembly code! A printf() clearly verifies that the
_abs() function has returned one.
I even added an own if() check, but apparently the compiler is lost!
cat << EOF > test.c
#include <stdio.h>
#include <stdint.h>
typedef struct {
int32_t x;
int32_t y;
} c32_t;
static c32_t
_mul(c32_t a, c32_t b)
{
return (c32_t){
a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x
};
}
static uint32_t
_abs(c32_t a)
{
return (a.x * a.x + a.y * a.y);
}
int
main()
{
c32_t a = {4, 1};
c32_t b = {};
uint32_t l1 = 0;
while (_abs(a) != 1) {
uint32_t l0 = _abs(a) & ~1;
/* figure out least significant bit */
l0 &= ~(l0 - 1);
if (l0 == 0 || l0 > l1) {
l1 = l0;
if (_abs(a) == 1)
printf("VALUE IS ONE\n");
printf("Y:0x%08x ABS:0x%08x\n", a.y, _abs(a));
}
a.y += 2;
}
size_t count = 0;
do {
count++;
b = _mul(b, a);
} while (b.x != 1 || b.y != 0);
printf("%d,%d,0x%x COUNT=%zd\n", a.x, a.y, _abs(a), count);
return (0);
}
EOF
clang11 -O2 test.c && ./a.out
--HPS
_______________________________________________
If _abs(a) is not an Integer , please do NOT use " equality" comparison
with Integer 1 .
Instead , please use "If abs ( ( _abs(a) - 1 ) ) is 'less than or
equal to an Epsilon approximate to Zero' " , because equality of a
floating point value to an integer value is approximately "Impossible" due
to trailing bits unpredictability of floating point values .

And do not rely on "printed values" of "floating point" values , because
a value in memory and its printed values approximately are always different
due to formatting specification .


Mehmet Erol Sanliturk
Loading...