
前回は、数多い条件選択命令群のうち「表」の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
実行結果が以下に。
なんてことはないですが、さらっと触るだけでも数が多いのでメンドイ。





