MVEX エンコーディング

REX/VEXエンコーディングによって16本のレジスタx86命令中にエンコードすることに成功したAMD/Intelであったが、MICではzmmレジスタは32個あって困るのだった。


ModR/M,REX, VEX そして MVEXまで、またそのうち調べる気がするのでメモ。というかModR/Mとか、多分メモするの3回目くらいだがメモの意味とは…

ModR/M

x86命令はレジスタだけでなくメモリをオペランドに取れる。オペランドの種類が、命令の次に続くModR/Mというバイトにエンコードしてある。8bitのうち、2bitで種類を、3bit x 2 で、8本のレジスタのdst/srcのペアを指定する(この解説はかなり正しくないので、真面目にやる場合はIntel Manual 2Aの最初を見たほうがいい)

Mod Reg R/M
オペランド種類 レジスタ3bit レジスタ3bit
0 1 2 3 4 5 6 7

REX

64bit拡張するついでにレジスタも増やそうぜと思ったAMDは、いくつかのどうでもいい命令を無効命令にして、それのうち2個の命令をREX prefixとした。INC/DECは犠牲になったのだ…

$ cat fmt.s
        inc     %edx
        dec     %edx

$ as -o fmt.o -32 fmt.s
$ objdump -d fmt.o

fmt.o:     ファイル形式 elf32-i386


セクション .text の逆アセンブル:

00000000 <.text>:
   0:   42                      inc    %edx
   1:   4a                      dec    %edx
$ as -o fmt.o -64 fmt.s
$ objdump -d fmt.o

fmt.o:     ファイル形式 elf64-x86-64


セクション .text の逆アセンブル:

0000000000000000 <.text>:
   0:   ff c2                   inc    %edx
   2:   ff ca                   dec    %edx
}

INC/DECは、オペコードの中にレジスタを含めることができ、実際には、

  • inc ax, inc cx, inc dx ...
  • dec ax, dec cx, dec dx ...

という8x2個の合計16個の命令があると言ったほうがいい。これが無くなったことで、x86-64では、4bit分自由に使えるビットが増えた。REX prefixは、この4bitを使って

のふたつを行なった。

4bit それぞれに、W/R/X/Bという名前が付いていて、

  • W : 実行する演算の幅
  • R : ModR/M の R/M に +1bit
  • X : SIB の INDEX に +1bit
  • B : ModR/M の Reg に +1bit

で、

   0:   44 01 04 00             add    %r8d,(%rax,%rax,1) # WRXB = 0100 = 0x44
   4:   4c 01 04 00             add    %r8,(%rax,%rax,1)  # WRXB = 1100 = 0x4c
   8:   41 01 04 00             add    %eax,(%r8,%rax,1)  # WRXB = 0001 = 0x41
   c:   43 01 04 00             add    %eax,(%r8,%r8,1)   # WRXB = 0011 = 0x43

こんな感じになる。

VEX

これを見てIntelの人は、「お前また prefix 増やしたのかよクソが」とか思った…か、どうかは知らないが、(というかIntelが増やしたほうが多いが)

 66 44 0f 38 00 3c 00    pshufb (%rax,%rax,1),%xmm15
 ^  ^  ^  ^  ^  ^  ^
 |  |  |  |  |  |  SIB
 |  |  |  |  |  ModR/M
 |  |  |  |  opcode
 +--+--+--+ prefix

もはやプリフィクスのほうが命令より長いという状態になりつつあった。


あと、長いだけでなく、依存の長さも問題になる。上の7byteのうち、

  • 66 : Operand-size override prefix : 命令長に影響がある
  • 0f : なんかFP : 命令長に影響がある
  • 00 : opcode : 命令長に影響がある
  • 3c : ModR/M : 命令長に影響がある
  • 00 : SIB : 命令長に影響がある

5byteが命令長に影響を与えるという状態だった。こんなのを4並列同時デコードしてるのが今のx86である。


このプレフィクスをまとめようと努力した結果がVEX prefixなのだった。あと3オペランドになる。

 66 44 0f 38 00 3c 00    pshufb (%rax,%rax,1),%xmm15
 c4 62 01 00 3c 00       vpshufb (%rax,%rax,1),%xmm15,%xmm15

32bitでも実現できるようにするためだと思うが、REX prefixと違って、VEX prefixは、既存の命令を破壊していない。


Q. すでに1byte命令空間は全て埋まっているCPUがあります。これに1byte prefixを増やすにはどうすればよいでしょう?

A. よく見るとビットが余ってるしそれ使えばいい。


LES/LDS という命令があって、レジスタ間接アドレシングしてロードする命令なのだが、これは、レジスタオペランドにとれない

	lds	0, %eax      # 有効
	lds	(%ecx), %eax # 有効
	lds	%ecx, %eax   # 無効

つまり、ModR/MのModには、レジスタオペランド(11)が指定されることは無い。VEX prefixはそこを突いた。

VEX prefixの2byte目のこの上位2bitは、既存の命令とかぶらないように設計されてて、

  • この部分は、REXの R, X と対応する。
  • VEX の WRXB は REX の WRXB と反転してる

32bitモードだと、レジスタが8本しかないので、REXのR,Xは意味が無い。つまり、常に0。そして、VEXのWRXBは反転した値が入ってる。つまり、32bitモードでは、常に、この部分が11になり、64bitモードでは、LES/LDS命令は未定義なので、このビットを自由に使って16本のレジスタを指定できる。

と、なっている。


まあ、そういう努力によって、1byteのプレフィクスを増やして、x86はREXのWRXBを含めて16bit使えるビットを手に入れた。


この16bitのうちわけは、

  • WRXB : 4bit。REXのWRXBを反転したもの
  • pp : 2bit。66 とかの prefix
  • m-mmmm : 5bit。0f 38とか prefix をまとめてる。あと将来の拡張用に予約
  • L : 1bit。256bit 演算
  • vvvv : 4bit。3つめのオペランド

となる。これによって、REX prefixの機能に加えて、

  • 複雑なprefixの簡略化
  • 演算の256bit化
  • 3operand

を実現した。

MVEX

Larrabee とか KNC とか Knights Corner とか MIC とか Xeon Phi とか k1om とか呼ばれるものがあった。俺の名前はいっぱいあってな…


これ用にもっと拡張したくなったのか、MVEXというprefixが増えた。今回犠牲になったのは、bound命令である。bound命令は、配列境界を超えてないかどうかをチェックする命令である。「Java用に、配列チェックする命令があればいいんではないか」とか考える人は、x86を使うべきである。


bound命令もLES/LDSと一緒でメモリしかオペランドにとらないので、VEXと同じような形で利用されることになった。MVEXは、VEXよりもさらに1byte多い(1+3=4byte)。つまり8bit自由に使える。

詳細は、
http://software.intel.com/en-us/forums/topic/278102
の Instruction Set Reference Manual にある。KNC_goldとか書いてあるやつを展開して中のRPM入れればk1om用のbinutilsが使える。


8bitのうちわけは、

レジスタを32本にするため、最初の2byteもVEXから少し変更が入っている。

  • m-mmmm が mmmm になっている。かわりにRに+1bitするR'が増えている
  • SIB が無いとき、X がBに+1bitされるようになっている
  • L が0予約

これによって、

を実現している。