C 標準では、ポインター修飾子 __restrict
を利用できます。これは、ポインターが参照するものと、その他すべての変数との間のデータ独立性を明示することで、より積極的なコンパイラ最適化を可能にします。次に例を示します。
int a; // global variable
void foo(int* __restrict p, int* q)
{
for (...) { ... *p += a + *q; ...}
}
foo
の解析は、*p
が *q
および a
と同じオブジェクトを指していないという事実に基づいて進めることができます。そのため、a
と *q
はループの前に一度に読み込むことができます。
現在のところ、コンパイラのフロント エンドでは、同じ配列への異なるアクセスは明確に区別されません。そのため、配列の 1 つの要素をアップデートすると、配列全体の値が変更されたと想定されます。__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);
これは、最適化で両方のポインターを異なるものに保持するために必要です (たとえば、共通部分式除去なし)。この動作は、オプション -mllvm -chess-implicit-chess_copy=false
を使用して、aiecompiler
フロントエンドで無効にできます。そのため、chess_copy
により 2 つのポインターが作成され、__restrict
によりこれらのポインターを使用したストア/ロード間の相互依存はコンパイラにより考慮されません。ローカル スコープを持つ __restrict
ポインターでは、__restrict
ポインターが有効である間のみ相互に独立していると想定されます。
__restrict
ポインターから派生したポインター (rA+1
やポインター組み込み関数など) も restrict ポインターとなり、同じ制限されたメモリ領域をポイントします。