私が理解している限りでは、プログラム (たとえば C で記述されたもの) をコンパイルすると、まずアセンブリ言語に変換され、次に機械語に変換されます。「アセンブリ言語のステップ」をスキップできない (スキップできない) のはなぜですか?
7 に答える
あなたの理解は間違っています。コンパイラは必ずしも C コードをアセンブラに変換するとは限りません。それらは通常、いくつかのフェーズを実行し、内部表現を持っていますが、これは必ずしも人間が読めるアセンブラーに似ているとは限りません。
ここで、 LLVMの素敵な紹介を見つけました。LLVM は、clang に使用されるコンパイラ ツールキットです。
コンパイラ開発者にとっては簡単です。
Cを読み取り、オブジェクトコードを書き込むコンパイラを作成することができます。ただし、これには、コンパイラー作成者が命令をエンコードするすべての計算を書き込む必要があります。一部のマシンでは、命令のエンコードは複雑です。さらに、分岐ターゲットがどれだけ離れているかなど、他の相互作用に応じて入力するフィールドがあります。これは、分岐とターゲットの間にある命令によって異なります。
さらに、コンパイラの記述方法の一部は、「オブジェクトxをインクリメントするには、インクリメント命令を発行する」などのパターンを使用することです。オブジェクトコードを直接書き込むには、書き込みたいすべての命令をそれらのパターンにエンコードする必要があります。つまり、パターンには、命令を記述するためのある種の言語が必要です。
ええと、私たちはすでにそのための言語を持っています:アセンブリ言語。したがって、「オブジェクトxをインクリメントするには、発行する」などの方法でパターンを作成する方が簡単ですinc x
。</ p>
最新のコンパイラには多くの層があります。Cテキスト(または他の言語)を読み取り、コンパイラ内部の言語に変換するフロントエンドがあります。内部言語(またはその表現)を操作し、コードを改善しようとするオプティマイザーがあります。内部言語をアセンブリ言語に変換するバックエンドがあります。アセンブリをオブジェクトコードに変換するアセンブラがあります。また、オブジェクトコードを実行可能ファイルにリンクするリンカーがあります。
多くの複雑なタスクと同様に、複雑なタスクが適切な部分に分割されていると、人間の心が複雑なタスクを処理するのが簡単になります。これにより、バグが減り、ソフトウェアの操作にかかる時間が改善されます。また、新しい言語をサポートするようにフロントエンドを変更したり(CではなくJavaなど)、新しいプロセッサをサポートするようにバックエンドを変更したり(IntelアセンブリからPowerPCアセンブリに変更)できるため、ソフトウェアが柔軟になります。また、1つのオプティマイザーを変更すると、JavaとC、IntelとPowerPCのすべてのコンパイラーが改善されます。
コンパイルに使用するgccコマンドは、実際には、フロントエンド処理、最適化、アセンブリ、およびリンクを実行する他のプログラムを呼び出す単なるドライバーです。これらのフェーズのほとんどを個別に呼び出すことも、スイッチを使用してgccに使用しているコマンドを表示するように指示することもできます。
さらに、GCCには、開発者がCコードと直接混合されたアセンブリ言語を挿入できる機能があります。これにより、GCCはアセンブラを含める必要があります。
オペレーティング システムはそのようなことはしません。これはコンパイラの仕事です。実際、多くの場合、オブジェクト ファイルを直接出力します。アセンブリ コードを出力するよう明示的に要求する必要があります。他の人は、完全な機能を備えたオブジェクト ファイルを発行するには、このために存在するさまざまな形式に関する専門知識が必要であるため、そうしないことを選択します。アセンブラには、作業を容易にするさまざまな便利な機能があり、(場合によっては?) アセンブリ コードを変更せずに複数のオブジェクト ファイル形式をターゲットにすることができます。また、注釈付きのアセンブリ コードを発行することは非常に便利な機能であるため、オブジェクト ファイルを直接発行するためだけに別のコード ジェネレーターを使用しないことで、(アセンブラーが必要になることを除いて) 制限なしで時間を節約できます。資力。
クロスコンパイラは、そのクロスコンパイラがインストールされているOSの助けを借りずに、マシンコードを直接生成できます。
たとえば、Windowsにインストールされたtornadoパッケージは、vxworksのマシンコードを生成できます。
アセンブリコードは、マシンコードの便利で人間が読める形式の表現であり、さまざまな変換ユニットの出力をまとめるときにリンカが必要とするシンボリック参照と再配置です。中間のアセンブリ言語ステップがなければ、コンパイラーはリンカーが必要とする形式で再配置を生成する責任もあります。これは実行可能ですが、面倒です。この機能を備えたアセンブラは、手書きのアセンブリコードを処理するためにすでに存在しているため、これを使用するのは理にかなっています。
通常、アセンブラー段階はありません。MSVC (cl.exe) と GCC は、すぐにマシン コード (.obj、.o) を生成します。
コンパイラに依存します。アセンブリ コードは実際には必要ありません。
おそらく、あなたが話しているコンパイラ (GNU-CC?) の作成者は、ブランチなどの特定のものを自分で解決する必要がなければ、自分自身にとって少し簡単だと考えていました。