一部のハイパーモダンCコンパイラは、特定の入力が与えられたときにプログラムが未定義の動作を呼び出す場合、そのような入力は決して受信されないと推測します。その結果、そのような入力が受信されない限り無関係であるコードは排除される可能性があります。
簡単な例として、
void foo(uint32_t);
uint32_t rotateleft(uint_t value, uint32_t amount)
{
return (value << amount) | (value >> (32-amount));
}
uint32_t blah(uint32_t x, uint32_t y)
{
if (y != 0) foo(y);
return rotateleft(x,y);
}
コンパイラは、の評価が0のvalue >> (32-amount)
場合に未定義の動作を生成するためamount
、関数が0でblah
呼び出されることはないと推測y
します。foo
したがって、呼び出しは無条件にすることができます。
私が言うことができることから、この哲学は2010年頃にいつか定着したようです。そのルーツについて私が見た最も初期の証拠は2009年に遡り、未定義の動作が発生した場合に明示的に述べているC11標準に安置されていますプログラムの実行のある時点で、プログラム全体の動作が遡って定義されなくなります。
コンパイラは、IEで未定義の動作(逆因果最適化を正当化するために未定義の動作を使用することを試みるべきであるという考えたrotateleft
機能がそれを前提とするコンパイラを起こすべきではblah
非ゼロと呼ばれている必要がありますy
何が今まで引き起こすかどうか、y
にゼロ以外の値を保持する)2009年より前に真剣に提唱されましたか?最適化手法として最初に真剣に提案されたのはいつですか。
[補遺]
一部のコンパイラには、20世紀でさえ、ループとその中で計算された値に関する特定の種類の推論を有効にするオプションが含まれています。たとえば、
int i; int total=0;
for (i=n; i>=0; i--)
{
doSomething();
total += i*1000;
}
コンパイラーは、オプションの推論がない場合でも、次のように書き直します。
int i; int total=0; int x1000;
for (i=n, x1000=n*1000; i>0; i--, x1000-=1000)
{
doSomething();
total += x1000;
}
そのコードの動作は元のコードと正確に一致するため、コンパイラがそのint
値を常にmod-65536の2の補数形式でラップするように指定したとしても。追加の推論のオプションは、コンパイラがいるのでそれを認識させるだろうi
とx1000
同時にゼロを横断する必要があり、元の変数を排除することができます。
int total=0; int x1000;
for (x1000=n*1000; x1000 > 0; x1000-=1000)
{
doSomething();
total += x1000;
}
int
値がmod 65536でラップされたシステムでは、最初の2つのループのいずれかn
を33と実行しようとすると、doSomething()
33回呼び出されます。対照的にdoSomething()
、最初の呼び出しがdoSomething()
算術オーバーフローの前にあったとしても、最後のループはまったく呼び出されません。このような動作は「非因果的」と見なされる可能性がありますが、影響はかなりよく抑制されており、動作が明らかに無害である場合が多くあります(入力が与えられたときに関数が何らかの値を生成する必要がある場合でも、値入力が無効である場合は任意である可能性があり、無効な値が指定された場合にループがより速く終了しますn
実際に有益です)。さらに、コンパイラーのドキュメントは、UBに関係するプログラムであっても、プログラムの動作を変更するという事実に対して謝罪する傾向がありました。
私は、コンパイラーの作者の態度が、プラットフォームによって標準で義務付けられていない場合でもいくつかの使用可能な動作制約を実際に文書化する必要があるという考えから、によって義務付けられていない動作に依存するすべての構文が標準は、ほとんどの既存のコンパイラで同じ要件を満たす厳密に準拠したコードと同じかそれ以上に機能する場合でも、不正にブランド化する必要があります(多くの場合、厳密に準拠したコードでは不可能な最適化を許可します)。
shape->Is2D()
派生していないオブジェクトで呼び出されることはありません。からShape2D
。ある巨大な重要な未定義の動作をしている場合にのみ適用されるであろうコードから最適化の違い、すでに起こった場合のみで関連すると思われるコード対はどこ...
Shape2D::Is2D
は、プログラムに値するよりも実際には優れています。
int prod(int x, int y) {return x*y;}
十分です。ただし、厳密に準拠した方法で「核を起動しない」に準拠するには、読みにくいコードが必要になり、ほとんどの場合確かに、多くのプラットフォームでは実行速度がかなり遅くなります