
前回に続き、A64の論理演算命令群、AND一族の2回目です。今回は演算結果をフラグに反映する「S」のついた命令どもをエクササイズしてみます。またands命令の「エイリアス」、tst命令についてもそのコード生成確認とともに練習してみます。Arm64ビットのクセに慣れればこれはこれでらくちん。
※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら
※動作確認は普及価格帯のAndroidスマホで行っています。Cortex-A73/Cortex-A53 各4コアの bigLITTLE 64ビット。Android上にインストールしたTermux環境にWindowsPCからSSH接続し、clang/llvmのツールチェーンでビルドしております。
今回練習するアセンブラコード
冒頭のアイキャッチ画像にAND一族の表を掲げてありますが、今回練習する命令はその赤枠の部分です。
-
- 1命令=1関数のスタイルの「ミニマム」なコードです。
- フラグに結果が反映された演算結果を戻り値にするためにcset しています(今気が付きましたが一部x0とすべきところw0としてしまってますが、結果変わらないのでオーライと。どこがオーライ?)
- 即値をとる命令については即値のとりえる最大幅のオール1とのANDをとるようにしています。
- TST命令については、ことさらに同じ機械語コードが生成されるANDS命令をまずおいて、その直後にTST命令をおいてあります。後で同じコードが生成されていることを確認します。
アセンブリ言語ソースが以下に
.globl andsImmW, andsImmX, tstImmW, tstImmX, andsW, andsX, tstW, tstX, bicsW, bicsX
.text
.balign 4
andsImmW:
ands w0, w1, #0x1FFF
cset w0, EQ
ret
andsImmX:
ands x0, x1, #0x3FFF
cset w0, EQ
ret
tstImmW:
ands wzr, w1, #0x1FFF
tst w1, #0x1FFF
cset w0, EQ
ret
tstImmX:
ands xzr, x1, #0x3FFF
tst x1, #0x3FFF
cset w0, EQ
ret
andsW:
ands w0, w1, w2
cset w0, EQ
ret
andsX:
ands x0, x1, x2
cset x0, EQ
ret
tstW:
ands wzr, w1, w2
tst w1, w2
cset w0, EQ
ret
tstX:
ands xzr, x1, x2
tst x1, x2
cset x0, EQ
ret
bicsW:
bics w0, w1, w2
cset x0, EQ
ret
bicsX:
bics x0, x1, x2
cset x0, EQ
ret
TST imm命令の以下の記述ペアが同一の機械語になることの確認
ands wzr, w1, #0x1FFF
tst w1, #0x1FFF
ands xzr, x1, #0x3FFF
tst x1, #0x3FFF
TST 命令の以下の記述ペアが同一の機械語になることの確認
ands wzr, w1, w2
tst w1, w2
ands xzr, x1, x2
tst x1, x2
実験に使用したC言語ソース
上記のアセンブリ言語ソースで定義された関数を呼び出して結果を観察するためのC言語コードが以下に。1命令につき1ケースしかテストしていない「ほぼほぼザル」なコードです。よゐこは真似してはいけないヤツ。
#include <stdio.h>
extern uint32_t andsImmW(uint32_t, uint32_t);
extern uint64_t andsImmX(uint64_t, uint64_t);
extern uint32_t andsW(uint32_t, uint32_t, uint32_t);
extern uint64_t andsX(uint64_t, uint64_t, uint32_t);
extern uint32_t tstImmW(uint32_t, uint32_t);
extern uint64_t tstImmX(uint64_t, uint64_t);
extern uint32_t tstW(uint32_t, uint32_t, uint32_t);
extern uint64_t tstX(uint64_t, uint64_t, uint32_t);
extern uint32_t bicsW(uint32_t, uint32_t, uint32_t);
extern uint64_t bicsX(uint64_t, uint64_t, uint32_t);
int main(void)
{
int32_t result;
int64_t resultX;
result = andsImmW(0, 0xE000);
printf ("andsImmW(0, 0xE000): EQ=0x%x\n", result);
resultX = andsImmX(0, 0xC000);
printf ("andsImmX(0, 0xC000): EQ=0x%lx\n", resultX);
result = tstImmW(0, 0xE000);
printf ("tstImmW(0, 0xE000): EQ=0x%x\n", result);
resultX = tstImmX(0, 0xC000);
printf ("tstImmX(0, 0xC000): EQ=0x%lx\n", resultX);
result = andsW(0, 0xFFFFFFFE, 1);
printf ("andsW(0, 0xFFFFFFFE, 1): EQ=0x%x\n", result);
resultX = andsX(0, 0x1FFFFFFFE, 1);
printf ("andsX(0, 0x1FFFFFFFE, 1): EQ=0x%lx\n", resultX);
result = tstW(0, 0xFFFFFFFE, 1);
printf ("tstW(0, 0xFFFFFFFE, 1): EQ=0x%x\n", result);
resultX = tstX(0, 0x1FFFFFFFE, 1);
printf ("tstX(0, 0x1FFFFFFFE, 1): EQ=0x%lx\n", resultX);
result = bicsW(0, 0xFFFFFFFE, 1);
printf ("bicsW(0, 0xFFFFFFFE, 1): EQ=0x%x\n", result);
resultX = bicsX(0, 0x1FFFFFFFE, 1);
printf ("bicsX(0, 0x1FFFFFFFE, 1): EQ=0x%lx\n", resultX);
return 0;
}
ビルドして実行
例によってTermux上でのビルドは clang 使用です。
$ clang -g -O0 -o ands ands.c ands.s
実行結果が以下に。
予定通りの結果が得られましたぞ。今回は10命令も練習できたですが、命令は多く先は長いデス。



