Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'restrict' keyword - Why is it allowed to assign from a outer restricted variable to an inner restricted variable?

First some references. The C99 Standard says this about restrict in section 6.7.3:

An object that is accessed through a restrict-qualified pointer has a special association with that pointer. This association, defined in 6.7.3.1 below, requires that all accesses to that object use, directly or indirectly, the value of that particular pointer.117) The intended use of the restrict qualifier (like the register storage class) is to promote optimization, and deleting all instances of the qualifier from all preprocessing translation units composing a conforming program does not change its meaning (i.e., observable behavior).

And then (§6.7.3.1 "Formal definition of restrict"):

Let D be a declaration of an ordinary identifier that provides a means of designating an object P as a restrict-qualified pointer to type T.

If D appears inside a block and does not have storage class extern, let B denote the block. If D appears in the list of parameter declarations of a function definition, let B denote the associated block. Otherwise, let B denote the block of main (or the block of whatever function is called at program startup in a freestanding environment).

In what follows, a pointer expression E is said to be based on object P if (at some sequence point in the execution of B prior to the evaluation of E) modifying P to point to a copy of the array object into which it formerly pointed would change the value of E.119) Note that ''based'' is defined only for expressions with pointer types.

During each execution of B, let L be any lvalue that has &L based on P. If L is used to access the value of the object X that it designates, and X is also modified (by any means), then the following requirements apply: T shall not be const-qualified. Every other lvalue used to access the value of X shall also have its address based on P. Every access that modifies X shall be considered also to modify P, for the purposes of this subclause. If P is assigned the value of a pointer expression E that is based on another restricted pointer object P2, associated with block B2, then either the execution of B2 shall begin before the execution of B, or the execution of B2 shall end prior to the assignment. If these requirements are not met, then the behavior is undefined.

As some have pointed out, this illustrates the rules (Example 4 from the standard):

{
    int * restrict p1;
    int * restrict q1;

    p1 = q1; //  undefined behavior

    {
        int * restrict p2 = p1; //  valid
        int * restrict q2 = q1; //  valid
        p1 = q2; //  undefined behavior
        p2 = q2; //  undefined behavior
    }
}

Now, my first question is this: why is it okay to assign from an outer restricted pointer to an inner one?

My understanding is that nothing forbids this, which has clear aliasing:

int * restricted x = /* ... */ ;

{
    int * restricted y = x;
    *x = 3;
    printf("%d\n", *y); // 3
    *y = 4;
    printf("%d\n", *x); // 4
}

Of course, the set of aliases is restricted to the two pointers.

Hence my second question: what is the difference assigning from outer to inner (allowed), but not from inner to outer (forbidden, e.g. p1 = q1; in the first example above)?

like image 363
Norswap Avatar asked Mar 26 '15 10:03

Norswap


People also ask

What does restrict keyword do in C?

In the C programming language, restrict is a keyword that can be used in pointer declarations. By adding this type qualifier, a programmer hints to the compiler that for the lifetime of the pointer, no other pointer will be used to access the object to which it points.


1 Answers

I think the rules are designed to satisfy two objectives:

  1. Allow creation of a temporary pointer similar to what would naturally be created when an argument is passed to a function call, without requiring that code using the pointer be moved into a physically separate function.

  2. Ensure that a graph showing pointer derivation will be free of cycles (meaning that if pointer x is derived from y or anything that is directly or indirectly derived from y, y cannot be derived from x nor anything which is directly or indirectly derived from x). While the rules may be tighter than would be absolutely necessary to achieve #2, nearly all of the useful cases that would satisfy this second requirement also satisfy the rules as written, and compilers would have difficulty getting much benefit from restrict in the cases that don't.

Unfortunately, the authors of the rules don't seem to have made any particular effort to consider all possible corner cases and ensure that the rules in the Standard could be sensibly applied to all of them. There are two usage cases where the semantics of restrict are clear and sensible:

  1. A function receives a restrict-qualified pointer, in which case the qualifier should affect the pointer value passed to the function, independent of anything else that might be stored in the argument.

  2. The definition of an automatic object with a restrict qualifier includes an initialization expression, in which case the qualifier should affect the pointer value used for the initialization, independent of anything else that might be stored in the object.

The semantics of having restrict "guard" anything other than an address stored into a pointer via one of the two means above are rather murky at best, even if one tries to add scope rules like those in the Standard. Adding restrict qualifiers in other cases would be more likely to break something as to have a useful effect, even though the most likely outcome would be that the qualifiers would have no effect whatsoever.

like image 62
supercat Avatar answered Sep 22 '22 07:09

supercat