web-dev-qa-db-ja.com

コンパイラはどうしてそんなに信頼できるのですか?

コンパイラーは、その正しさが与えられているかのように毎日使用していますが、コンパイラーもプログラムであり、バグが含まれている可能性があります。私は常にこの間違いのない堅牢性について疑問に思っていました。コンパイラ自体のバグに遭遇したことがありますか?それは何で、どのようにコンパイラ自体に問題があることに気づきましたか?

...そして、どのようにdoコンパイラーを信頼性の高いものにしますか?

67
EpsilonVector

何千人、または何百万人もの開発者による使用を通じて、長期にわたって徹底的にテストされます。

また、解決すべき問題は(非常に詳細な技術仕様によって)明確に定義されています。そして、タスクの性質は、ユニット/システムテストに役立ちます。つまりこれは基本的に、テキスト入力を非常に特定の形式で変換して、別の種類の明確に定義された形式(ある種のバイトコードまたはマシンコード)で出力します。したがって、テストケースの作成と検証は簡単です。

さらに、通常、バグも簡単に再現できます。正確なプラットフォームとコンパイラのバージョン情報は別として、通常必要なのは入力コードの一部だけです。言うまでもなく、コンパイラーのユーザー(開発者自身)は、平均的なコンピューターユーザーよりもはるかに正確で詳細なバグレポートを提供する傾向があります:-)

101
Péter Török

これまでのすべての素晴らしい答えに加えて:

「オブザーバーバイアス」があります。バグは観察されないため、バグはないと想定します。

私はあなたのように考えていました。それから私は専門的にコンパイラーを書き始めました、そして私にあなたに言わせてください、そこにはたくさんのバグがあります!

人々が書く残りのすべてのコードの99.999%のようなコードを書くので、バグを見ることはありません。あなたはおそらく、メソッドを呼び出してループを実行し、空想的で奇妙なことを行わない、完全に通常の、単純明快で明確なコードを記述します。これは、通常のビジネス問題を解決する通常の開発者だからです。

コンパイラーのバグは分析が簡単な通常のコードシナリオではないため、コンパイラーのバグは表示されません。バグはあなたが書いていない奇妙なコードの分析にあります。

一方、私は反対の観測者バイアスを持っています。私は毎日クレイジーなコードを毎日目にしているので、コンパイラーにはバグがぎっしり詰まっているようです。

あなたが任意の言語の言語仕様に腰を下ろし、その言語のコンパイラ実装を取り、コンパイラが仕様を正確に実装しているかどうかを本当に一生懸命試し、あいまいなコーナーケースに集中すると、すぐにあなたは見つけるでしょうコンパイラのバグが頻繁に発生します。例を挙げましょう。5分前に文字通り見つけたC#コンパイラのバグです。

static void N(ref int x){}
...
N(ref 123);

コンパイラーは3つのエラーを出します。

  • Refまたはout引数は割り当て可能な変数でなければなりません。
  • N(ref int x)の最適な一致には無効な引数があります。
  • 引数1に「ref」がありません。

明らかに、最初のエラーメッセージは正しく、3番目のエラーメッセージはバグです。エラー生成アルゴリズムは、最初の引数が無効である理由を解明しようと試み、それを調べて、定数であることを確認し、ソースコードに戻って「ref」としてマークされているかどうかを確認しません。むしろ、定数を参照としてマークするほど愚かな人はいないと想定し、参照が欠落している必要があると判断します。

正しい3番目のエラーメッセージが何であるかは明らかではありませんが、これはそうではありません。実際、secondエラーメッセージが正しいかどうかは明確ではありません。オーバーロードの解決が失敗するか、または「ref 123」を正しいタイプのref引数として扱う必要がありますか?ここで、考えてトリアージチームと話し合って、正しい動作を判断できるようにする必要があります。

あなたはおそらく、参照で123を渡そうとするほど愚かなことをしないので、このバグを見たことはありません。また、最初のメッセージは正しく、問題を診断するのに十分であるため、3番目のエラーメッセージが無意味であることにも気付かないでしょう。しかし、コンパイラを壊すためにtryingをしているので、私はそのようなことをやろうとします。試してみると、バグも表示されます。

66
Eric Lippert

私をからかってるの?コンパイラーにもバグがあり、実際にロードされます。

GCCはおそらく地球で最も有名なオープンソースコンパイラであり、そのバグデータベースをご覧ください。 http://gcc.gnu.org/bugzilla/buglist.cgi?product=gcc&component=c%2B% 2B&解像度= ---

GCC 3.2とGCC 3.2.3の間で、修正されたバグの数を確認してください: http://gcc.gnu.org/gcc-3.2/changes.html

Visual C++のような他のものについては、私は始めたくありません。

コンパイラを信頼できるようにするにはどうすればよいですか?そもそも、それらには負荷と単体テストの負荷があります。そして、地球全体がそれらを使用するので、テスターの不足はありません。

真面目な話ですが、私が信じたいコンパイラー開発者は優れたプログラマーであり、間違いなくではありませんが、かなりの能力を備えています。

54
Fanatic23

一日に二、三回出会った。 1つを検出する唯一の実際の方法は、アセンブリコードを確認することです。

他の投稿者が指摘した理由により、コンパイラーは信頼性が高いですが、コンパイラーの信頼性は多くの場合、自己実現的な評価であると思います。プログラマーはコンパイラーを標準として見る傾向があります。何かがうまくいかない場合、あなたはそれを自分のせいにして(99.999%の確率で)、コードを変更して、コンパイラーの問題を回避するようにします。たとえば、高度な最適化設定でコードがクラッシュすることは間違いなくコンパイラのバグですが、ほとんどの人はそれを少し低く設定して、バグを報告せずに次に進みます。

21
Karl Bielefeldt

コンパイラには、その正確さにつながるいくつかのプロパティがあります。

  • ドメインは非常によく知られており、研究されています。問題は明確に定義されており、提供されるソリューションは明確に定義されています。
  • コンパイラが正しく動作することを証明するには、自動テストで十分です
  • コンパイラーには非常に広範な、通常はパブリックな自動化された単体テストがあり、他のほとんどのプログラムよりも多くのエラー領域をカバーするために、時間とともに蓄積されてきました
  • コンパイラは結果を監視する非常に多くの眼球を持っています
15
blueberryfields

私たちはコンパイラを日常的に使用しています

...そして、コンパイラをどのようにして信頼性の高いものにするのでしょうか?

彼らはしません。します。 なぜなら誰もが常にそれらを使用しているため、バグがすぐに見つかります。

それは数字ゲームです。コンパイラは非常に広く使用されているため、バグwillが誰かによって引き起こされる可能性は非常に高いですが、ユーザーの数が非常に多いため、nlikely誰かが具体的にあなたになります。

したがって、それはあなたの視点に依存します。すべてのユーザーにわたって、コンパイラはバグがあります。しかし、あなたが行う前に他の誰かが同様のコードをコンパイルした可能性が非常に高いので、もしそれらのバグなら、あなたではなく、あなたに個人視点、それはバグが決してなかったように見えます。

もちろん、その上に、他のすべての答えをここに追加できます。コンパイラーはよく研究され、よく理解されています。彼らが書くのは難しいという神話があります。つまり、非常に賢く、非常に優れたプログラマーが実際に書こうとするだけであり、書くときは特に注意が必要です。これらは一般にテストが簡単で、ストレステストやファズテストも簡単です。コンパイラのユーザーは、それ自体がエキスパートプログラマである傾向があり、高品質のバグレポートにつながります。逆に、コンパイラの作成者は自分のコンパイラを使用する傾向があります。

14
Jörg W Mittag

すでにすべての回答に加えて、私は追加したいと思います:

信じる多くの場合、ベンダーは自分のドッグフードを食べています。つまり、コンパイラー自体を記述しています。

12
DevSolo

コンパイラのバグに頻繁に遭遇しました。

テスターの数が少ない暗いコーナーでそれらを見つけることができます。たとえば、GCCのバグを見つけるには、次のことを試してください。

  • クロスコンパイラーをビルドします。文字通り数十 GCCの構成およびビルドスクリプトのバグを見つけます。 GCCのコンパイル中にビルドエラーが発生するものもあれば、クロスコンパイラーが機能する実行可能ファイルをビルドできない場合もあります。
  • プロファイルブートストラップを使用して、GCCのItaniumバージョンをビルドします。私がGCC 4.4と4.5でこれを試した最後の数回は、動作するC++例外ハンドラーを生成できませんでした。最適化されていないビルドは正常に機能しました。私が報告したバグを修正することに誰も興味を持っていないようで、GCC asmメモリ仕様の何が壊れているのかを調べようとした後、自分で修正することをあきらめました。
  • ディストリビューションビルドスクリプトを実行せずに、最新のものから独自の動作するGCJをビルドしてみてください。出来ることならどうぞ。
8
Zan Lynx

いくつかの理由:

  • コンパイラライター "自分のドッグフードを食べる"。
  • コンパイラは、CSのよく理解された原則に基づいています。
  • コンパイラは非常に明確な仕様に構築されています。
  • コンパイラはtestedを取得します。
  • コンパイラは常に非常に信頼できるとは限りませんです。
6
Kramii

彼らは通常-O0で非常に優れています。実際、コンパイラのバグが疑われる場合は、-O0と使用しようとしているレベルを比較します。最適化レベルが高いほど、リスクが高くなります。一部は意図的にそうでさえあり、ドキュメントでそのようにラベル付けされています。私は非常に多く(少なくとも100人)に遭遇しましたが、最近では非常にまれになっています。それにもかかわらず、優れたスペックマークの数値(またはマーケティングにとって重要な他のベンチマーク)を追求する場合、限界をプッシュする誘惑は素晴らしいです。数年前に、ベンダー(名前を付けずに)が、特別に明確にラベル付けされたコンパイルオプションではなく、括弧のデフォルトに違反することを決定したときに問題が発生しました。

コンパイラのエラーを診断するのは難しいですが、メモリの参照を参照するのではなく、さまざまなオプションを使用して再コンパイルすると、メモリ内のデータオブジェクトの相対的な位置が単純にスクランブルされるため、ソースコードのハイゼンバグなのか、バグがあるのか​​わかりません。コンパイラ。また、多くの最適化は、演算の順序に正当な変更を加えたり、代数を代数的に単純化したりします。これらは、浮動小数点の丸めとアンダーフロー/オーバーフローに関して異なるプロパティを持ちます。これらの影響をREALのバグから解きほぐすことは困難です。この理由により、ハードコアの浮動小数点計算は困難です。これは、バグや数値の敏感さを解決することが難しい場合が多いためです。

5
Omega Centauri

コンパイラのバグはそれほど珍しいものではありません。最も一般的なケースは、コンパイラーが受け入れるべきコードに関するエラーを報告するか、またはコンパイラーが拒否されたはずのコードを受け入れることです。

5
kevin cline

コンパイラ自体のバグに遭遇したことがありますか?それは何でしたか、そしてコンパイラー自体に問題があることにどのように気づきましたか?

うん!

最も印象に残った2つは、私が遭遇した最初の2つでした。どちらも1985-7年頃の680x0 Mac用のLightspeed Cコンパイラにありました。

最初のものは、状況によっては、整数のポストインクリメント演算子が何も実行しなかった場所です。つまり、特定のコードでは、「i ++」は単に「i」に対して何もしませんでした。分解を見るまで髪を抜いていた。次に、別の方法でインクリメントを行い、バグレポートを送信しました。

2つ目はもう少し複雑で、実際には考えられない「機能」であり、失敗しました。初期のMacには、低レベルのディスク操作を行うための複雑なシステムがありました。何らかの理由で私は理解できなかった-おそらくより小さな実行可能ファイルの作成に関係している-コンパイラーがオブジェクトコード内にディスク操作命令をインプレースで生成するのではなく、Lightspeedコンパイラーは実行時にディスク操作を生成する内部関数を呼び出すスタック上の命令とそこにジャンプしました。

これは68000 CPUでうまく機能しましたが、68020 CPUで同じコードを実行すると、奇妙なことがよくあります。 68020の新機能は、プリミティブ命令256バイトの命令キャッシュであることが判明しました。これはCPUキャッシュの初期の段階であり、キャッシュが「ダーティ」であり、再充填する必要があるという概念はありませんでした。 MotorolaのCPU設計者は自己変更コードについて考えていなかったと思います。したがって、実行シーケンスで2つのディスク操作を十分に近づけて行い、Lightspeedランタイムがスタック上の同じ場所に実際の命令を作成した場合、CPUは誤って命令キャッシュヒットがあると見なし、最初のディスク操作を2回実行します。

繰り返しになりますが、それを理解するには、逆アセンブラーを使って少し掘り下げ、低レベルのデバッガーで多くのシングルステップを実行しました。私の回避策は、すべてのディスク操作の前に、256個の「NOP」命令を実行する関数の呼び出しを追加することでした。これにより、命令キャッシュがフラッディング(およびクリア)されました。

それからの25年間で、コンパイラーのバグは次第に少なくなっています。その理由はいくつかあると思います。

  • コンパイラの検証テストのセットは増え続けています。
  • 最新のコンパイラは通常、2つ以上の部分に分かれています。1つはプラットフォームに依存しないコード(たとえば、仮想CPUと見なされる可能性のあるものをターゲットとするLLVMのコード)を生成し、もう1つは実際のターゲットハードウェアの命令に変換します。マルチプラットフォームコンパイラでは、最初の部分があらゆる場所で使用されるため、実際のテストが大量に行われます。
4
Bob Murphy

5.5年前にTurbo Pascalで重大なエラーが見つかりました。前のバージョン(5.0)と次のバージョン(6.0)のどちらのコンパイラーにもエラーはありません。そして、それはまったくのコーナーケースではなかったので、テストするのは簡単だったはずです(一般的に使用されていない呼び出しのみ)。

一般に、(趣味のプロジェクトではなく)商用コンパイラビルダーには、非常に広範なQAとテスト手順が用意されています。彼らは彼らのコンパイラーが彼らの旗艦プロジェクトであることを知っており、欠陥は彼らにとって非常にひどく見え、他のほとんどの製品を作っている他の会社よりもひどいものになるだろう。ソフトウェア開発者は容赦のない束であり、ツールサプライヤーは、サプライヤーからの修正を待つのではなく、代替案を探しに行く可能性が高いと私たちを落胆させます。例。他の多くの業界ではそうではないので、深刻なバグの結果としてのコンパイラーメーカーの潜在的な損失は、ビデオ編集ソフトウェアのメーカーと言うよりもはるかに大きいです。

4
jwenting

はい、昨日ASP.NETコンパイラでバグが発生しました。

ビューで強く型付けされたモデルを使用する場合、テンプレートに含めることができるパラメーターの数には制限があります。明らかに4つを超えるテンプレートパラメータをとることはできないため、以下の両方の例では、コンパイラが処理するには多すぎます。

ViewUserControl<System.Tuple<type1, type2, type3, type4, type5>>

そのままではコンパイルできませんが、type5は削除されました。

ViewUserControl<System.Tuple<MyModel, System.Func<type1, type2, type3, type4>>>

type4は削除されました。

ご了承ください System.Tupleには多くのオーバーロードがあり、最大16個のパラメーターをとることができます(私が知っているのは奇妙です)。

3
user8685

コンパイラのバグが発生しますが、奇妙なコーナーでそれらを見つける傾向があります...

1990年代にDigital Equipment Corporation VAX VMS Cコンパイラに奇妙なバグがありました

(当時のファッションと同様に、私はベルトにタマネギを着ていました)

Forループの前にある余分なセミコロンは、forループの本体としてコンパイルされます。

f(){...}
;
g(){...}

void test(){
  int i;
  for ( i=0; i < 10; i++){
     puts("hello");
  }
}

問題のコンパイラでは、ループは1回だけ実行されます。

見る

f(){...}
g(){...}

void test(){
  int i;
  for ( i=0; i < 10; i++) ;  /* empty statement for fun */

  {
     puts("hello");
  }
}

それは私に多くの時間を費やしました。

私たちが(以前)実務経験に与えた古いバージョンのPIC Cコンパイラは、優先度の高い割り込みを正しく使用するコードを生成できませんでした。 2〜3年待ってアップグレードする必要がありました。

MSVC 6コンパイラのリンカには気の利いたバグがあり、セグメンテーションエラーが発生し、理由もなく停止することがあります。クリーンビルドは一般的にそれを修正しました(しかしsighは常にではありません)。

3
Tim Williscroft

-O0と-O2を使用してコンパイルしたときにソフトウェアの動作が異なる場合、コンパイラのバグが見つかりました。

ソフトウェアの動作が予想とまったく異なる場合、バグはコードにある可能性があります。

2
mouviciel

アビオニクスソフトウェアなどの一部のドメインでは、コードとハードウェア、およびコンパイラに関して、非常に高い認証要件があります。この最後の部分については、正式に検証されたCコンパイラを作成することを目的としたプロジェクトがあり、これは Compcert と呼ばれます。理論的には、この種類のコンパイラは、信頼できるものです。

2
Axel

私はいくつかのコンパイラのバグを見てきましたが、いくつか(特にF#で)自分で報告しました。

とは言っても、コンパイラーを書く人は一般に、コードの数学的な意味を本当に意識させるコンピューターサイエンスの厳密な概念に非常に慣れているため、コンパイラーのバグはまれだと思います。

それらのほとんどは、ラムダ計算、形式的検証、意味論的意味論などの事柄に非常に精通していると思います-私のような平均的なプログラマーがやっと理解できることだけです。

また、コンパイラでは通常、入力から出力へのかなり簡単なマッピングがあるため、プログラミング言語のデバッグは、たとえばブログエンジンのデバッグよりもはるかに簡単です。

2
Rei Miyasaka

私はC#コンパイラにバグを発見しましたが、C#設計チームのEric Lippertがどのようにしてバグを特定したかがわかります こちら

すでに与えられた答えに加えて、私はいくつかのことを追加したいと思います。多くの場合、コンパイラー設計者は非常に優れたプログラマーです。コンパイラーは非常に重要です。ほとんどのプログラミングはコンパイラーを使用して行われるため、コンパイラーは高品質でなければなりません。したがって、最高の人材を配置することがコンパイラを作成する企業の最大の利益になります(または、少なくとも非常に優れたコンパイラ:最高のコンパイラはコンパイラの設計を好まないかもしれません)。 Microsoftは、CおよびC++コンパイラーが適切に機能することを強く望んでいます。

また、本当に複雑なコンパイラを構築している場合は、それを一緒にハックすることはできません。コンパイラの背後にあるロジックは非常に複雑であり、形式化も簡単です。したがって、これらのプログラムは非常に「堅牢」で一般的な方法で構築されることが多く、バグが少なくなる傾向があります。

2
Alex ten Brink