ぐだぐだ低レベルプログラミング(166)ARM64(AArach64)SIMD即値シフト2

Joseph Halfmoon

前回は、即値でシフトのビット数を指定できる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命令を前回の大きな表から抜き出すと以下のようです。SIMD_SFTIMM_2

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

実行結果が以下に。SIMD_SFTIMM_2_RESLUTS

予定通りの実行結果よの。左シフトは符号など関係なく下からゼロ詰め。右シフトは、符号付きなら符号ビットが保存されるし、符号無ならゼロ詰めされるっと。このくらいならお惚け老人も間違えることはあるまいて。でも次回からが思いやられるな。

ぐだぐだ低レベルプログラミング(165)ARM64(AArach64)SIMD即値シフト へ戻る

ぐだぐだ低レベルプログラミング(167)ARM64(AArach64)SIMD即値シフト3 へ進む