前回に続き、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命令も練習できたですが、命令は多く先は長いデス。