Consider the following simple example:
struct __attribute__ ((__packed__)) {
int code[1];
int place_holder[100];
} s;
void test(int n)
{
int i;
for (i = 0; i < n; i++) {
s.code[i] = 1;
}
}
The for-loop is writing to the field code
, which is of size 1. The next field after code
is place_holder
.
I would expect that in case of n > 1
, the write to code
array would overflow and 1
would be written to place_holder
.
However, when compiling with -O2
(on gcc 4.9.4 but probably on other versions as well) something interesting happens.
The compiler identifies that the code might overflow array code
, and limits loop unrolling to 1 iteration.
It's easy to see that when compiling with -fdump-tree-all
and looking at the last tree pass ("t.optimized"):
;; Function test (test, funcdef_no=0, decl_uid=1366, symbol_order=1)
Removing basic block 5
test (int n)
{
<bb 2>:
# DEBUG i => 0
# DEBUG i => 0
if (n_4(D) > 0)
goto <bb 3>;
else
goto <bb 4>;
<bb 3>:
s.code[0] = 1;
# DEBUG i => 1
# DEBUG i => 1
<bb 4>:
return;
}
So in this case the compiler completely unrolled the loop to a single iteration.
My questions are:
- From C specification viewpoint, is overflowing (deliberately) from one struct member to the next is illegal or undefined behavior?
Let's assume I'm aware of the struct layout in memory and know what I'm doing when deliberately overflowing thecode
array. - Is there a way to prevent gcc from unrolling the loop in such case? I know I can completely prevent loop unrolling, however I'm still interested in loop unrolling on other cases. I also suspect that the analysis the compiler is doing might affect passes other than loop unrolling.
gcc is assuming I'm not going to overflow when accessing my array, so what I'm really looking for is way to tell the compiler not to take this assumption (by providing some compiler option).
I'm aware it's a bad practice to write such code that overflows from one field to another, and I'm not intending to write such code.
I'm also aware of the practice to put an array (possibly zero sized) as the last struct field to allow it to overflow, this is well supported by compilers, while in this case the array code
is not the last field.
So this is not a question of "how to fix the code", but rather a question of understanding the compiler assumptions and affecting them.
These questions came up when I observed existing code that was already written in such way, and debugged it to find out why it's not behaving as the original developer expected it to behave.
The risk is that there are other places in the code where such problem exists. Static analysis tools can help to find out, but I would also like to know if there's a way to make the compiler tolerate such code and still generate the result we would expect.
Update
I got clear answer to question (1) above, but not for question (2).
- Can gcc allow this as an extension, by some compile options?
- Is there a way to at least get a warning when gcc identifies it? (and it clearly identifies it, by optimizing things out).
That's important in order to identify such cases in a large existing code base.