前回は、即値でシフトのビット数を指定できるSIMD命令のあまりの多さに、その特徴を表に整理するところで力つきました。ようやく今回から実機練習に戻ります。まずは「一番簡単な」シフトから。まあ後に控えている奴らを考えたら素直で分かり易いです。単純な左シフトと右シフトです。対称な2命令かと思えば、A64がそんな筈ないっと。
※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら
※実機動作確認には以下を使用しております。
-
- Raspberry Pi 4 model B、Cortex-A72コア(ARMv8-A)
- Raspberry Pi OS (64bit) bullseye
- gcc (Debian 10.2.1-6) 10.2.1 20210110
ARMv8もいろいろレベルがあり、Arm Cortex-A72はARMv8の中でもベーシックな(命令数の少ない)ARMv8p0です。
※A64の最新のマニュアルは以下でダウンロード可能です。
Arm Architecture Reference Manual for A-profile architecture
今回練習の3命令
今回練習の3命令を前回の大きな表から抜き出すと以下のようです。
Lft(左シフト)にX印がついていて後は何も属性のないSHL命令こそ、一族の中で一番素直な左シフト命令です。一方Rgt(右シフト)にX印がついている方は2命令あります。SSHRとUSHRです。SSHRはSgn(符号付)、USHRはUsn(符号無)ということでデータの符号によって動作が異なります。このぐらい、可愛いもんか。
実験につかったアセンブリ言語記述の被テスト関数
例によって手抜きの関数プロローグ、エピローグ無の被テスト関数のソースが以下に。単純な命令なので、SIMD要素の幅は、バイト、ハーフワード、ワード、ダブルワードをとれるのですが、例によって手抜きでワード(32ビット)のみ練習してます。シフト量は16ビットきめうちです。まあ4ビットの倍数がHEX表記したときに見やすいからね。。。
.globl shl4V, sshr4V, ushr4V .text .balign 4 shl4V: ld1 {v0.4S, v1.4S}, [x0] shl v0.4S, v1.4S, #16 st1 {v0.4S}, [x0] ret sshr4V: ld1 {v0.4S, v1.4S}, [x0] sshr v0.4S, v1.4S, #16 st1 {v0.4S}, [x0] ret ushr4V: ld1 {v0.4S, v1.4S}, [x0] ushr v0.4S, v1.4S, #16 st1 {v0.4S}, [x0] ret
C言語記述のmain関数
上記のアセンブリ言語関数を呼び出すmain関数が以下に。SSHRは符号付、USHRは符号無と扱うデータの本来の素性は異なりますが、アセンブラにまで来てもらえれば、C言語レベルの変数型などなすすべもなし、ということで、C言語レベルでは皆 uint32_t で宣言してます。手抜き。
#include <stdio.h> #include <stdint.h> #include <math.h> #define MAXMEM (8) uint32_t TargetMEM[MAXMEM]; extern void shl4V(uint32_t *); extern void sshr4V(uint32_t *); extern void ushr4V(uint32_t *); void initTGT() { TargetMEM[0] = 0x0; TargetMEM[1] = 0x0; TargetMEM[2] = 0x0; TargetMEM[3] = 0x0; TargetMEM[4] = 0x07654321; TargetMEM[5] = 0x87654321; TargetMEM[6] = 0x5A5A5A5A; TargetMEM[7] = 0xFFFFFFFF; } void dumpTGT(const char *arg) { printf("%s\n", arg); for (int i=0; i<4; i++) { printf("%02d: %08x\n", i, TargetMEM[i]); } } int main(void) { initTGT(); shl4V(TargetMEM); dumpTGT("shl v0.4S, V1.4S, #16"); initTGT(); sshr4V(TargetMEM); dumpTGT("sshr v0.4S, V1.4S, #16"); initTGT(); ushr4V(TargetMEM); dumpTGT("ushr v0.4S, V1.4S, #16"); return 0; }
実機実行結果の確認
以下のようにしてビルドして実行しています。
$ gcc -g -O0 simdSFTImm2.c simdSFTImm2.s $ ./a.out
予定通りの実行結果よの。左シフトは符号など関係なく下からゼロ詰め。右シフトは、符号付きなら符号ビットが保存されるし、符号無ならゼロ詰めされるっと。このくらいならお惚け老人も間違えることはあるまいて。でも次回からが思いやられるな。