ぐだぐだ低レベルプログラミング(87)ARM64(AArach64)、整数乗算命令その3

Joseph Halfmoon

RISC-Vのとき基本整数命令セットを一当たりなめるのは大した回数でなかった記憶。しかしArmの64ビット命令セットA64は命令多いです。まだ整数乗算命令です。今回は前回やってない「パターン2」と「パターン3」ですが符合無のみ。符合付も一度にやるとちょっと疲れそうだったから。そういうところで回数多くなっているな、オイ。

※「ぐだぐだ低レベルプログラミング」投稿順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です。

今回実習の符合無整数乗算命令

前回は、ソースもデスティネーションもすべて同じビット幅の「パターン1」(当方で勝手に名付けてます)でした。ソースが32ビットなら32ビットの結果、ソースが64ビットなら64ビットの結果でした。今回は「パターン2」と「パターン3」を試してみます。前々回の説明で使った図を再掲します。

パターン2

SMADDL_UMADDL

パターン3

SMULH_UMULH

パターン2もパターン3も符合付、符合無あるのですが、符合付は「疲れる」ので今回は符合無のみです。ニーモニックでいうと以下の5つ。

    • UMADDL
    • UMULL
    • UMSUBL
    • UMNEGL
    • UMULH

例によって赤字の2つはエイリアスなんでありますが。

実験用のアセンブリ言語ソース

例によって手抜き(関数プロローグもエピローグもない)な、ほぼ1命令1関数スタイルです。エイリアス命令については、元の命令表記と、エイリアス表記を2つ並べてあります。後でディスアセンブルして生成されたコードをみるため。

なお、umaddlとumsublは4オペランド命令ですが、簡単のため、デスティネーションと第3ソースは同じX0レジスタにしてあります。この方が「積和」な感じがするから?

.globl	umaddlX, umullX, umsublX, umneglX, umulhX
.text
.balign	4

umaddlX:
    umaddl x0, w1, w2, x0
    ret

umsublX:
    umsubl x0, w1, w2, x0
    ret

umullX:
    umaddl x0, w1, w2, xzr
    umull  x0, w1, w2
    ret

umneglX:
    umsubl x0, w1, w2, xzr
    umnegl x0, w1, w2
    ret

umulhX:
    umulh x0, x1, x2
    ret

エイリアス命令使っている umullXとumneglX関数の逆アセンブル結果が以下です。上記のソースでは、xzrレジスタを第4オペランドに持つ命令が、エイリアスと解釈されて3オペランドに見えてるのがわかります。

aliasDisasm

実験に使用したC言語ソース

上記のアセンブリ言語関数を呼び出すテスト用のCソースが以下に。

#include <stdio.h>
#include <stdint.h>

extern uint64_t umaddlX(uint64_t, uint32_t, uint32_t);
extern uint64_t umsublX(uint64_t, uint32_t, uint32_t);
extern uint64_t umullX(uint64_t, uint32_t, uint32_t);
extern uint64_t umneglX(uint64_t, uint32_t, uint32_t);
extern uint64_t umulhX(uint64_t, uint64_t, uint64_t);

int main(void)
{
    uint32_t uresult;
    uint64_t uresultX;

    uresultX = umaddlX(0x100000000, 0x80000000, 16);
    printf ("umaddlX(0x100000000, 0x80000000, 16): %16lx\n", uresultX);
    uresultX = umsublX(0x800000000, 0x80000000, 16);
    printf ("umsublX(0x800000000, 0x80000000, 16): %16lx\n", uresultX);

    uresultX = umullX(0, 0x80000000, 16);
    printf ("umullX(0, 0x80000000, 16): %16lx\n", uresultX);
    uresultX = umneglX(0, 0x80000000, 16);
    printf ("umnegX(0, 0x80000000, 16): %16lx\n", uresultX);

    uresultX = umulhX(0, 0x00000000FFFFFFFE, 0x00000000FFFFFFFE);
    printf ("umulhX(0, 0x00000000FFFFFFFE, 0x00000000FFFFFFFE): %16lx\n", uresultX);
    uresultX = umullX(0, 0xFFFFFFFE, 0xFFFFFFFE);
    printf ("umullX(0, 0xFFFFFFFE, 0xFFFFFFFE): %16lx\n", uresultX);

    return 0;
}
ビルドと実行

ビルドのコマンドラインが以下に。

gcc -g -O0 umul.c umul.s

実行したところが以下に。

umulResults

符合無の16進表記だと、桁が多くて目が回りそうですが、あっているハズ。ホントか?

ぐだぐだ低レベルプログラミング(86)ARM64(AArach64)、整数乗算命令その2 へ戻る

ぐだぐだ低レベルプログラミング(88)\ARM64(AArach64)、整数乗算命令その4 へ進む