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.
char [23][8]
array. Which on access will bechar (*)[8]
, with the same address ofs
ors.b
. But taking the address again&s.b
will have the type pointer tochar (*)[8]
while&s
would be a pointer tostruct S
. Nows.b
on access should/would be subject to6.3.2.1(p3)
resulting in the bounds ofchar (*)[8]
being limited tob[0]
. But that's as far as I get:)
– Weissberg(char *)
casts and think about whether the use ofstrlen()
is on a compatible type. That's why I don't think there would be a compiler flag to deal with the specific optimization. But my certainty there is why this has been posted as a comment and not an answer. – Weissberg/ sizeof(BUF)
is skipped, what is your output? 16 16? – Campney2 2
under various options. – Campney&s.b
being a pointer to the 23x8 array subobject) – Frisian&s.b
, still result is zero – Frayas.b
is limited tob[0]
it is limited to 8 characters, and hence two options: (1) out-of-bound access in case there are 8 non-null characters, which is UB, (2) there is a null character, in which the len is less than 8, hence dividing by 8 gives zero. So putting together (1)+(2) compiler can use the UB to give same result to both cases – Fraya-fno-optimize-strlen
didn't work here. – Aiaian = strlen((char *)&s.b) / sizeof(BUF);
with setting n to 0 – Frisianis there a gcc flag to turn off this specific optimization?
-fno-builtin-strlen
seems to fix it. – Subsume-fno-optimize-strlen
didn't work. But other flags did. See my aswer. – Keli