GCC@SH4
- ===---
r00:関数戻り値
r01:Work
r02:Work
r03:Work
r04:最初のパラメータ
r05:2番目のパラメータ
r06:3番目のパラメータ
r07:それ以上のパラメータを格納した場所へのポインタ
r08:これから呼び出す関数を指すポインタ
r09:
r10:
r11:
r12:
r13:
r14:スタック内の自動変数をアクセスするためのベースポインタ
r15:スタックポインタ - ===---
こんな感じ。r1~r3は汎用に使われているらしい。
r9~r13の用途はまだ詳しくわかってないので不明。
r6は3番目のパラメータを格納するという役目だけでなく、
r7をポインタとするパラメータ領域へのアクセスにおいても、
データ転送の一時ワーク領域としてよく用いられているようだ。
32bitクリーンな設計ってのはラクでいいね。
int型の値も、アドレスポインタも、どっちも32bitだから、同じように
レジスタに積んで渡すことができる。
例えば、今回の解析にはprintf()を使ったサンプルコードを使ったのだが、
フォーマット指定文字列へのポインタが第1パラメータとして、
r04に格納され、次の引数がr05、その次はr06…という感じなのだ。
昔いじってたLSI-C(86)みたいに複雑な置き方してないのでわかりやすい。 まぁ、LSI-Cって奴は関数呼び出し時の効率アップを狙って、 特にレジスタ渡しを積極的に使う処理系だったのだけどね。 内部に大量のレジスタを持つRISCチップでは、このテの高速化テクニックなど常識だが、 汎用レジスタの少ないx86チップではあまり変わり無いように思った。 なんせ、使える汎用レジスタはAX,BX,CX,DXの4本しか無い。 IntelはSIやDIといったIndexレジスタも汎用レジスタだと言い張ってたけど、無理がある。 結局、レジスタに入れて渡されても、ワークエリアを確保するために、 またスタックにレジスタの値を積んでしまう場合がほとんどで、 呼び出し側で積むか、呼び出された側で積むかの違いしか無かった様に思う。 必ずスタックに積まれる場合よりもチューンできる可能性があるのは確かだけどね。
r8は関数呼び出しの前に必ず関数のエントリポイントがセットされる。
関数呼び出しは基本的に、jsr r8 命令で行うらしい。
引数の積み方は、まず変数の必要量をコンパイラが計算、
確保する容量分だけスタックを減算(まとめてプッシュダウン)し、
それをr14へ転送。あとはr14をベースポインタとしてディスプレースメント付きのメモリアクセスで
それぞれの変数にアクセスする仕組みらしい。
基本的に自動変数はメモリの下位から、出てきた順番に載せていくようで、
個々にプッシュダウン・スタックを使って順番に格納した時のように、メモリの上側から
次々にメモリの下位へ向けて積まれるのではない。(その逆になる)
使い終われば、スタックポインタが動かされて、使っていた領域は放棄される。
関数に積まれるものはレジスタに3つまで格納できるので、
確保されるスタックは3ワード(DWORD)分少ない領域になる。といった感じ。
また、関数が利用する自動変数は、基本的にスタック上に宣言された分だけ確保され、
レジスタに割りつけることはしていないらしい。(Optimize -O 指定無しの場合)
SuperHにはサブルーチン呼び出し時に戻りアドレスをセーブできる領域が1つしかないため、
多重にサブルーチンを呼び出す場合は内部レジスタに置いておくか、
メモリ上(普通はスタック)への退避が不可欠になるが、このへんの処理に関しては
まだよく理解しきれていないので次の機会に。
そうそう、SuperHの場合、自動的にメモリアドレスを計算してくれるロジックは
レジスタのオートインクリメント/デクリメントぐらいしかないようで、
スタック操作などは全て個別に命令を発行しているのが見える。
まぁ、GCCが汎用レジスタのr15をそういう目的に割り当てているだけのことだし、
普通の演算命令を組み合わせてそれっぽく見せているだけのことなので、
当然と言えば当然なんだが、それでもやはり、全部命令発行して動かすところが、
何というか、RISCチップらしいよね。
P.S
ちなみに、富士通が出した某チップの開発環境作成にかかわっている
これまた某氏に言わせれば、Advanced RISC MachinesのARMは、基本的にRISCでは無いのだとか。
(現在は社名変更でARM社になっているはず)
まぁ、高機能過ぎる命令セットだからなぁ。なんせ、通常命令に
シフト演算だの条件判断をくっつけているという、変態ちっくな命令セットだし。
このへんは@ITの記事に面白いことが書いてあった。
つまり、ARMはもともRISC向きでないそのアーキテクチャが持つ特性を
数々の拡張で覆い隠し、覇権を取ったのだね。
消費電力にフォーカスして、組み込み向けに必要な機能をそのアーキテクチャに、
次から次へと取り込んで大きくなったという感じか。
SuperHも同じものを目指していたはずだが、
SHはRISCにこだわり、貧弱なバスでも性能を確保できる16bit固定長命令セットにこだわっていたため、
そのコード体系が狭い空間に無理やり押しこまれたものになっててビンボ臭い。
対してARMはもともと高機能で、16Bitコードセットは基本的にサブセット。
さらに命令拡張を繰り返してぶくぶくと太ったIntelのx86系に通じるものがあるな。
だけど、コードをチューンして少ない命令で周波数以上の性能を発揮できる
CISCっぽい特性を備えているのは、メモリの限られた組み込み分野で見れば、
有利なポイントになるのだろう。
しかし、アーキテクチャが肥大してしまえば、チップの上に載せるトランジスタ数が増える。
これらの消費する電力がそのうち問題になってくるはずだ。
その場合は、あっさりサブセットになってる命令を切り落とすのかな。