as-if ルール
プログラムの観察可能な動作を変更しない、あらゆるコード変換を許可します。
目次 |
[編集] 説明
プログラムの観察可能な動作には以下が含まれます。
| (C++11まで) | |
|
(C++11以降) |
|
(C++26まで) |
|
(C++26以降) |
- 対話型デバイスに送信されるプロンプトテキストは、プログラムが入力待ちになる前に表示されます。
- ISO C pragma #pragma STDC FENV_ACCESS がサポートされており、
ONに設定されている場合、浮動小数点環境(浮動小数点例外と丸めモード)への変更は、あたかも記述どおりに実行されたかのように浮動小数点演算子と関数呼び出しによって観察されることが保証されます。ただし、以下の場合は例外です。- キャストと代入以外の任意の浮動小数点式の結果は、その式の型とは異なる浮動小数点型の範囲と精度を持つことがあります(FLT_EVAL_METHODを参照)。
- 上記にもかかわらず、任意の浮動小数点式の中間結果は、無限の範囲と精度であるかのように計算されることがあります( #pragma STDC FP_CONTRACT が
OFFでない限り)。
|
C++コンパイラは、同じ入力が与えられた場合、プログラムの観察可能な動作がその入力に対応する可能な観察可能な動作の1つである限り、プログラムに対して任意の変更を実行することが許可されます。 ただし、特定の入力が未定義動作を引き起こす場合、コンパイラはその入力によるプログラムの観察可能な動作を保証できません。たとえ観察可能な動作のいずれかの操作が、可能な未定義の操作の前に発生したとしてもです。 |
(C++26まで) |
|
プログラムは観察可能なチェックポイントを含むことがあります。 操作 C++コンパイラは、同じ入力が与えられた場合、プログラムの定義済みプレフィックスの観察可能な動作が、その定義済みプレフィックスに対応する可能な観察可能な動作の1つである限り、プログラムに対して任意の変更を実行することが許可されます。 特定の入力が未定義動作を引き起こす場合、コンパイラはその定義済みプレフィックスに属さない、その入力によるプログラムの観察可能な動作を保証できません。 |
(C++26以降) |
[編集] 注記
コンパイラは(通常)外部ライブラリのコードを分析してI/Oまたはvolatileアクセスを実行するかどうかを判断できないため、サードパーティライブラリの呼び出しも最適化の影響を受けません。ただし、標準ライブラリの呼び出しは、最適化中に他の呼び出しに置き換えられたり、削除されたり、プログラムに追加されたりすることがあります。静的リンクされたサードパーティライブラリのコードは、リンク時最適化の対象となることがあります。
未定義動作を含むプログラムは、異なる最適化設定で再コンパイルすると、観察可能な動作がしばしば変化します。例えば、符号付き整数オーバーフローのテストがそのオーバーフローの結果に依存している場合、例えばif (n + 1 < n) abort();のように、一部のコンパイラでは完全に削除されます。これは符号付きオーバーフローが未定義動作であるため、最適化ツールはそれが決して起こらないと仮定してもよく、テストは冗長であると見なせるからです。
コピー省略はas-ifルールの例外です。コンパイラは、一時オブジェクトの移動コンストラクタとコピーコンストラクタへの呼び出し、および対応するデストラクタへの呼び出しが観察可能な副作用を持つ場合でも、それらを削除することがあります。
|
new 式にはas-ifルールのもう一つの例外があります。コンパイラは、ユーザー定義の置き換えが提供され、観察可能な副作用を持つ場合でも、置き換え可能な割り当て関数への呼び出しを削除することがあります。 |
(C++14以降) |
浮動小数点例外の数と順序は、次の浮動小数点操作によって観察される状態が最適化が行われなかった場合と同様である限り、最適化によって変更されることがあります。
#pragma STDC FENV_ACCESS ON for (i = 0; i < n; ++i) x + 1; // x + 1 is dead code, but may raise FP exceptions // (unless the optimizer can prove otherwise). However, executing it n times // will raise the same exception over and over. So this can be optimized to: if (0 < n) x + 1;
[編集] 例
int& preinc(int& n) { return ++n; } int add(int n, int m) { return n + m; } // volatile input to prevent constant folding volatile int input = 7; // volatile output to make the result a visible side-effect volatile int result; int main() { int n = input; // using built-in operators would invoke undefined behavior // int m = ++n + ++n; // but using functions makes sure the code executes as-if // the functions were not overlapped int m = add(preinc(n), preinc(n)); result = m; }
出力
# full code of the main() function as produced by the GCC compiler
# x86 (Intel) platform:
movl input(%rip), %eax # eax = input
leal 3(%rax,%rax), %eax # eax = 3 + eax + eax
movl %eax, result(%rip) # result = eax
xorl %eax, %eax # eax = 0 (the return value of main())
ret
# PowerPC (IBM) platform:
lwz 9,LC..1(2)
li 3,0 # r3 = 0 (the return value of main())
lwz 11,0(9) # r11 = input;
slwi 11,11,1 # r11 = r11 << 1;
addi 0,11,3 # r0 = r11 + 3;
stw 0,4(9) # result = r0;
blr
# Sparc (Sun) platform:
sethi %hi(result), %g2
sethi %hi(input), %g1
mov 0, %o0 # o0 = 0 (the return value of main)
ld [%g1+%lo(input)], %g1 # g1 = input
add %g1, %g1, %g1 # g1 = g1 + g1
add %g1, 3, %g1 # g1 = 3 + g1
st %g1, [%g2+%lo(result)] # result = g1
jmp %o7+8
nop
# in all cases, the side effects of preinc() were eliminated, and the
# entire main() function was reduced to the equivalent of result = 2 * input + 3;[編集] 関連項目
| as-if ruleのCドキュメント
|