Consider the following code:
void doesnt_modify(const int *);
int foo(int *n) {
*n = 42;
doesnt_modify(n);
return *n;
}
where
If not, is there a (portable, if possible) way to tell the compiler doesnt_modify doesn’t modify what its argument points to?
No such way.
Compiler optimizers have difficulty optimizing when pointer and reference function parameters are involved. Because the implementation of that function can cast away constness compilers assume that T const*
is as bad as T*
.
Hence, in your example, after the call doesnt_modify(n)
it must reload *n
from memory.
See 2013 Keynote: Chandler Carruth: Optimizing the Emergent Structures of C++. It applies to C as well.
Adding restrict
keyword here does not change the above.
Simultaneous use of a restrict
qualifier on a pointer-type parameter and a const
qualifier on its target type would invite a compiler to assume that no region of storage which is accessed during the lifetime of the pointer object via the pointer contained therein or any pointer derived from it, will be modified via any means during that pointer's lifetime. It generally says nothing whatsoever about regions of storage which are not accessed using the pointer in question.
The only situations where const restrict
would have implications for an entire object would be those where pointer is declared using array syntax with a static
bound. In that situation, behavior would only be defined in cases where the entire array object could be read (without invoking UB). Since reading any part of the array object which changes during function execution would invoke UB, code would be allowed to assume that no portion of the array can be changed in any fashion whatsoever.
Unfortunately, while a compiler that knew that a function's actual definition starts with:
void foo(int const thing[restrict static 1]);
would be entitled to assume that no part of *thing
would be changed during the function's execution, even if the object might be one the function could otherwise access via pointer not derived from thing
, the fact that a function's prototype includes such qualifiers would not compel its definition to do likewise.
Generally, restrict
means that the pointer is not aliased (i.e. only it or a pointer derived from it can be used to access the pointed-to object).
With const
, this means that the pointed-to object cannot be modified by well-formed code.
There is, however, nothing to stop the programmer breaking the rules using an explicit type conversion to remove the const
ness. Then the compiler (having been beaten into submission by the programmer) will permit an attempt to modify the pointed-to object without any complaint. This, strictly speaking, results in undefined behaviour so any result imaginable is then permitted including - possibly - modifying the pointed-to object.
Version 1 seems clearly specified by the formal definition of restrict
(C11 6.7.3.1). For the following code:
const int *restrict P = n;
doesnt_modify(P);
return *P;
the symbols used in 6.7.3.1 are:
P
*P
which is const int
P
*P
is what we're interested in6.7.3.1/4 (partial):
During each execution of
B
, letL
be any lvalue that has&L
based onP
. IfL
is used to access the value of the objectX
that it designates, andX
is also modified (by any means), then the following requirements apply:T
shall not be const-qualified [...] If these requirements are not met, then the behavior is undefined.
Note that T
is const-qualified. Therefore, if X
is modified in any way during this block (which includes during the call to a function in that block), the behaviour is undefined.
Therefore the compiler can optimize as if doesnt_modify
did not modify X
.
Version 2 is a bit more difficult for the compiler. 6.7.6.3/15 says that top-level qualifiers are not considered in prototype compatibility -- although they aren't ignored completely.
So although the prototype says:
void doesnt_modify2(const int *restrict p);
it could still be that the body of the function is declared as void doesnt_modify2(const int *p)
and therefore might modify *p
.
My conclusion is that if and only if the compiler can see the definition for doesnt_modify2
and confirm that p
is declared restrict
in the definition's parameter list then it would be able to perform the optimization.