シフト演算命令 ()
シフト演算とは論理演算と同じようにビット列を操作する演算です。シフト演算命令には、単純な左右へのシフトの他に、複雑な動作をするビットフィールド移動命令があります。単純な左右へのシフトも内部的にはビットフィールド移動命令などで実現されているため、ここではビットフィールド移動命令もシフト演算のカテゴリーとして含めています。
シフト演算
シフト演算の基本は、レジスタのデータを左に1ビットずらす(シフト)と値は 2 倍になり、右に1ビットシフトすると値は 1/2 になることです。 乗除算より高速に実行できるため、うまく使うとプログラムを大幅に高速化できます。
サンプル
シフト演算のサンプルプログラムで実際に実行して試してみましょう。
shift.s
stdio.s を置いたディレクトリに、次のコードを shift.s というファイル名で作成します。 赤く表示されている命令がシフト演算の命令です。 16進数表示で「0x876543210fedcba9」、符号付きの10進数表示では「-8,690,466,096,661,279,831」となる64ビットの大きな数値を 8 ビットシフトして、値の変化を表示するプログラムとなっています。 この後の実行例では、赤く表示されている命令をコメントアウトされている命令の1つと入れ替えて、いろいろなシフト演算を試しています。
.include "stdio.s" .text .global _start num: .quad 0x876543210fedcba9 _start: ldr x2, num mov x0, x2 bl PrintHex16 bl NewLine mov x1, #22 bl PrintRight bl NewLine lsl x0, x2, #8 // asr x0, x2, #8 // lsr x0, x2, #8 // ror x0, x2, #8 // ror x0, x2, #64-8 // rol bl PrintHex16 bl NewLine mov x1, #22 bl PrintRight bl NewLine bl Exit
論理左シフト
論理左シフト(LSL)はデータを左側にずらし、空いた右側のビットに 0 が入ります。シフト量を n ビットとすると、2 の n 乗の乗算に相当します。次の例は「0x876543210fedcba9」という数値を 8 ビット論理左シフトした結果を示します。整数に 256 を乗算する計算に相当します。 左側にあふれるビットは捨てられます。この例では左側にあふれてしまうため、結果は256倍になりません。
// LSL x0, x2, #8 as -o lsl.o shift.s // アセンブル ld -o lsl lsl.o // リンク ./lsl // 実行 876543210FEDCBA9 // シフト前 -8690466096661279831 6543210FEDCBA900 // シフト後 7296712173568108800
算術右シフト
算術右シフトは(ASR)はデータを右側にずらし、空いた左側のビットには最上位ビットと同じビットが入ってきます。つまり、最上位ビットが 1 ならば左側から 1 が、0 ならば左側から 0 が入ってきます。シフト量を n ビットとすると、2 の n 乗の符号付き除算に相当します。次の例は「0x876543210fedcba9」という数値を 8 ビット算術右シフトした結果を示します。符号付き整数を256で除算する計算に相当します。シフト前の数値が正の整数の場合は、結果も正の整数となります。
// ASR x0, x2, #8 as -o asr.o shift.s // アセンブル ld -o asr asr.o // リンク ./asr // 実行 876543210FEDCBA9 // シフト前 -8690466096661279831 FF876543210FEDCB // シフト後 -33947133190083125
論理右シフト
論理右シフト (LSR)はデータを右側にずらし、空いた左側のビットに常に 0 が入ります。シフト量を n ビットとすると、2 の n 乗の符号無し除算に相当します。次の例は「0x876543210fedcba9」という数値を 8 ビット論理右シフトした結果を示します。符号無し整数を 256 で除算する計算に相当します。
// LSR x0, x2, #8 as -o lsr.o shift.s // アセンブル ld -o lsr lsr.o // リンク ./lsr // 実行 876543210FEDCBA9 // シフト前 -8690466096661279831 00876543210FEDCB // シフト後 38110460847844811
右ローテート
右ローテート命令(ROR)は 64 ビットの最上位と最下位のビットが環状につながっているとして、右にシフト(時計回り回転)します。次の例は「0x876543210fedcba9」という数値を 8 ビット右ローテートした結果を示します。右側にあふれ出る「A9」が左側に回り込んで入ってきます。
// ROR x0, x2, #8 as -o ror.o shift.s // アセンブル ld -o ror ror.o // リンク ./ror // 実行 876543210FEDCBA9 // シフト前 -8690466096661279831 A9876543210FEDCB // シフト後 -6230900220451885621
左ローテート
左ローテート命令はありませんが、右ローテート(ROR)のシフト量を (64 - ビット数) とすることで左ローテートを実現できます。 次の例は「0x876543210fedcba9」という数値を 8 ビット左ローテートした結果を示します。 左側にあふれ出る「87」が右側から入ってきます。
// ROR x0, x2, #64-8 as -o rol.o shift.s // アセンブル ld -o rol rol.o // リンク ./rol // 実行 876543210FEDCBA9 // シフト前 -8690466096661279831 6543210FEDCBA987 // シフト後 7296712173568108935
アドレッシングモード
シフト演算命令には、シフト量を定数で指定するイミディエートとレジスタで指定するモードの2種類のアドレッシングモードがあります。
イミディエート
汎用レジスタ Rn の内容を定数(6ビット)で指定したビット数だけシフトし、 結果を汎用レジスタ Rd に格納します。6ビットの定数では 0 から 63 までの定数が指定できます。
LSR Wd, Wn, #imm6 LSR Xd, Xn, #imm6
レジスタ
汎用レジスタ Rn を汎用レジスタ Rm で指定したビット数だけシフトし、 結果を汎用レジスタ Rd に格納します。
LSR Wd, Wn, Wm LSR Xd, Xn, Xm
ASR
ASR命令 (Arithmetic Shift Right) は算術右シフトを実行します。算術右シフトはRn レジスタの内容を Rm レジスタで指定したビット数だけ右にシフトします。 左側(最上位)のビットは符号ビットがコピーされます。 例えば、符号付整数 -10 を 1 ビット算術右シフトすると -5 となります。16進表記では 0xFFFFFFFFFFFFFFF6 を右に 1 ビットシフトすると 0xFFFFFFFFFFFFFFFB となります。 シフト量にレジスタを使う ASR 命令は ASRV の別名です。
ASR Wd, Wn, Wm // ASRV Wd, Wn, Wm と同じ ASR Xd, Xn, Xm // ASRV Xd, Xn, Xm と同じ
次の形式ではレジスタ Rn の内容を定数で指定したビット数だけ右にシフトしてレジスタ Rd に返します。
ASR Wd, Wn, #shift // SBFM Wd, Wn, #shift, #31 ASR Xd, Xn, #shift // SBFM Xd, Xn, #shift, #63
LSR
LSR命令 (Logical Shift Right)は論理右シフトを実行します。論理右シフトはレジスタ Rn の内容をレジスタ Rm で指定したビット数だけ右にシフトしてレジスタ Rd に返します。 左側(最上位)のビットには 0 が入ります。0xFFFFFFFFFFFFFFF6 は 0x7FFFFFFFFFFFFFFB となります。符号付整数の10進表記ではとすると -10 は1ビットの論理右シフトで 9223372036854775803 になります。 シフト量にレジスタを使う LSR 命令は LSRV の別名です。
LSR Wd, Wn, Wm // LSRV Wd, Wn, Wm と同じ LSR Xd, Xn, Xm // LSRV Xd, Xn, Xm と同じ
レジスタ Rn の内容を定数で指定したビット数だけ右にシフトしてレジスタ Rd に返します。
LSR Wd, Wn, #shift // UBFM Wd, Wn, #shift, #31 LSR Xd, Xn, #shift // UBFM Xd, Xn, #shift, #63
LSL
LSL命令 (Logical Shift Left)は論理左シフトを実行します。論理左シフトはレジスタ Rn の内容をレジスタ Rm で指定したビット数だけ左にシフトしてレジスタ Rd に返します 。右側(最下位)のビットには 0 が入ります。シフト量にレジスタを使う LSL 命令は LSLV の別名です。
LSL Wd, Wn, Wm // LSLV Wd, Wn, Wm と同じ LSL Xd, Xn, Xm // LSLV Xd, Xn, Xm と同じ
レジスタ Rn の内容を定数で指定したビット数だけ左にシフトしてレジスタ Rd に返します。
LSL Wd, Wn, #shift // UBFM Wd, Wn, #(-shift MOD 32), #(31-shift) LSL Xd, Xn, #shift // UBFM Xd, Xn, #(-shift MOD 64), #(63 - shift)
ROR
レジスタ Rn の内容をレジスタ Rm で指定したビット数(レジスタのビット数で除算した剰余)でローテイトした値をレジスタ Rd に返します。 ローテイトの結果右端からあふれたビットは、左から挿入されます。 ローテイトするビット数にレジスタを使う ROR 命令は RORV の別名です。
ROR Wd, Wn, Wm // RORV Wd, Wn, Wm と同じ ROR Xd, Xn, Xm // RORV Xd, Xn, Xm と同じ
レジスタ Rn の内容を定数で指定したビット数でローテイトした値をレジスタ Rd に返します。
ROR Wd, Ws, #shift // EXTR Wd, Ws, Ws, #shift ROR Xd, Xs, #shift // EXTR Xd, Xs, Xs, #shift
EXTR
レジスタ Wn とレジスタ Wm のペアを連結してデータのビット列を抽出します。定数には抽出する最下位のビット位置を指定します。
EXTR Wd, Wn, Wm, #lsb EXTR Xd, Xn, Xm, #lsb
分かり難いので、実際に試してみます。
num0: .quad 0xabcdefabcdefabcd num1: .quad 0x1234567890123456 // ldr x11, num0 ldr x12, num1 // extr x0, X11, x12, #8 // CD12345678901234 extr x0, X11, x12, #16 // ABCD123456789012 extr x0, X11, x12, #24 // EFABCD1234567890
上記のコードの extr命令では、x0 に右側のコメントの値が設定されます。 次の表のように x11 と x12 のレジスタ2つを連結したデータから、 右側から定数で指定したビット数だけ左にずらした位置のデータ (レジスタのビット数) が抽出されます。 ROR命令とする場合はレジスタを同じレジスタを並べることで実現しています。
x11 | x12 | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
a | b | c | d | e | f | a | b | c | d | e | f | a | b | c | d | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
CD12345678901234 | 8 | ||||||||||||||||||||||||||||||
ABCD123456789012 | 16 | ||||||||||||||||||||||||||||||
EFABCD1234567890 | 24 |
命令エンコード
命令 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ASR (sbfm imm) | z | 0 0 | 1 0 0 1 1 0 | N | immr | x 1 1 1 1 1 | Rn | Rd | ||||||||||||||||||||||||
ASRV | z | 0 | 0 | 1 1 0 1 0 1 1 0 | Rm | 0 0 1 0 | 1 0 | Rn | Rt | |||||||||||||||||||||||
LSR (ubfm imm) | z | 1 0 | 1 0 0 1 1 0 | N | immr | x 1 1 1 1 1 | Rn | Rd | ||||||||||||||||||||||||
LSRV | z | 0 | 0 | 1 1 0 1 0 1 1 0 | Rm | 0 0 1 0 | 0 1 | Rn | Rt | |||||||||||||||||||||||
LSL (ubfm imm) | z | 1 0 | 1 0 0 1 1 0 | N | immr | x 1 1 1 1 1 | Rn | Rd | ||||||||||||||||||||||||
LSLV | z | 0 | 0 | 1 1 0 1 0 1 1 0 | Rm | 0 0 1 0 | 0 0 | Rn | Rt | |||||||||||||||||||||||
RORV | z | 0 | 0 | 1 1 0 1 0 1 1 0 | Rm | 0 0 1 0 | 1 1 | Rn | Rt | |||||||||||||||||||||||
EXTR | z | 0 | 0 | 1 0 0 1 1 1 | N | 0 | Rm | imm6 | Rn | Rt |
ビットフィールドムーブ命令
レジスタ内の最下位のビットを含む連続したビット列(最も右側のビット列)を移動する命令です。 移動先レジスタをゼロクリアして 移動するビット列の最上位を符号フラグとして移動先レジスタの 上位ビットに符号をコピーする SBFM 命令、 移動先のレジスタの内容の対応するビット列だけ上書きして元の内容を保持する BFM 命令、移動先レジスタをゼロクリアしてビット列だけ を書き込む UBFM 命令があります。
次の例は、x2レジスタの下位24ビット「BCDEF0」を x3 レジスタに 3種類の命令で右から40ビットの位置が先頭になるようにビットフィールドムーブした場合の結果を示しています。
x2 : 923486789ABCDEF0 x3 : 0123456789ABCDEF (使われない) SBFM X3, X2, #40, #23 --> x3 : FFFFBCDEF0000000 x2 : 923486789ABCDEF0 x3 : BBBBBBBBBBBBBBBB (保持される) BFM X3, X2, #40, #23 --> x3 : BBBBBCDEF0BBBBBB x2 : 923486789ABCDEF0 x3 : 0123456789ABCDEF (使われない) UBFM X3, X2, #40, #23 --> x3 : 0000BCDEF0000000
アセンブラがビットフィールドムーブ命令に翻訳して実行する命令として シフト演算や符号拡張等の命令があります。ビットフィールドムーブ命令は 動作が複雑なので、SBFM、BFM、UBFM を実際に実行した結果も記載しました。 じっくり追ってみてください。
SBFM
この命令は Signed Bitfield Move の略で、ソースレジスタ(Wn、Xn)の 下位ビット側の任意の桁数 (imms+1) を、転送先レジスタの右シフト (immr)するビット位置にコピーします。 imms にはコピー元のビット列の最も左側のビット番号(0から63)を 指定します。immr には右にシフトする量(0から63)を指定します。 上位ビットには符号ビット、下位ビットには0が入ってきます。
SBFM Wd, Wn, #immr, #imms SBFM Xd, Xn, #immr, #imms
x2 に 0x823456789ABCDEF0 という値が入っている場合に移動するビット数(imm6s+1)、 右にローテイトするビット数(imm6r) にいろいろな値を入れた場合の SBFM命令を実行した結果を次に示します。移動するデータを 24 ビット とした場合は符号ビットが「1」、48ビットとした場合は符号ビットは 「0」となります。
x2 : 823456789ABCDEF0 x5 : 5555555555555555 SBFM X5, X2, #60, #23 --> FFFFFFFFFBCDEF00 SBFM X5, X2, #56, #23 --> FFFFFFFFBCDEF000 SBFM X5, X2, #60, #47 --> 00056789ABCDEF00 SBFM X5, X2, #56, #47 --> 0056789ABCDEF000 SBFM X5, X2, #24, #47 --> 000000000056789A SBFM X5, X2, #16, #23 --> FFFFFFFFFFFFFFBC SBFM X5, X2, #8, #23 --> FFFFFFFFFFFFBCDE
SBFM はアセンブラによって色々な命令から翻訳されて使われています。
ASR
ASR 命令 (Arithmetic Shift Right) は転送元レジスタの内容を定数で指定したビット数だけ右にシフトして転送先レジスタにコピーします。左側(最上位)のビットは符号ビットがコピーされます。
ASR Wd, Wn, #shift // SBFM Wd, Wn, #shift, #31 ASR Xd, Xn, #shift // SBFM Xd, Xn, #shift, #63
SBFIZ
SBFIZ (Signed Bitfield Insert in Zero) 命令は、転送元レジスタの最下位ビットのビット番号が lsb、幅 が width のビット列を転送先レジスタの下位にコピーします。
SBFIZ Xd, Xn, #lsb, #width // SBFM Wd, Wn, #(-lsb MOD 32), #(width-1) SBFIZ Wd, Wn, #lsb, #width // SBFM Xd, Xn, #(-lsb MOD 64), #(width-1)
SBFX
SBFX (Signed Bitfield Extract) 命令は、転送元レジスタの最下位ビットのビット番号が lsb、幅 が width のビット列を取り出して符号拡張して転送先レジスタにコピーします。
SBFX Wd, Wn, #lsb, #width // SBFM Wd, Wn, #lsb, #(lsb+width-1) SBFX Xd, Xn, #lsb, #width // SBFM Xd, Xn, #lsb, #(lsb+width-1)
SXTB
SXTB (Signed Extend Byte) 命令は転送元レジスタの下位8ビットを符号拡張して転送先レジスタにコピーします。
SXTB Wd, Wn // SBFM Wd, Wn, #0, #7 SXTB Xd, Wn // SBFM Xd, Xn, #0, #7
SXTH
SXTH (Sign Extend Halfword) 命令は転送元レジスタの下位16ビットを符号拡張して転送先レジスタにコピーします。
SXTH Wd, Wn // SBFM Wd, Wn, #0, #15 SXTH Xd, Wn // SBFM Xd, Xn, #0, #15
SXTW
SXTW (Sign Extend Word) 命令は転送元レジスタの下位32ビットを符号拡張して転送先レジスタにコピーします。
SXTW Xd, Wn // SBFM Xd, Xn, #0, #31
BFM
この命令は Bitfield Move の略で、ソースレジスタ(Wn、Xn)の 下位ビット側の任意の桁数 (imms+1) を、転送先レジスタの 右シフト(immr)したビット位置にコピーします。 imms にはコピー元のビット列の最も左側のビット番号(0から63)を 指定します。immr には右にシフトする量(0から63)を指定します。 転送先レジスタの他のビットは変化しません。
BFM Wd, Wn, #imm6r, #imm6s // sf = 0 && N = 0 BFM Xd, Xn, #imm6r, #imm6s // sf = 1 && N = 1
結果を返す x5 に 0x5555555555555555、x2 に 0x823456789ABCDEF0 という値が 入っている場合に移動するビット数(imm6s+1)、 右にローテイトするビット数(imm6r) にいろいろな値を入れた場合の BFM命令を実行した結果を次に示します。
x2 : 823456789ABCDEF0 x5 : 5555555555555555 BFM X5, X2, #60, #23 --> 555555555BCDEF05 BFM X5, X2, #56, #23 --> 55555555BCDEF055 BFM X5, X2, #60, #47 --> 55556789ABCDEF05 BFM X5, X2, #56, #47 --> 5556789ABCDEF055 BFM X5, X2, #24, #47 --> 555555555556789A BFM X5, X2, #16, #23 --> 55555555555555BC BFM X5, X2, #8, #23 --> 555555555555BCDE
x5 に最初に格納されていた値 (0x5555555555555555) がBFM命令実行後も 残っていることが確認できます。
BFM もアセンブラによって翻訳されて使われます。
BFI
BFI命令 (Bitfield Insert) は、転送元レジスタの指定した幅(width)の下位ビットを転送先レジスタの転送位置となる最下位ビットのビット番号(Least-significant bit) を指定してコピーします。
<--- width ---> before -----------------bbbbbbbbbbbbbbb <--- width ---> lsb after ------------bbbbbbbbbbbbbbb-----
BFI Wd, Wn, #lsb, #width // BFM Wd, Wn, #(-lsb MOD 32), #(width-1) BFI Xd, Xn, #lsb, #width // BFM Xd, Xn, #(-lsb MOD 64), #(width-1)
BFXIL
BFXIL命令 (Bitfield eXtract and Insert at Low end) は、転送元レジスタの最下位ビットのビット番号が lsb 幅 が width のビット列を転送先レジスタの下位にコピーします。BFI と逆の動作となります。
<--- width ---> lsb before ------------bbbbbbbbbbbbbbb----- <--- width ---> after -----------------bbbbbbbbbbbbbbb
BFXIL Wd, Wn, #lsb, #width // BFM Wd, Wn, #lsb, #(lsb+width-1) BFXIL Xd, Xn, #lsb, #width // BFM Xd, Xn, #lsb, #(lsb+width-1)
UBFM
この命令は Unsigned Bitfield Move の略で、ソースレジスタ(Wn、Xn)の 下位ビット側の任意の桁数 (imms+1) を、デスティネーションレジスタの 右シフト(immr)したビット位置にコピーします。 imms にはコピー元のビット列の最も左側のビット番号(0から63)を 指定します。immr には右にシフトする量(0から63)を指定します。 コピー先の前後のビットは 0 になります。
UBFM Wd, Wn, #imm6r, #imm6s // sf = 0 && N = 0 UBFM Xd, Xn, #imm6r, #imm6s // sf = 1 && N = 1
x2 に 0x823456789ABCDEF0 という値が入っている場合に移動するビット数(imm6s+1)、 右にローテイトするビット数(imm6r) にいろいろな値を入れた場合の UBFM命令を実行した結果を次に示します。
x2 : 823456789ABCDEF0 x5 : 5555555555555555 UBFM X5, X2, #60, #23 --> 000000000BCDEF00 UBFM X5, X2, #56, #23 --> 00000000BCDEF000 UBFM X5, X2, #60, #47 --> 00056789ABCDEF00 UBFM X5, X2, #56, #47 --> 0056789ABCDEF000 UBFM X5, X2, #24, #47 --> 000000000056789A UBFM X5, X2, #16, #23 --> 00000000000000BC UBFM X5, X2, #8, #23 --> 000000000000BCDE
LSL
LSL命令 (Logical Shift Left) は転送元レジスタの内容を定数で指定したビット数だけ左にシフトしてレジスタに返します。
LSL Wd, Wn, #shift // UBFM Wd, Wn, #(-shift MOD 32), #(31-shift) LSL Xd, Xn, #shift // UBFM Xd, Xn, #(-shift MOD 64), #(63-shift)
LSR
LSR 命令は転送元レジスタの内容を定数で指定したビット数だけ右にシフトしてレジスタに返します。
LSR Wd, Wn, #shift // UBFM Wd, Wn, #shift, #31 LSR Xd, Xn, #shift // UBFM Xd, Xn, #shift, #63
UBFIZ
UBFIZ (Unsigned Bitfield Insert in Zero) 命令は転送先レジスタを0にした後、転送元レジスタの最下位ビットのビット番号が lsb、幅 が width のビット列を転送先レジスタの下位にコピーします。
UBFIZ Wd, Wn, #lsb, #width // UBFM Wd, Wn, #(-lsb MOD 32), #(width-1) UBFIZ Xd, Xn, #lsb, #width // UBFM Xd, Xn, #(-lsb MOD 64), #(width-1)
UBFX
UBFX (Unsigned Bitfield Extract) 命令は転送先レジスタを0にした後、、転送元レジスタの最下位ビットのビット番号が lsb、幅 が width のビット列を転送先レジスタの下位にコピーします。コピーするビット列をゼロ拡張して転送する動作となります。
UBFX Wd, Wn, #lsb, #width // UBFM Wd, Wn, #lsb, #(lsb+width-1) UBFX Xd, Xn, #lsb, #width // UBFM Xd, Xn, #lsb, #(lsb+width-1)
UXTB
UXTB (Unsigned Extend Byte) 命令は転送元レジスタの下位8ビットをゼロ拡張して転送先レジスタにコピーします。
UXTB Wd, Wn // UBFM Wd, Wn, #0, #7
UXTH
UXTH (Sign Extend Halfword) 命令は転送元レジスタの下位16ビットをゼロ拡張して転送先レジスタにコピーします。
UXTH Wd, Wn // UBFM Wd, Wn, #0, #15
命令エンコード
命令 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
SBFM | z | 0 0 | 1 0 0 1 1 0 | N | immr | imms | Rn | Rd | ||||||||||||||||||||||||
BFM | z | 0 1 | 1 0 0 1 1 0 | N | immr | imms | Rn | Rd | ||||||||||||||||||||||||
UBFM | z | 1 0 | 1 0 0 1 1 0 | N | immr | imms | Rn | Rd |
次回は整数を扱う命令の最後として分岐命令、条件実行命令などを予定しています。