デッドコード削除

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動先: 案内検索

デッドコード削除(デッドコードさくじょ、: dead code elimination)は、コンパイラ最適化技法の一種で、プログラムの結果に影響しないコードを削除することでプログラムのサイズを削減することを指す。デッドコード (到達不能コード) としては、全く実行されないコード、参照されない変数(そのプログラムでは不要な変数)を更新するだけのコードなどが含まれる。

[編集]

次のC言語のコード例を見てみよう。

int foo() {
  int a = 24;
  int b = 25; /* 参照されない変数への代入 */
  int c;
  c = a << 2;
  return c;
  b = 24; /* 実行されないコード */
}

変数bにはreturn文の後に値が代入されているが、これは実行されない。すなわち、コードの実行は逐次的であり、return文が何らかの条件で囲まれているわけでもないので、return文の後のコードは実行不可能である。ただし、例えばreturn文の後にラベルがあって、どこかから分岐して飛んでくる場合は、その限りではない。

さらに、この代入文を削除すると、bという変数が初期値を代入したとき以外では全く使われないことがわかる。オプティマイザがどこまで積極的に最適化するかにもよるが、変数bは生成されるコードから完全に削除される可能性もある。

関数内で何らかの計算が行われたとしても、その結果がこの関数のスコープ外からアクセス可能な位置に格納されなければ、その計算は無意味である。さらにこの関数は常に同じ値 (96) を返すので、単にその値を返す関数として単純化される可能性もある。

最近[いつ?]コンパイラはデッドコード削除を行うか否かをオプションで指定でき、場合によってはそのレベルを指定できるものもある。低いレベルでは、実行されないコードのみを削除するだろう。もう少し高いレベルでは、使われない変数のための領域を確保しないことになる。さらに高いレベルでは、意味のないコードや関数を特定して、それらを削除する。

デッドコード削除の典型的な利用例として、プリプロセッサによるオプションコードの代替としての使い方がある。次のコードを見てみよう。

/* デバッグ時には 1 とする */
#define ENABLE_DEBUG_PRINT 0

int main() {
  int a = 5;
  int b = 6;
  int c;
  c = a * (b >> 1);
  if (ENABLE_DEBUG_PRINT) {
    printf("%d\n", c);
  }
  return c;
}

if文の条件式が0ということは、常に偽であることを意味し、このif文の中のコードは実行されない。そのため、コンパイラの最適化により、デッドコードとして除去される可能性が高い。デバッグ用のコードを残しておく際に使われることのあるテクニックである。

しかし、以下のようなコードでは、たとえif文の条件式が常に偽であっても、if文内部のコードは到達不能ではなく、実行されることがあるため、最適化による除去の対象とはならない。したがって、コンパイラは単純にif文の条件式だけをもとに一律デッドコードか否かを判断することはできず、if文内部のパース(解析)がやはり必要である。

  if (0) {
label_inner:
    puts("Inner");
    goto label_exit;
  }
  goto label_inner;
label_exit:
  puts("Exit");

もしリリース時に静的にコードを除去する目的があるのであれば、最適化によるデッドコードの除去を期待するよりも、プリプロセッサを用いるほうが確実であり、ひいては総合的なコンパイル時間およびビルド時間を短縮することにもつながる。また、到達不能コードのあるプログラムにはバグが潜んでいる可能性が高く、通例コンパイラが警告を発する。警告を無視することは、バグを無視することにつながる危険性もある。

関連項目[編集]