Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restricting field access between two objects of the same type in gcc

Assume we have the following code:

typedef struct {
  int f1;
  int f2;
} t_str;

int f(t_str* p, t_str* q)
{
  p[0].f1++;
  q[0].f2++;
  p[0].f1++;
  q[0].f2++;
  p[0].f1++;
  q[0].f2++;
  p[0].f1++;
  q[0].f2++;
  p[0].f1++;
  q[0].f2++;
  p[0].f1++;
  q[0].f2++;
  p[0].f1++;
  q[0].f2++;
  p[0].f1++;
  q[0].f2++;
  p[0].f1++;
  q[0].f2++;

return 0;
}

When we compile it (I used gcc-5.1.0) with -O3 option, the compiler gets the following assembler:

f:
.LFB0:
    .cfi_startproc
    movl    8(%esp), %edx
    movl    4(%esp), %ecx
    movl    4(%edx), %eax
    addl    $9, (%ecx)
    addl    $9, %eax
    movl    %eax, 4(%edx)
    xorl    %eax, %eax
    ret
    .cfi_endproc

That means that gcc decided access to field f1 of p and access to field f2 of q never alias. I guess this comes from assume that two objects of the same type never overlaps or they are the same. But I didn't find the issue in standard.

So please, can anyone find this issue in standard, or another point why gcc restricted field access, or comment what happened?

UPD:

Well, I thought about Paragraph 7 of Section 6.5 too, but it would be more comfortable for me to have something like that in explicit form for all objects:

6.5.16.1 Simple assignment

3 If the value being stored in an object is read from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have qualified or unqualified versions of a compatible type; otherwise, the behavior is undefined.

Unfortunately this rule can not be used here.

Now look, if for code above I make the following function:

void main()
{
    char * c = malloc(12);
    memset(c, 0, 12);
    f((t_str *)(c + 4), (t_str *)c);
    printf("%d %d %d\n", ((t_str *)c)->f1, ((t_str *)c)->f2, ((t_str *)(c + 4))->f2);
}

Now I get the following during execution:

$ gcc-5.1.0 test1.c -O3 && ./a.out
0 9 0
$ gcc-5.1.0 test1.c -O0 && ./a.out
0 18 0

So how do you think is this code valid? Because I'm not shure if it statisfy Paragraph 7 of Section 6.5.

PS: interesting thing:

$ gcc-5.1.0 test1.c -O3 -fwhole-program && ./a.out
0 10 0
$ gcc-5.1.0 test1.c -O3 -flto && ./a.out
0 10 0
like image 237
alexanius Avatar asked Oct 19 '22 11:10

alexanius


1 Answers

Paragraph 7 of Section 6.5 of the latest draft (N1570) of C11 reads:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:88) — a type compatible with the effective type of the object, — a qualified version of a type compatible with the effective type of the object, — a type that is the signed or unsigned type corresponding to the effective type of the object, — a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object, — an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or — a character type.

I interpret this to mean that objects pointed to by p and q cannot overlap unless they are the same object, because the t_str objects are supposed to be accessed by proper pointers.

The Standard is not precise enough to make it clear that &p->f2 is not a valid pointer to a t_str object composed of 2 int shared between p[0] and p[1]. Yet it seems incorrect because the compiler could possibly insert padding between f1 and f2 or indeed between f2 and the end of the structure.

Incidentally, &p->f2 - &p->f1 is not a valid expression because paragraph 9 of section 6.5.6 Additive operators states this constraint: When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object;

If function f() took a pointer to char as parameter and accessed data via this pointer, gcc could not assume this data to be distinct from the int members of the structures pointed to by p and q. This somewhat counterintuitive exception is the reason why so many C library function prototypes have restrict qualifiers on many pointer arguments. (These qualifiers in function prototypes are merely a hint to the programmer, but do not really tell the compiler anything).

like image 128
chqrlie Avatar answered Oct 22 '22 01:10

chqrlie