前回は、数多い条件選択命令群のうち「表」の4命令を動かしてみました。今回は「裏(エイリアス)」の5命令を動かしてみたいと思います。共通しているのは、「表」の命令と条件判断がひっくりかえっていること、そして本来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
条件選択命令、エイリアス
「表」の条件選択命令は、条件を判定し、真であれば第1ソースをデスティネーションにおくり、偽であったら第2ソースに命令により異なる「加工」をしてデスティネーションにおくるという命令でした。C言語などの3項演算子のような命令です。
上記を踏まえて条件の真偽を反転し、ソースオペランドに制約を加えると以下のエイリアス命令となります。
-
- CINC、第1ソースと第2ソースに同レジスタ指定の真偽反転CSINC命令
- CSET、第1ソースと第2ソースがゼロレジスタの真偽反転CSINC命令
- CINV、第1ソースと第2ソースに同レジスタ指定の真偽反転CSINV命令
- CSETM、第1ソースと第2ソースがゼロレジスタの真偽反転CSINV命令
- CNEG、第1ソースと第2ソースに同レジスタ指定の真偽反転CSNEG命令
実験用のアセンブリ言語ソースとアセンブル結果
今回の実験用アセンブラ関数は以下のステップです。
-
- cmp命令で第1引数と第2引数を比較し、条件を既知の状態にしておく
- 「表の」命令エンコードでの表現
- エイリアス命令としての表現
2と3は同一の命令を2つ重ねていることになります。逆アセンブル・ダンプにより2と3が一致していることを確かめます。
まずはCINC命令。
.globl cincW, cincX, csetW, csetX, cinvW, cinvX, csetmW, csetmX, cnegW, cnegX .text .balign 4 cincW: cmp w0, w1 csinc w0, w2, w2, NE cinc w0, w2, EQ ret cincX: cmp x0, x1 csinc x0, x2, x2, NE cinc x0, x2, EQ ret
その逆アセンブルリスト。逆アセンブル時には、「わかりにくい」表の命令表現でなく、エイリアス命令の表現が優先されるようです。
つづいて CSET命令。
csetW: cmp w0, w1 csinc w0, wzr, wzr, NE cset w0, EQ ret csetX: cmp x0, x1 csinc x0, xzr, xzr, NE cset x0, EQ ret
CINV命令。
cinvW: cmp w0, w1 csinv w0, w2, w2, NE cinv w0, w2, EQ ret cinvX: cmp x0, x1 csinv x0, x2, x2, NE cinv x0, x2, EQ ret
その逆アセンブル。
CSETM命令。
csetmW: cmp w0, w1 csinv w0, wzr, wzr, NE csetm w0, EQ ret csetmX: cmp x0, x1 csinv x0, xzr, xzr, NE csetm x0, EQ ret
その逆アセンブル。
CNEG命令。
cnegW: cmp w0, w1 csneg w0, w2, w2, NE cneg w0, w2, EQ ret cnegX: cmp x0, x1 csneg x0, x2, x2, NE cneg x0, x2, EQ ret
その逆アセンブル。
実験に使用したC言語ソース
各命令とも、W(32ビット・レジスタ)とX(64ビット・レジスタ)の2種類あります。それぞれについて真の場合と偽の場合を通してます。テストケース手抜きですが、一応触ったということで。
#include <stdio.h> #include <stdint.h> extern uint32_t cincW(uint32_t, uint32_t, uint32_t); extern uint64_t cincX(uint64_t, uint64_t, uint64_t); extern uint32_t csetW(uint32_t, uint32_t); extern uint64_t csetX(uint64_t, uint64_t); extern uint32_t cinvW(uint32_t, uint32_t, uint32_t); extern uint64_t cinvX(uint64_t, uint64_t, uint64_t); extern uint32_t csetmW(uint32_t, uint32_t); extern uint64_t csetmX(uint64_t, uint64_t); extern uint32_t cnegW(uint32_t, uint32_t, uint32_t); extern uint64_t cnegX(uint64_t, uint64_t, uint64_t); int main(void) { uint32_t uresult; uint64_t uresultX; uresult = cincW(9, 0, 2); printf ("cincW(9, 0, 2): %d\n", uresult); uresult = cincW(9, 9, 2); printf ("cincW(9, 9, 2): %d\n", uresult); uresultX = cincX(9, 0, 2); printf ("cincX(9, 0, 2): %ld\n", uresultX); uresultX = cincX(9, 9, 2); printf ("cincX(9, 9, 2): %ld\n", uresultX); uresult = csetW(9, 0); printf ("csetW(9, 0): %d\n", uresult); uresult = csetW(9, 9); printf ("csetW(9, 9): %d\n", uresult); uresultX = csetX(9, 0); printf ("csetX(9, 0): %ld\n", uresultX); uresultX = csetX(9, 9); printf ("csetX(9, 9): %ld\n", uresultX); uresult = cinvW(9, 0, 2); printf ("cinvW(9, 0, 2): %08x\n", uresult); uresult = cinvW(9, 9, 2); printf ("cinvW(9, 9, 2): %08x\n", uresult); uresultX = cinvX(9, 0, 2); printf ("cinvX(9, 0, 2): %016lx\n", uresultX); uresultX = cinvX(9, 9, 2); printf ("cinvX(9, 9, 2): %016lx\n", uresultX); uresult = csetmW(9, 0); printf ("csetmW(9, 0): %08x\n", uresult); uresult = csetmW(9, 9); printf ("csetmW(9, 9): %08x\n", uresult); uresultX = csetmX(9, 0); printf ("csetmX(9, 0): %016lx\n", uresultX); uresultX = csetmX(9, 9); printf ("csetmX(9, 9): %016lx\n", uresultX); return 0; }
実行確認
ビルドのコマンドラインは以下です。
gcc -g -O0 -o csel2 csel2.c csel2.s
実行結果が以下に。
なんてことはないですが、さらっと触るだけでも数が多いのでメンドイ。