ぐだぐだ低レベルプログラミング(66) ARM64(AArch64)、adc、加算キャリー付

Joseph Halfmoon

add系命令の5回目です。流石にそろそろaddを終わらせて次回は次の命令に行きたいと思います。addの最後を飾る?のはadc、キャリー付きのaddです。ソースオペランドレジスタ2個に加えてキャリーフラグの1ビットを加えるもの。この命令に関してだけは、オペランドのシフトも拡張もありません。良かった簡単で。

※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら

※動作確認は普及価格帯のAndroidスマホで行っています。Cortex-A73/Cortex-A53 各4コアの bigLITTLE 、64ビット動作です。Android上にインストールしたTermux環境にWindowsPCからSSH接続し、clang/llvmのツールチェーンでビルドしております。

アセンブラ・ソースコード

32ビットレジスタ幅の加算と、64ビットレジスタ幅の加算を行います。それに先立ってキャリーフラグを「立てる/下す」操作のためにadds命令を使っています。キャリーの操作だけなので、addsの方は32ビット幅で行っています。先行するaddsに与えるオペランドの値によりCY=1か0か決めてからターゲットのadc命令を実行するという目論見です。

.globl  adcW, adcX
.text
.balign 4

adcW:
        adds    w0, w1, w2
        adc     w0, w3, w4
        ret

adcX:
        adds    w0, w1, w2
        adc     x0, x3, x4
        ret
被テスト・アセンブラ関数を駆動するためのCソース

以下のCソースで、上記のアセンブラ関数を呼び出しています。

    • 関数の第1引数はダミー。上書きされてしまうのでなんでも良い
    • 関数の第2、第3引数のaddsの結果でCYを事前設定する
    • 関数の第4,第5引数がadcのソースオペランドとなる

4回テストしていますが、adc命令は、1+0xFFFFFFFE という同じ計算を行っています。

    • 1回目のadcはCY=1で32ビット幅
    • 2回目のadcはCY=0で32ビット幅
    • 3回目のadcはCY=1で64ビット幅
    • 4回目のadcはCY=0で64ビット幅

という設定です。

#include <stdio.h>

#define TSTV  (0xFFFFFFFE)

extern uint32_t adcW(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
extern uint64_t adcX(uint64_t, uint32_t, uint32_t, uint64_t, uint64_t);

int main(void)
{
        uint32_t result;
        uint64_t resultX;

        result = adcW(0, 2, TSTV, 1, TSTV);
        printf ("adcW 1+TSTV+CY1: %08x\n", result);
        result = adcW(0, 1, TSTV, 1, TSTV);
        printf ("adcW 1+TSTV+CY0: %08x\n", result);
        resultX = adcX(0, 2, TSTV, 1, TSTV);
        printf ("adcX 1+TSTV+CY1: %lx\n", resultX);
        resultX = adcX(0, 1, TSTV, 1, TSTV);
        printf ("adcX 1+TSTV+CY0: %lx\n", resultX);

        return 0;
}
ビルドと実行結果

ビルドするときのコマンドラインが以下に。

$ clang -g -O0 -o adc adc.c adc.s

キャリーフラグが本当に立っているか否かをまずはデバッガ(gdb)で確認してみました。以下のスクリーンキャプチャの赤枠内に C が含まれていればCYが立っており、C がなければ立ってません。

まずは1回目のadc実行直前。たっとります。

gdb_CYA

 

次に2回目のadc実行直前。おりてます。

gdb_NOCY

さて、実行結果(stdout)が以下に。

$ ./adc
adcW 1+TSTV+CY1: 00000000
adcW 1+TSTV+CY0: ffffffff
adcX 1+TSTV+CY1: 100000000
adcX 1+TSTV+CY0: ffffffff

以下全て符号無です。

1回目は 1+0xFFFFFFFE+CY(1) で32ビット値がクルリと回って0となりました。予定通り。

2回目は 1+0xFFFFFFFE+CY(0) で0xFFFFFFFFで止まりました。予定通り。

3回目は 1+0xFFFFFFFE+CY(1) ですが64ビット幅なので、結果は上位に突き抜けて0x100000000となりました。予定通り。

4回目は 1+0xFFFFFFFE+CY(0) で0xFFFFFFFFで止まりました。これまた予定通り。

とくに波乱もなく終了っと。

ぐだぐだ低レベルプログラミング(65) ARM64(AArch64)、レジスタ拡張付きadd へ戻る

ぐだぐだ低レベルプログラミング(67) ARM64(AArch64)、算術命令エイリアス#1 へ進む