Consider the following function:
int bar(const int* __restrict x, void g())
{
int result = *x;
g();
result += *x;
return result;
}
Do we need to read twice from x
because of the call to g()
? Or is the __restrict
ion enough to guarantee the invocation of g()
does not access/does not alter the value at address x
?
At this link we see the most popular compilers have to say about this (GodBolt; language standard C99, platform AMD64):
- clang 7.0: Restriction respected.
- GCC 8.3: No restriction.
- MSVC 19.16: No restriction.
Is clang rightly optimizing the second read away, or isn't it? I'm asking both for C and C++ here, as the behavior is the same (thanks @PSkocik).
Related information and some notes:
- Readers unfamiliar with
__restrict
(or __restrict__
) may want to have a look at: What does the restrict keyword mean in C++?
- GCC's documentation page on restricted pointers in C++.
- I've opened a bug against GCC on this point.
- The fact that
x
is marked const
is not significant here - we get the same behavior if we drop the const
and the question stands as is.
I think this is effectively a C question, since C is effectively the language that has restrict
, with a formal spec attached to it.
The part of the C standard that governs the use of restrict
is 6.7.3.1:
1 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.
2 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).
3 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.137) Note
that ''based'' is defined only for expressions with pointer types.
4 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.
5 Here an execution of B means that portion of the execution of the
program that would correspond to the lifetime of an object with scalar
type and automatic storage duration associated with B.
The way I read it, the execution of g()
falls under the execution of the bar
's block, so g()
is disallowed from modifying *x
and clang
is right to optimize out the second load (IOW, if *x
refers to a non-const global, g()
must not modify that global).