2012年04月09日
ARMの64bitアーキテクチャ ARMv8 の概要
"ARMv8 Instruction Set Overview"というドキュメントがARMのサイトからダウンロード可能です。(ただし登録が必要。)
この資料からARMv8アーキテクチャの概要をながめてみました。
"ARMv8 Instruction Set Overview"
これはまだOverviewで、各命令のビットパターンまでは書いてありません。そのためこの資料だけでは、逆アセンブラやCPUエミュレータを作ることはできません。
2つの実行状態
AArch64: ARMv8での64bitの実行状態。
AArch32: ARMv8での32bitの実行状態。ほぼARMv7互換。追加されてる命令があります。
(この実行状態をどうやって切り替えるのかはこのドキュメントには書いてないようです。おそらく特権モード(OSカーネルの中)でないと切り替えられないのではないでしょうか。そのため、ARM<->Thumbのように、関数呼び出しのたびに切り替え可能なものではなく、x86_64でのx86モードのように、プロセス単位で選択して使用するようになるのではないかと思われます。)
命令セットの名称
A64: AArch64状態で利用可能な命令セット。
A32: 32bitのARM命令セット。
T32: 16/32bit混合のThumb2命令セット。
(ThumbEEのモードがあるかどうかは明言されていません。)
A64
A64 overview: A clean, fixed length instruction set.
32bit固定長。
"clean" Thumb2の時は隙間に無理矢理詰め込んだから、すごく不規則なビットパターンでした。逆アセンブラを作るのは大変だそうです。A64は新設計なのですっきりとデコードしやすいものになるはずです。
AArch64モードとAArch32モードではデコーダが完全に切り替わるということを意味しています。
逆アセンブル表示するならば、まずどちらのモードなのかを知る必要があります。
レジスタ
31個の64bitの汎用レジスタ。(0-30)
5bitのフィールドで表現され、31は特別扱いです。
The procedure call link register (LR) はレジスタ30。
これとは別に浮動小数点レジスタがあります。32個の128bitレジスタ。
SIMD命令(NEON)はこの浮動小数点レジスタを使用します。
レジスタ31
レジスタ31は大抵のケースでゼロレジスタとして機能します。つまり、読んだらゼロで、書いた内容は捨てらます。
load/storeのベースレジスタとしてレジスタ31が使われた場合と、一部の算術演算命令でレジスタ31は現在のモードのスタックポインタを参照します。割り込みなどで動作モードが切り替わったときにはCPUは自動的にスタックポインタを切り替えます。
ADD命令のdstにSPが指定できるので、スタックポインタへの代入は、
ADD SP, Xn, #0
と書けばできます。また、アセンブラでは
MOV SP, Xn
と表記できます。実際には、これはADD SP, Xn, #0 と同じコードが生成されます。
プログラムカウンタ
PC(Program Counter)は汎用レジスタに含まれません。PC相対のアドレッシングモードがあるので、これを使ってPCの値は取得することができます。PCへの書き込みは分岐命令によって行います。
ARM命令だとR15がPCで、R15にmovするとブランチしました。また、R15にaddすることでswitch文をシンプルに実装したりできました。
パイプラインが深くなる今時のCPUの実装を考えたら、任意の時点でPCをレジスタとして読み書きを許すのは大変です。どの時点でPCの値がどうなっているかを全部仕様で明らかにしなければなりません。また、将来にわたってそれを守ることが実装の足かせになります。
Thumb2ではR15への書き込みはunpredictableだったはずです。
メモリアクセス
LDM, STM, PUSH, POPのような複数のレジスタをまとめて、ロードストアする命令はありません。ただしレジスタペア(連続したレジスタ番号でなくてもよい)の128bitでのロードスストアが可能です。
レジスタペアやfloat, SIMDレジスタを含む、ほとんどのロードストアでUnaligned addressesが可能です。
ただし、exclusive and ordered accessesのロードストアはalinged adressのみです。
オフセットを加算する前のスタックポインタの値は16byteアラインでなければなりません。そうでないとstack alignment exception が発生します。
LDNP, STNP命令: Load-Store Non-temporal Pair 2つのレジスタのロードストアを"non-temporal”のヒント付きで行う。これは、このあと再び同じところをアクセスする可能性は低いという意味です。キャッシュシステムはこのヒントによって処理を変えることができます。
命令
ARM命令では全ての命令の条件付きで実行することができましたが、A64では条件付きの命令はかなり減りました。条件付きブランチと少数のdata processing命令のみです。Thumb2のIT命令に相当するものはありません。
ARM命令のアドレシングモードにあったrotated 8-bit immediate はもっとわかりやすいものに置き換えられました。
CPSRをひとつのレジスタとしてアクセスする命令はなくなりました。その代わりそれぞれのプロセッサ状態を更新する新しいシステム命令が導入されました。
“coprocessor”という概念は削除されました。 ARMv7まではMMUやTLB, Cacheなどはコプロセッサに対する操作でした。
レジスタの無条件ブランチで、BR XmとRET {Xm}が区別されています。CPUにサブルーチンからの戻りかどうかのヒントが渡されます。RET命令ではレジスタ省略するとX30が使わます。
条件付き代入。
CSEL Wd, Wn, Wm, cond
- - Conditional Select: Wd = if cond then Wn else Wm.
整数演算
A64のほとんどの整数命令は32bit演算と64bit演算の2つの形式を持ちます。
アセンブラ表記では、整数演算でレジスタをWnと書くと32bit演算、Xnと書くと64bit演算になります。(0<=n<31)
32bit演算の場合:
ソースレジスタの上位32bitは無視されます。
デスティネーションレジスタの上位32bitはゼロクリアされます。
コンディションbitは下位32bitの結果を反映したものになります。
ゼロレジスタとしてのレジスタ31の表記。
32bit: WZR
64bit: XZR。
スタックポインタとしてのレジスタ31の表記。
32bit: WSP
64bit: SP。
W31やX31は無い。
仮想メモリシステム
AArch64のメモリ変換システムは49bitの仮想アドレスをサポートします。(変換テーブルあたり48bit) --> ?? 残りの1bitは?
仮想アドレスは49bitから符号拡張されて64bitポインタに格納されます。
オプションとして64bitの中の上位8bitはタグとして扱うことを選択できます。タグはロードストア命令と間接ブランチ命令では無視されます。
このようなタグは動的型付け言語の実装に便利です。
49bitの仮想アドレスの最上位ビットはカーネル空間とユーザー空間の区別に使うと便利なのでしょうか?
除算命令
ARMv7r,m には除算命令が追加されましたが、ARMv7aには除算命令はありませんでした。
A64には符号付き/無し/32bit/64bitの除算命令があります。剰余算は無い。剰余は被序数 - 序数 * 商 で算出できますが、これは一命令で可能です。
“divide by zero”に対するハードウェアのチェックはありません。しかし長いレイテンシの後で0割りの結果は0が書き込まれます。つまり、例外は発生しません。除算命令を実行する前に序数をチェックするべきです。
符号付き整数の割り算で、INT_MIN / (-1) を実行すると何も例外を発生させずに結果はINT_MINになります。これはJavaVMでの定義と同じです。
浮動小数点演算
全ての浮動小数点のmultiply-addとmultiply-subtract命令は“fused”(飽和演算)
浮動小数点演算は強力に拡張されていますが、ここでは省略。