オプション沼(5) gccに-Sオプションつけて、アセンブラ出力を愛でてみる

Joseph Halfmoon

今回は gcc の -S オプションを使ってみます。「もっとも狭義の」コンパイル、アセンブリ言語ソース生成までで処理を止めるもの。コンパイラのお仕事を途中で止めておくオプションでも -c はよく使うとおもいますが、-Sはあまり使わない気がします(個人の感想デス。)アセンブリ言語ソースを読まねばいられない人は別にして。

今回は、以下の異なるOS環境の異なるgccバージョンにて生成物を眺めたいと思います。

    1. Ubuntu 20.04LTS、Windows11上のWSL2内で動作
    2. 32bit の Raspberry Pi OS
    3. 64bit の Raspberry Pi OS

1のCPUアーキテクチャはx86_64。2はARMv6(32ビット)、3はARMv8(64ビット)です。CPUが異なるので、出力されるアセンブリ言語ソースも違う筈。

なお、使用するソースファイルは、前回アッカーマン関数をgprofにかけたときに使用したソースそのままです。上記3つの環境でビルドして動作することはそれぞれ確認済。ただし、実行速度は段違いな感じがします。知らんけど。

-Sオプション以外に最適化しない-O0を漏れなくつけてます。こんな感じ。

$ gcc -S -O0 ackermann.c

それでは、-S オプションつけて生成されるアセンブリ言語ソース(.s拡張子)を愛でていきたいと思います。

Windows11上のWSL2内のUbuntu20.04LTSの場合

まず、実験に使用したUbuntu(Linuxのディストリビューションの一つです、念のため)のos-releaseは以下のとおり。UbuntuOSversion

この上でデフォルトで使えるgcc のバージョンは以下の通りでした。UbuntuGCCversion

さて、-Sオプションつけて生成された アセンブリ言語ソースの先頭部分が以下に(特に指定しなければ ackermann.c を -Sつきでコンパイルすると ackermann.s が生成されます。)x86_64_ackermannS

確かに、x86_64のソースだけれど、GAS伝統のAT&T式のソースです。オペランドの代入は左から右へ(モトローラ風ともいえる。)まあ、個人的には右から左へのインテル式の方が慣れているのですが。でもデフォルト優先主義者なので、直にAT&T式を受け入れましょう(どうもインテル式ソースを出力させるオプションもあるみたいですが、それはまた後で。)

レジスタの記述に %rspとかのrチョメチョメと、%EAXなどのeチョメチョメが混在しているのは64ビットのコードならではですな。メモリアドレスを指す時は、%rspのように64ビットのフル幅のレジスタで指し、32ビット整数データの処理は%eaxのような32ビット幅のレジスタで処理すると。

しかし、上記はアセンブリ言語ソースであって、まだ、アセンブルもリンクもかかっていない状態です。その後のアセンブルとリンクの過程を経たオブジェクトを逆アセンブルすると上記のあたりはこんな感じ。

アセンブリコードの面影は色濃く残っているけれども、後から決まったものどももあり、また、ローカルなとび先ラベルは消えてしまうし、と。x86_64_ackermannLis

Raspberry Pi 3 model B+上のRaspbian OS (32bit) の場合

つづいては32ビットのArmです。Cortex-A53の32ビットモード。まずは os-releaseから。raspberryPi3OSversion

その上のGCCは8.3.0とな。raspberryPi3GCCversion

さて、アセンブリ言語ソースが以下に。”.align 2” にRISCの片りん?を感じますな。さきほどのx86_64は64ビットといいながらバイト可変長なCISCだったので。

冒頭のスタックへのPUSH、{}で3レジスタを一気にしまっているところなどArmの32ビットならではです(Armでも64bitになるとこういうヤバイ命令はなくなります。)Armはキホン、オペランドは右から左のインテル式なので読みやすいっす。raspberryPi3SRC

コンパイラに与えるコマンドライン引数は一緒でも、生成されるコードはまったく違うと。あったり前。

Raspberry Pi 4 model B上のRaspberry Pi OS (64bit)の場合

それではArmでも64ビット環境。Cotex-A72の64ビットモード使い。まずはOSから。raspberryPi4OSversion

そしてGCCのバージョン。これが3環境の中では一番新しいみたいっす。raspberryPi4GCCversion

そして生成されたアセンブリ言語ソースが以下に。raspberryPi4SRC

32ビットのArmの面影も残っているちゃ残っているけれども、x86_64に似ている部分もあり。だいたい最初のスタックプッシュのstp命令、Arm64ならではな感じ(A64のスタックは16バイトアライメント)がします。詳しくは別シリーズのA64のアセンブラ記事へ。なおこちら(64ビットモード)ではレジスタ名のxチョメチョメが64ビット幅、wチョメチョメが32ビット幅。Armらしい荒業の「ポスト」「プリ」インデックスアドレシングを使うときは!マークがつきます。

まあ -S オプションつければアセンブラは読めるっと。でも読まなきゃならない状況には入りたくない気もするし。。。たまに眺めて悦に入るのが関の山?

オプション沼(4) gccに-pgオプションつけて、アッカーマン関数の再帰の回数を数えてみる へ戻る

オプション沼(6) gccに -c オプション、できるファイルはいろいろなのよ。へ進む