この質問はまだ少しわかりにくいと思いますが、質問を答えられる形式に言い換えることができるかどうか見てみましょう. まず、質問の背景をもう一度述べさせてください。
C# 2.0 では、このコード:
int x = 123;
int y;
if (x * 0 == 0)
y = 345;
Console.WriteLine(y);
あなたが書いたかのように扱われた
int x = 123;
int y;
if (true)
y = 345;
Console.WriteLine(y);
これは次のように扱われます。
int x = 123;
int y;
y = 345;
Console.WriteLine(y);
これは合法的なプログラムです。
しかし、C# 3.0 では、これを防ぐために重大な変更を行いました。あなたも私も条件が常に真であることを知っているにもかかわらず、コンパイラーはもはや条件を「常に真」であるとは見なしません。コンパイラは、「if」の本体が常に実行されることを認識していないため、ローカル変数 y が使用される前に常に割り当てられていることを認識していないため、これを不正なプログラムにします。
C# 3.0 の動作が正しいのはなぜですか?
仕様に次のように記載されているため、正しいです。
したがって、与えられたコードは、条件ステートメントの結果を常に到達可能であると分類すべきではなく、したがって、ローカルy
を確実に割り当てられるものとして分類すべきではありません。
定数式に定数のみを含めることが望ましいのはなぜですか?
私たちは、C# 言語をユーザーが明確に理解し、コンパイラの作成者が正しく実装できるようにしたいと考えています。コンパイラが式の値について考えられるすべての論理的推論を行うことを要求することは、それらの目標に反します。与えられた式が定数であるかどうか、定数である場合はその値を簡単に判断できる必要があります。簡単に言えば、定数評価コードは算術の実行方法を知っている必要がありますが、算術操作に関する事実を知っている必要はありません。定数評価器は 2 * 1 の乗算方法を知っていますが、「1 は整数の乗法恒等式である」という事実を知る必要はありません。
現在、コンパイラの作成者は、賢くできる領域があると判断し、それによってより最適なコードを生成する可能性があります。コンパイラの作成者はこれを行うことが許可されていますが、コードが合法か違法かを変更する方法では許可されていません。正当なコードが与えられた場合に、コンパイラの出力を改善する最適化のみを行うことが許可されています。
このバグは C# 2.0 でどのように発生したのですか?
何が起こったのかというと、コンパイラが算術オプティマイザを実行するのが早すぎるように書かれているためです。オプティマイザは、賢いはずのビットであり、プログラムが正当であると判断された後に実行されるべきでした。プログラムが合法であると判断される前に実行されたため、結果に影響を与えていました。
これは互換性を破る可能性のある変更でした。これにより、コンパイラが仕様に準拠するようになりましたが、作業コードがエラー コードに変わる可能性もありました。変更の動機は何ですか?
LINQ 機能、特に式ツリー。次のようなことを言った場合:
(int x)=>x * 0 == 0
それを式ツリーに変換すると、次の式ツリーが生成されると思いますか?
(int x)=>true
? おそらくそうではありません!おそらく、「x をゼロで乗算し、結果をゼロと比較する」ための式ツリーが生成されることを期待していました。 式ツリーは、本文の式の論理構造を保持する必要があります。
式ツリーのコードを書いたとき、設計委員会が決定するかどうかはまだ明確ではありませんでした。
()=>2 + 3
「add two to three」の式ツリーまたは「five」の式ツリーを生成しようとしていました。私たちは後者を選択しました。定数は式ツリーが生成される前に折り畳まれますが、式ツリーが生成される前に演算がオプティマイザーを介して実行されるべきではありません。
それでは、今述べた依存関係を考えてみましょう。
- codegen の前に算術最適化を行う必要があります。
- 式ツリーの書き換えは、算術最適化の前に行う必要があります
- 式ツリーの書き換え前に一定の折り畳みが発生する必要がある
- フロー解析の前に一定の折り畳みが発生する必要があります
- 式ツリーの書き換え前にフロー分析を行う必要があります (式ツリーが初期化されていないローカルを使用しているかどうかを知る必要があるため)
これらすべての依存関係を考慮して、このすべての作業を実行する順序を見つける必要があります。C# 2.0 のコンパイラは、次の順序でそれらを実行しました。
- 定数畳み込みと算術最適化を同時に
- 流動解析
- コード生成
式ツリーの書き換えはどこに入ることができますか? どこにも!フロー分析は現在、算術オプティマイザーによって推定された事実を考慮しているため、これは明らかにバグです。次の順序で処理を行うように、コンパイラを作り直すことにしました。
- 一定の折り畳み
- 流動解析
- 式ツリーの書き換え
- 算術最適化
- コード生成
これは明らかに重大な変更を必要とします。
さて、これを行うことで、既存の壊れた動作を維持することを検討しました。
- 一定の折り畳み
- 算術最適化
- 流動解析
- 算術最適化解除
- 式ツリーの書き換え
- 再び算術最適化
- コード生成
最適化された算術式には、最適化されていない形式へのポインターが含まれます。これはバグを残すには複雑すぎると判断しました。代わりに、バグを修正し、重大な変更を行い、コンパイラ アーキテクチャをより簡単に理解できるようにする方がよいと判断しました。