C 语言标准可提供特定指针限定符 __restrict
,此限定符旨在通过显式声明任意指针引用与所有其它变量之间的数据独立性,从而开展更激进的编译器最优化。例如:
int a; // global variable
void foo(int* __restrict p, int* q)
{
for (...) { ... *p += a + *q; ...}
}
现在 foo
分析可在已知 *p
所表示的对象与 *q
和 a
并不相同的前提下继续操作。因此,a
和 *q
现在可在循环之前加载一次。
当前,编译器前端并不会消除对相同阵列进行不同访问之间所存在的任何歧义。因此更新阵列中的某个元素时,假定整个阵列均已更改该值。__restrict
限定符可用于覆盖此保守假定。如果要获取对同一阵列的多个独立指针,那么此限定符很有用。
void foo(int A[])
{
int* __restrict rA = A; // force independent access
for (int i = ...)
rA[i] = ... A[i];
}
在此示例中,__restrict
限定符允许对循环进行软件流水打拍,在上一个阵列元素仍必须完成存储的同时,下一个阵列的元素可能已加载。为了尽可能扩大 __restrict
限定符的影响,默认情况下,编译器前端会在初始化器中插入 chess_copy
运算,编写方式如下:
int* __restrict rA = chess_copy(A);
为了使优化器中的两个指针之间保持有所差别(例如,无公共子表达式消除),这是必需的。对于 AI 引擎编译器前端,可通过 -mllvm -chess-implicit-chess_copy=false
选项来禁用此行为。因此,chess_copy
会创建两个指针,而 __restrict
则会告知编译器不考量通过这些指针的存储/加载之间的任何相互依赖关系。对于具有局部作用域的 __restrict
指针,仅在 __restrict
指针生存期内,相互独立性的假设才有效。
衍生自 __restrict
指针的指针(例如,rA+1
或者穿越指针内部调用)会保留此限制,即这些指针被视为指向相同的受限存储区域。