前回まで実習していた条件フラグ命令、マニュアルにはまだまだあるな~と思っていたのですが勘違いでした。今回ターゲットのArm Cortex-A72はARMv8の中ではキホンのARMv8p0なので使えないようです。残念。そこで今回から整数の乗算命令に転じます。これがまた、いろいろあるのよ。今回はざっと眺めるだけ、実習なし。
※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら
Armの場合、品種が大変多いせいか、同じARMv8アーキテクチャといっても、8.0、8.1、8.2…という感じで、バージョンアップがたびたびあり命令が追加されております。その中で実習には 8.0 (ARMv8p0)のCortex-A72を使っているので、v8の基本命令以外には手が出せませぬ。まあ、だだでさえ多い命令にさらに追加されている命令までやるのは辛いのでまあいいか、というところ。それに追加命令には「これでもRISC?」と思われる命令も多数(個人の感想です。)
整数乗算命令
さて今回から整数の乗算命令に入りたいと思います。例によって「本物」の命令と「エイリアス命令」が混在していて数が多いので、表(冒頭のアイキャッチ画像参照)にまとめました。
-
- 基本は4オペランド命令(表で–となっているところもエンコードはXZR相当)
- ソース1とソース2を掛け算して、第3のソースと足す/引くして、デスティネーションに書き込む(基本が積和算)
- その際のレジスタの幅(32、64ビット)と符合付、符号無で命令が分かれる
- 第3のソースに暗黙のゼロレジスタを置くことでエイリアスを実現していたりする(単純な乗算はエイリアス)
レジスタの使い方でおおきく以下の3パターンに分類できると思います。
MADD(MUL)、MSUB(MNEG)
最初のパターンは、3つのソース、1つのデスティネーションレジスタの幅がすべて同じものです。すべて32ビットのWレジスタを指定するか、すべて64ビットのXレジスタを指定します。
言語処理系では一般的な等ビット幅での処理ですが、掛け算の場合、32ビットx32ビットの結果は64ビットと倍幅にならなければ完全には収まらないです。このパターンでは上位半分を「捨てる」ことになります。上位を捨ててしまうので処理対象は符合無となります。
ソース1とソース2を掛けた結果を、第3のソースに加えるか、第3のソースから減じたものをデスティネーションに書き込みます。こんな感じ。
上記のA3のところに、ゼロレジスタ(32ビットではWZR、64ビットではXZR)を置くとエイリアス命令 MULとMNEGとなります(エイリアス命令の表記では第3ソースは記述しない。)
SMADDL、SMSUBL、UMADDL、UMSUBL
次のパターンは乗算としては自然な、32ビットのソース1とソース2を乗算した結果の64ビットを、第3ソースに加えるか、第3のソースから減じたものを64ビットのデスティネーションに書き込みます。
この形であれば符合付を扱えるので、命令にはSから始まる符合付とUから始まる符合無の種別があります。
やはりA3のところに、ゼロレジスタ(XZR)を置くとエイリアス命令 SMULL、SMNEGL、UMULL、UMNEGLとなります(エイリアス命令の表記では第3ソースは記述しない。)
SMULH、UMULH
最後は、64ビットレジスタと64ビットレジスタを乗じた結果の128ビットのうち、ビット127からビット64までの上位64ビットのみを取り出して64ビットのデスティネーションに書き込む命令です。符合付、符合無の種別があります。
下位の64ビットについては、ひとつ前のパターンのSMULL、UMULLエイリアスで計算可能なのでそちらを使え、ということだと思います。なお、SMULH、UMULHのHはHighのようですが、SMULL、UMULLのLはLongであってLowではないようです。コマケー話。