I checked this and it reproduced with -O1
on gcc 8.3, so I just opened list of gcc optimization flags here and started experimenting with them one by one. It turned out that disabling only sparse conditional constant propagation with -fno-tree-ccp
made the problem disappear (oh luck, I planned to test couples of flags if testing one by one gives no result).
Then I switched to -O2
but did not erase -fno-tree-ccp
flag. It reproduced again. I said "OK" and just started testing additional -O2
flags. It again appeared that disabling single Value Range Propagation additionaly leads to intended 2 2
output.
I then erased that first -fno-tree-ccp
flag, but it started reproducing again. So for -O2
you can specify -O2 -fno-tree-ccp -fno-tree-vrp
to make yor program work as expected.
I did not erase these flags, but switched to -O3
then. Problem did not reproduced.
So both of these two optimization techniques in gcc 8.3 lead to such a strange behaviour (maybe they use something common internally):
- Sparse conditional constant propagation on trees
- Value Range Propagation on trees
I'm not pro in all that stuff to explain what and why is happening there, maybe someone else could explain. But for sure you can specify -fno-tree-ccp -fno-tree-vrp
flags to disable these optimizaton techniques for your code to work as expected.
“The harder I work, the luckier I get.”
– Samuel Goldwyn
Edit
As @KamilCuk noted in question comments, -fno-builtin-strlen
leads to inteded behaviour too, so most probably there is a compiler bug in combination of built-in strlen
and another optimization, that is intended to cut off dead code, statically determine possible expression values and propagate constants through a program. I thought compiler most probably mistakenly considered something, that determines string length in its strlen
implementation (maybe in combination with integer division and/or two-dimensional arrays) as dead code and cut it off or calculated it as 0 at compile time. So I decided to play a little bit with the code to check the theories and eliminate other possible "participants" of the bug. I came to this minimal example of the behaviour, which confirmed my thoughts:
int main()
{
// note that "7" - inner arrays size, you can put any other number here
char b[23][7]; // local variable, no structs, no typedefs
memcpy(&b[0][0], "12345678123456781234", 21);
printf("%d\n", strlen(&b[0][0]) / 8); // greater than that "7" !!!
printf("%d\n", strlen(&b[0][0]) / 7);
printf("%d\n", strlen(&b[0][0]) / 6); // less than that "7" !!!
printf("%d\n", strlen(&b[0][0])); // without division
}
0
0
3
20
I think we can consider this a bug in gcc.
I think -fno-builtin-strlen
is better solution for the problem, as it works for all optimization levels alone and built-in strlen
seems to be less powerful optimization technique, especially if your program doesn't use strlen()
a lot. Still -fno-tree-ccp -fno-tree-vrp
is also an option.