GAIO CLUB

2023年09月28日

【第13回】C言語と機械語の関係

静的解析/コンパイラ技術
いまさら聞けない静的解析/コンパイラ技術
これまでメモリの話や変数初期化の話などをしてきましたが、今回は関数の機械語化についての話です。
CコンパイラはC言語プログラムをアセンブリ言語に変換(翻訳)します。アセンブリ言語はCPUの機械語に対応しています(マイクロコードが介在することもありますが)。

高級言語と低級言語

低級言語という言い方はあまりしないと思いますが、アセンブリ言語は高級言語ではありません。ソースを見て、プログラムの処理内容が理解しやすい言語が高級言語なのだと思います。もちろん書き方によりますから、一般的な話です。

C言語に比べて、アセンブリ言語が低級と位置付けられる理由は、第一にCPUによって言語仕様が違います。アセンブリ言語ソースプログラムは特定のCPUでしか使えません。
そして、if・for・switchなどの文がありません。あるのは、比較、その結果による条件分岐、無条件分岐、コール、リターン、ほかには加減乗除などの演算命令がある程度です。分岐やコールについては、距離制限付きで遠くまで飛べないものもあります。演算命令も、浮動小数点用が無かったり、4バイトの演算ができなかったり、かなり厳しいです。その演算も、変数をそのまま使うことができず、一旦レジスタに入れて計算し、結果を変数に戻さなければなりません。そして、レジスタの数には限りがあります。Cコンパイラは、Cソースプログラムをこんなアセンブリ言語に変換しています。

LLVMが有名になったコンパイラclangはCソースを一旦LLVM-IR言語に変換し、その後、CPUのアセンブリ言語に変換しています。アセンブリ言語に近いと言われるLLVM-IRに変換するところまでは、どんなCPUでも使えるわけです。機械語にするCPUを決めてないので、LLVM-IRのレジスタ数は無限です。目的のCPUのアセンブリ言語に変換する時は、足りないレジスタの代わりにスタックを使うことになるはずです。
アセンブリ言語
余談ですが、実は、GAIO製コンパイラも独自の中間言語に変換した後、いろんなCPUのアセンブリ言語に変換していました。GAIO製は中間言語に変換する前にCPUの情報を取り込んでいたので、レジスタ数は無限ではなく、目的のCPUに合わせた中間言語を作っていました。

C言語の文がどのようなアセンブリ言語に変わるか、お話したいのですが、この話はみなさんの開発にはあまり役立たないと思いますので、少しだけ例を見て頂こうと思います。

文の展開

アセンブリ言語の説明を見て頂いて、おわかりと思いますが、文は比較と条件分岐・無条件分岐の命令で実現しています。今は書く方もおられないでしょうが、フローチャートで表現すると分かりやすいと思うので、C言語のfor文・switch文に対するアセンブリ言語と、そのフローチャートの例をお見せします。

アセンブリ言語はGAIO製Cコンパイラ(SH3版)で生成したものです。赤字は関数の先頭、緑字はコメントです。

for文の例

for文の例
C言語の本に見られるfor文のフローチャート説明とは少し違って、条件判定が下にありますが、処理自体は同じです。

switch文の例

switch文の例
switchの(i)とcaseラベル値を比較&条件分岐する命令が並びます。
caseラベルがある程度整列している時(たとえばcase 10~case 15まで揃っているような時)は、条件分岐命令を並べるのではなく、分岐先のアドレステーブルを作り、(i)の値をインデックスにしてテーブル分岐するような命令を使うこともあります。
アドレステーブルと条件分岐連続のどちらが効率的か、コンパイラは機械語命令の仕様と相談して使い分けます。

ループが得意なDSP

DSPなどのアセンブリ言語にはループ処理のための命令が揃っているものがあり、C言語のfor文に合わせやすい場合があります。
ループが得意なDSP
デバッグに関しては別の回でお話しようと思いますが、たとえばアセンブリ言語1命令でループ処理が終わってしまう場合、C言語プログラムのデバッグでfor文をステップ実行しても、全くループせずに終わってしまいます。
このような例を見ると、ある意味では、アセンブリ言語の方が高級と言えるかもしれません。

筆者紹介

浅野 昌尚(あさの まさなお)

ガイオ・テクノロジー株式会社

開発2部 QTXグループ

1980年代から30年以上にわたり汎用構造のCコンパイラ開発に従事し、その間に8ビットマイコンからRISC・VLIW・画像処理プロセッサまで、さまざまなCPU向けのクロスCコンパイラを開発。

人気のコラム

最新のコラム