前回はシフト、ローテイトの落穂ひろいでした。今回は先行する(MSB側の)ビットカウントを行う命令です。時々「こういう操作」が必要なことがあり、「こういう専用命令」が無いと時間がかかるので有った方が嬉しい気もするのです。しかしArmはRISCの割には複雑な感じを醸しだしてる気もします(個人の感想です。)
※「ぐだぐだ低レベルプログラミング」投稿順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
CLSとCLZ命令
ビットカウントといった場合、「変数(レジスタ)の中で1が立っているビットの数を数える」ことを指すことがありますが、ここではソースレジスタの「先行する」ビットを数えるという前提です。数えた結果はデスティネーションレジスタに格納します。
-
- CLS (Count Leading Sign bits)
- CLZ (Count Leading Zero bits)
以下に32ビットのソースレジスタ(Wx)に対してCLS、CLZ命令を適用した場合の数えるビット範囲のイメージを図にしています。CLSは緑のMSBと一致する黄緑のビット数を数えます。MSBと不一致となる青色ビットで数えるのを止めます。CLZはともかくMSB側から0を数えていき、1を発見したら止めます。
注意すべきなのは、サインビットを数えるCLS命令の場合、レジスタ先頭のMSBと一致する先行ビットの数を数えるのですが、数えるときにMSBは含まれない、というお約束であることです。
それに対してCLZ命令ではべたに数えるので、MSBも0であれば数に含まれます(MSBが0でなければ結果は0です。)
実験用アセンブリ言語ソース
いつもと違い、エイリアス命令などは登場しないので、ただ、32ビット(W)と64ビット(X)の違いだけです。1関数=1命令の実験です。
.globl clsW, clsX, clzW, clzX .text .balign 4 clsW: cls w0, w1 ret clsX: cls x0, x1 ret clzW: clz w0, w1 ret clzX: clz x0, x1 ret
アセンブル関数を呼び出してテストするCのコード
例によって「とりあえず各命令1例だけ」触ってみるコードが以下に。結果は分かり易いように10進表示です。
#include <stdio.h> #include <stdint.h> extern uint32_t clsW(uint32_t, int32_t); extern uint64_t clsX(uint64_t, int64_t); extern uint32_t clzW(uint32_t, uint32_t); extern uint64_t clzX(uint64_t, uint64_t); int main(void) { uint32_t result; uint64_t resultX; result = clsW(0, -256); printf ("clsW: %d\n", result); resultX = clsX(0, -256); printf ("clsX %ld\n", resultX); result = clzW(0, 0x87); printf ("clzW: %d\n", result); resultX = clzX(0, 0x87); printf ("clzX %ld\n", resultX); return 0; }
ビルドして実行
コマンドラインが以下に。
gcc -g -O0 -o clsclz clsclz.c clsclz.s
実行結果が以下に。
CLSとCLZの数え方の違いが出ておりますな。