コンパイラ

上の関数で、コンパイラが *b の読み込みをループの外に出す最適化を、そのままでは行えない理由はどれか。

void add(int *a, int *b, int n) {
    for (int i = 0; i < n; i++)
        a[i] += *b;
}
  1. A*b は整数なので、ループの外に出すと値の精度が落ちるから
  2. Ba と b が同じ場所を指している可能性があり、a[i] への書き込みで *b が変わるかもしれないから
  3. Cポインタを引数にとる関数は、コンパイラが一切最適化してはいけない決まりだから
答えと解説を見る

正解:B. a と b が同じ場所を指している可能性があり、a[i] への書き込みで *b が変わるかもしれないから

一見すると *b はループの中で変わらないので、1回だけ読めばよさそうです。しかし呼び出し側が add(x, &x[3], n) のように、bがaの要素を指すポインタを渡すと、a[3] への書き込みで *b の値が変わります。コンパイラはこの可能性(エイリアス)を否定できないので、毎回読み直すコードを作ります。C言語ではポインタに restrict を付けて、別の場所を指すことを約束すれば最適化できます。精度の問題でも、最適化が禁止されているわけでもありません。

  • A. 整数の読み込みを移動しても精度は変わりません。
  • B. 正しい。aとbが重なる可能性(エイリアス)があるためです。
  • C. ポインタを使う関数も最適化されます。
この分野を解く 分野ページへ

コンパイラのほかの問題

  1. コンパイラのレジスタ割り当てで、干渉グラフを塗り分ける(彩色する)方法を使うとき、グラフの節と辺が表すものの組み合わせとして正しいものはどれか。
  2. レジスタ割り当てで「スピル」が起こると、生成されるコードはどうなるか。
  3. コンパイラの命令スケジューリングの主な目的はどれか。
  4. 上の関数の最後の再帰呼び出しに末尾呼び出しの最適化を行うと、どのような効果があるか。
  5. コンパイラとインタプリタの違いとして正しいものはどれか。