AMD64での高速化


アセンブリ言語を使うメリットは、処理のスループットが上がることだけではない。アセンブリ言語はあらゆる操作を表現できるため、大きな既成のライブラリを使わなくてもよい。アセンブリ言語レベルで無駄を省けば、不必要なライブラリルーチンや、既成のライブラリとつじつまを合わせるための余計なコードを含まない、非常にコンパクトなプログラムになる。小さなプログラムは外部記憶装置からのロードが速く、キャッシュにも載りやすいので、あらゆる操作が軽くなる。

アセンブリ言語のプログラムは、実行速度に関して処理系の影響を受けにくく、実行環境からの影響は直接的でわかりやすい。そのため、実行時間の予測がしやすく、時間のかかる部分を容易に発見して修正することができる。また、ソースコードを表示できるような高級なデバッガが使えないときでも、デバッグが簡単である。

アセンブリ言語でプログラム全体を記述するなら、そこには高級言語で見えたのとは別の世界が広がっている。

  1. はじめに
  2. x86からの拡張の概要
  3. 搭載プロセッサ
  4. 主な命令一覧(準備中)
  5. 参考文献

1. はじめに

AMD64は、当初はx86-64と呼ばれていた、x86を拡張する命令セットアーキテクチャである。64ビット演算が可能なモードでも、多くのx86命令はそのまま64ビットに拡張されて実行される。

この文章では、AMD64がx86から拡張された部分の概要を説明した後、AMD64を実装するプロセッサの特性を紹介する。


2. x86からの拡張の概要

2.1 モード

AMD64には、大きく分けて二つのモードがある。legacyモードは、x86と互換のモード(拡張された機能は使えない)である。AMD64で追加されたlongモードは、さらに二つのサブモードに分かれる。compatibilityモードは、x86の16/32ビットプロテクトモードと互換性のあるモードである。64ビット命令が使えるのは、64ビットモードだけである。

longモードでは、割り込みハンドラは64ビットモードで呼び出され、ページテーブルやTSSの構造もlegacyモードとは異なるため、longモード用のシステムソフトウェアを用意する必要がある。システムソフトウェアで適切にサポートするなら、compatibilityモードで4GBを超える物理メモリを使用したり、x86のプロテクトモード用のアプリケーションプログラムと64ビットモード用のアプリケーションプログラムを並行して実行させたりすることができる。

各モードの特徴をまとめると次のようになる。
モードアドレスサイズオペランドサイズ汎用レジスタ数物理アドレス空間使用可能なリニアアドレス空間セグメントベースアドレスセグメントリミット
longモード64ビットモード64/3232/16/64,8162^52 #12^48 #10 #22^64-1
compatibilityモード3232/1632/16,882^32セグメントディスクリプタによる
1616/3216/32,8
legacyモードプロテクトモード3232/1632/16,82^32/2^40/2^52 #3
1616/3216/32,8
仮想86モード2^20+HMAセグメント値×162^16-1
リアルモード2^20+HMA #4
#1 物理アドレスと仮想アドレスのビット数はCPUID命令のEAX=80000008hで得られる。
#2 ES,CS,SS,DSは0。FS,GSはセグメントディスクリプタまたはモデル固有レジスタで指定。
#3 x86では、ページサイズ拡張または物理アドレス拡張を有効にすると2^36になるが、AMD64のlegacyモードではそれぞれ2^40、2^52になる。
#4 リアルモードの特殊な状態を使うと、2^32になる。

各モードの区別は次のビットで行う。

longモード中のサブモードの切り替えは、適切なセグメントディスクリプタを用意しておけば、JMP,CALL,RET命令の実行や割り込みハンドラの起動で行える。legacyモードとlongモードの切り替えは少々複雑である。例えば、legacyモード中のリアルモードからlongモード中のcompatibilityモードに切り替えるには、次のような手順を経る必要がある。 longモードを本格的に使うには、この後IDTRを設定する必要があるし、ディスクリプタテーブルなどを4GBを超える物理アドレスに置きたければ、64ビットモードで特権命令を使って設定する必要がある。

以下の各節では、主に64ビットモードで追加・拡張された機能について解説する。

2.2 レジスタ

64ビットモードでは、汎用レジスタは64ビットに拡張されただけでなく、数も2倍の16本になった。XMMレジスタも16本になったが、FPUデータレジスタおよびMMXレジスタは8本のままである。

64ビットの汎用レジスタはRAX,RCX,…で、下位半分が従来のEAX,ECX,…に相当する。追加された8本の汎用レジスタは、R8,R9,…,R15で、下位32ビットがR8D,R9D,…、下位16ビットがR8W,R9W,…、下位8ビットがR8B,R9B,…である。なお、SP,BP,SI,DIの下位8ビットは、SPL,BPL,SIL,DILとして使うことができる(後述のREXプリフィックスを使用したとき)。逆に、R8,R9,R10,R11のビット8-15を8ビットレジスタとして使うことはできない。

この文章で扱うレジスタをまとめると、次のようになる。

8ビットレジスタ
AL,CL,DL,BL,AH,CH,DH,BH,SPL,BPL,SIL,DIL,R8B,R9B,…,R15B
16ビットレジスタ
AX,CX,DX,BX,SP,BP,SI,DI,R8W,R9W,…,R15W
32ビットレジスタ
EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI,R8D,R9D,…,R15D
64ビットレジスタ
RAX,RCX,RDX,RBX,RSP,RBP,RSI,RDI,R8,R9,…,R15
セグメントレジスタ
ES,CS,SS,DS,FS,GS
命令ポインタ(16,32,64ビット)
IP,EIP,RIP
フラグレジスタ(16,32,64ビット)
FLAGS,EFLAGS,RFLAGS
個々のフラグ
CF,PF,AF,ZF,SF,TF,IF,DF,OF

オペランドサイズあるいはアドレスサイズによって、RAX,RCX,…またはEAX,ECX,…が使われる場合、rAX,rCX,…と表記する。R8,R9,…またはR8D,R9D…が使われる場合(さらにR8W,R9W,…とR8B,R9B,…を含むこともある)、r8,r9,…と表記する。

汎用レジスタの下位32ビットに書き込むと、上位32ビットは0になる。例えば、 MOV EAX,EAX は、RAXの上位32ビットをクリアする。16,8ビットの書き込みでは、残りの部分は変更されない。

2.3 REXプリフィックス

64ビットモードで追加されたレジスタを使用したり、64ビットオペランドサイズを使用したりするためには、REXプリフィックスを使う。REXプリフィックスは、40h-4Fhの16個あり、下位4ビットが以下の4つの機能に対応している。 REXプリフィックスは、x86のプリフィックスより後、オペコードの直前に置かなければならない。また、REXプリフィックスを使ったときは、AH,CH,DH,BHをオペランドとすることはできず、代わりにSPL,BPL,SIL,DILが使われる。

以上をまとめると次のようになる。

REX                -  40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
r/m,baseにr8-r15   × × ○ × ○ × ○ × ○ × ○ × ○ × ○ × ○
r/mにSPL,BPLなど   × ○ -- ○ -- ○ -- ○ -- ○ -- ○ -- ○ -- ○ --
indexにr8-r15      × × × ○ ○ × × ○ ○ × × ○ ○ × × ○ ○
regにr8-r15        × × × × × ○ ○ ○ ○ × × × × ○ ○ ○ ○
regにSPL,BPLなど   × ○ ○ ○ ○ -- -- -- -- ○ ○ ○ ○ -- -- -- --
64ビットオペランド × × × × × × × × × ○ ○ ○ ○ ○ ○ ○ ○

この章では今後、必要なビットだけが1になっているREXプリフィックスを代表として「REX=44」のように示す。複数の機能を組み合わせるときには、ORをとって一つのREXプリフィックスにする。

2.4 アドレシングモード

64ビットモードでは、レジスタが増えただけでなく、メモリオペランドにRIP相対アドレシングが使えるようになった。デフォルトではアドレスサイズは64ビットだが、アドレスサイズプリフィックスを用いると32ビットにすることができ、その場合(RIP相対であっても)すべてのアドレス計算の後で上位32ビットが0にされる(FS,GSは?)。

命令中の即値や変位は、いくつかの例外を除いて最大32ビットである。例外とは、A0h-A3hとB8h-BFhの各命令である。

ModR/Mバイトのregフィールドは、オペランドサイズに応じて、次のように解釈される。

size	reg	00	08	10	18	20	28	30	38
 8	-	AL	CL	DL	BL	AH	CH	DH	BH
	REX=40	AL	CL	DL	BL	SPL	BPL	SIL	DIL
	REX=44	R8B	R9B	R10B	R11B	R12B	R13B	R14B	R15B
16	-	AX	CX	DX	BX	SP	BP	SI	DI
	REX=44	R8W	R9W	R10W	R11W	R12W	R13W	R14W	R15W
32	-	EAX	ECX	EDX	EBX	ESP	EBP	ESI	EDI
	REX=44	R8D	R9D	R10D	R11D	R12D	R13D	R14D	R15D
64	-	RAX	RCX	RDX	RBX	RSP	RBP	RSI	RDI
	REX=44	R8	R9	R10	R11	R12	R13	R14	R15
表には示さないが、XMM8、CR8、DR8なども同様である。

ModR/Mバイトのmodフィールドとr/mフィールドは、オペランドサイズに応じて、次のように解釈される。SIBについては後述する。

r/m  -  REX=41    r/m     -        REX=41     r/m     -         REX=41      r/m      -       REX=41
00 [rAX] [r8]     40 [rAX+disp8] [r8+disp8]   80 [rAX+disp32] [r8+disp32]   C0 AL/AX/EAX/RAX   r8
01 [rCX] [r9]     41 [rCX+disp8] [r9+disp8]   81 [rCX+disp32] [r9+disp32]   C1 CL/CX/ECX/RCX   r9
02 [rDX] [r10]    42 [rDX+disp8] [r10+disp8]  82 [rDX+disp32] [r10+disp32]  C2 DL/DX/EDX/RDX   r10
03 [rBX] [r11]    43 [rBX+disp8] [r11+disp8]  83 [rBX+disp32] [r11+disp32]  C3 BL/BX/EBX/RBX   r11
04 [SIB] [SIB]    44 [SIB+disp8] [SIB+disp8]  84 [SIB+disp32] [SIB+disp32]  C4 AH#b/SP/ESP/RSP r12
05 [RIP+disp32]#a 45 [rBP+disp8] [r13+disp8]  85 [rBP+disp32] [r13+disp32]  C5 CH#b/BP/EBP/RBP r13
06 [rSI] [r14]    46 [rSI+disp8] [r14+disp8]  86 [rSI+disp32] [r14+disp32]  C6 DH#b/SI/ESI/RSI r14
07 [rDI] [r15]    47 [rDI+disp8] [r15+disp8]  87 [rDI+disp32] {r15+disp32]  C7 BH#b/DI/EDI/RDI r15
#a REX.Bに関係なくRIP相対
#b REX.Bが0のREXプリフィックスを使用すると、順にSPL,BPL,SIL,DIL
ModR/Mバイトのr/mフィールドが、SIBを使用することを示してしているとき、SIBバイトのbase,index,scaleの各フィールドはそれぞれ次のように解釈される。
     [SIB]    [SIB+disp8] [SIB+disp32]
base - REX=41   - REX=41    - REX=41
00  rAX  r8    rAX  r8     rAX  r8
01  rCX  r9    rCX  r9     rCX  r9
02  rDX  r10   rDX  r10    rDX  r10
03  rBX  r11   rBX  r11    rBX  r11
04  rSP  r12   rSP  r12    rSP  r12
05  disp32#c   rBP  r13    rBP  r13
06  rSI  r14   rSI  r14    rSI  r14
07  rDI  r15   rDI  r15    rDI  r15
#c REX.Bに関係なく直接アドレス

index	00	08	10	18	20	28	30	38
-	rAX	rCX	rDX	rBX	-#d	rBP	rSI	rDI
REX=42	r8	r9	r10	r11	r12	r13	r14	r15
#d [rSP]、[r12]、および[disp32]のために使う

scale	00	40	80	C0
	*1	*2	*3	*4

結局、メモリオペランドについては次のようなアドレシングモードがあることになる。

[base]			baseはrBP,r13以外
[RIP+disp32]
[disp32]
[base+disp]
[base+index*scale]	baseはrBP,r13以外
[index*scale+disp32]
[base+index*scale+disp]
baseはベースレジスタ(全汎用レジスタ)
indexはインデックスレジスタ(rSP以外)
scaleはスケール(1,2,4,8)
disp32は32ビットの変位(符号つき定数)
dispは8または32ビットの変位(符号つき定数)
注意するべき点は次の通りである。

2.5 セグメント

64ビットモードでは、セグメントレジスタの役割は非常に小さい。セグメント属性による保護は行われず、セグメントリミットのチェックもない。CS,DS,ES,SSのセグメントベースアドレスは常に0として扱われ、セグメントオーバーライドプリフィックスは無視される。ただし、FS,GSのベースアドレスは64ビット指定可能で、オーバーライドも有効である。結局次のようになる。
CS
レジスタの値に対応したセグメントディスクリプタが使われるが、DPL以外は無視される。(64ビットモードの有効なコードセグメントディスクリプタなので、S,P,Lビットは1、Dビットは0でなければならない)Cは?。
DS,ES,SS
値をロードするときには、legacyモードと同様にDPLのチェックが行われる。しかし、メモリアクセスの際はすべて無視される。CPL=3のSS以外は、NULLセレクタを入れておいてもよい。
FS,GS
値をロードすると、legacyモードと同様のDPLチェックの他、ベース(32ビット)が使われる(上位32ビットは0)。モデル固有レジスタを使って64ビットのベースアドレスを設定することもできる。

2.6 32/64ビットオペランド

64ビットモードでは、デフォルトオペランドサイズは32ビットであり、たいていの命令では、REXプリフィックスを使わなければ64ビットオペランドにアクセスできない。汎用レジスタに32ビットの値を書き込むと、上位32ビットはクリアされる。

64ビットオペランドであっても、即値のエンコーディングは32ビットであり、符号拡張されて使用される。

64ビットオペランドに対するシフトカウントは、下位6ビットが有効になる。

BSWAP命令

JMP,CALL,Jcc

CMOVで32ビットオペランドサイズのとき、条件が偽であっても転送先レジスタの上位32ビットはクリアされる。

CMPXCHG8B命令は追加された命令のCMPXCHG16B命令を参照。

CPUID命令のオペランドサイズは32ビット固定で、64ビットを指定しても無視される。

ENTER,LEAVE命令のオペランドサイズは64ビット固定である(32ビットはエンコードできない)。

INのオペランドは8,16,32ビットまで? OUTも? INS,INSW,INSD,OUTS,OUTSW,OUTSD

LFS,LGS,LSS命令のオペランドサイズは32ビット固定?

MMX命令のMOVD命令は、64ビットとしても使える。

PUSH,POPはデフォルト64ビットだが、16ビットにもできる(RSPも2ずつ増減)。

RET nearは64ビット固定

RET farはデフォルト32ビットだが、64ビットにもできる

VERR,VERW命令は16ビット固定

NOP(90h)は、 XCHG EAX,EAX とは異なる動作になる。後者は上位32ビットをクリアする。

2.7 追加された命令

(準備中)

MOVSXD CDQE CMPSQ IRETD,IRETQ JRCXZ LODSQ MOVSQ POPFQ PUSHFQ SCASQ STOSQ SWAPGS SYSCALL SYSRET

CMPXCHG16B命令は、CMPXCHG8B命令にREXプリフィックスをつけて64ビットにしたものである。CPUID命令のCMPXCHG16Bビットが1の場合に利用でき、そうでない場合は無効命令例外が発生する。

2.8 使用できない命令

(準備中)

AAA,AAS,AAM,AAD命令

BOUND命令

CALL,JMP命令のうちfar絶対

DAA,DAS命令

INTO命令

LAHF,SAHF命令(CPUID命令のLahfSahfビットが1の場合は使用できる。Errata 110の影響で、使用できるのにこのビットが0の場合があるが、通常はファームフェアで修正されている)

LDS,LES命令

PUSH ES/CS/SS/DS、POP ES/SS/DS

PUSHA,PUSHAD,POPA,POPAD命令

オペコード82h(80hの別名)

SALC(ドキュメント化されていない命令)

ARPL命令(MOVSXD命令になる)

命令長1バイトのINC,DEC命令(REXプリフィックスになる)

SYSENTER,SYSEXIT命令はlongモードで使用できない

2.9 longモードでのシステムプログラミング

(準備中)

3. 搭載プロセッサ

ここではAMDのプロセッサについて述べる。コアのアーキテクチャで大きく分けると、Family 0Fh、10h、11h、12h、14h、15hとなる。Familyは16進で表され、CPUIDのBaseFamily(AMD64搭載プロセッサでは0Fh)とExtendedFamilyを加算したものである。

同じFamilyの中でも製品によって、コア数、キャッシュサイズなどが異なる。特にFamily 0Fhでは、Revisionによって拡張命令などのサポート状況が異なる。

以下はAMD64を搭載する主なプロセッサの仕様である。 (準備中)

					コア数
					  L2キャッシュ	メモリの種類
製品名			コードネーム	    L3キャッシュ   チャンネル数	Family/Revision

Opteron			SledgeHammer	1 1MB		DDR 2		0Fh/C
Opteron 8xx		Athenes
Opteron 2xx		Troy
Opteron 1xx		Venus

Athlon 64 FX		SledgeHammer	1 1MB		DDR 2		0Fh/C
Athlon 64		ClawHammer	1 1MB/512KB	DDR 1		0Fh/C
Athlon 64		Newcastle	1 512KB		DDR 2		0Fh/C
Athlon 64		Winchester	1 512KB		DDR 2		0Fh/D
Athlon 64 FX		San Diego	1 1MB		DDR 2		0Fh/E
Athlon 64		Venice		1 1MB/512KB	DDR 2		0Fh/E
Athlon 64 FX/X2		Toledo		2 1MBx2		DDR 2		0Fh/E
Athlon 64 X2		Manchester	2 512KBx2	DDR 2		0Fh/E
Athlon 64 FX/X2		Windsor		2 1MBx2/512KBx2	DDR2 2		0Fh/F
Athlon 64		Orleans		1 1MB/512KB	DDR2 2		0Fh/F
Athlon (64) X2		Brisbane	2 512KBx2	DDR2 2		0Fh/G
Athlon (64)		Lima		1 512KB		DDR2 2		0Fh/G
Phenom X4		Agena		4 512KBx4 2MB	DDR2 2		10h
Phenom X3		Toliman		3 512KBx3 2MB	DDR2 2		10h
Athlon X2		Kuma		2 512KBx2 2MB	DDR2 2		10h
Phenom II X4		Deneb		4 512KBx4 6MB	DDR2/3 2	10h
Phenom II X3		Heka		3 512KBx3 6MB	DDR3 2		10h
Phenom II X2		Callisto	2 512KBx2 6MB	DDR3 2		10h
Athlon II X4		Propus		4 512KBx4	DDR3 2		10h
Athlon II X3		Rena		3 512KBx3	DDR3 2		10h
Athlon II X2		Regor		2 1MBx2		DDR3 2		10h
Phenom II X6		Thuban		6 512KBx6 6MB	DDR3 2		10h
AMD A-Series		Llano		4 1MBx4		DDR3 2		12h
AMD C-Series		Ontario		2 512KBx2	DDR3 1		14h
AMD E-Series		Zacate		1/2 512KBx1/2	DDR3 1		14h
AMD FX-Series		Zambezi		8 2MBx4		DDR3 2		15h

低電圧版モバイルAthlon 64  Oakville
Turion 64		Lancaster	1 1MB/512KB	DDR 1		0Fh/E
Turion 64 X2		Taylor		2				0Fh/F
Turion 64 X2				2				0Fh/G
Turion X2 Ultra		Griffin		2				11h
Turion II				2				10h

4. 主な命令一覧

(準備中)
REXプリフィックスを使用して64ビットレジスタを使ったり、拡張されたレジスタを使ったりすることによる、実行時間のペナルティーはない。

参考文献

[a1]-[a12]はDeveloper Guides and Manuals | AMD Developer Centralより、[i1][i2][i3]の英語最新版はIntel® 64 and IA-32 Architectures Software Developer's Manualsより、日本語版(英語版より古い)は日本語技術資料のダウンロードよりダウンロード可能である。
[a1]
AMD64 Architecture Programmer's Manual Volume 1: Application Programming
[a2]
AMD64 Architecture Programmer's Manual Volume 2: System Programming
[a3]
AMD64 Architecture Programmer's Manual Volume 3: General-Purpose and System Instructions
[a4]
AMD64 Architecture Programmer's Manual Volume 4: 128-bit and 256 bit media instructions
[a5]
AMD64 Architecture Programmer's Manual Volume 5: 64-Bit Media and x87 Floating-Point Instructions
[a6]
CPUID Specification
[a7]
Revision Guide for AMD Athlon 64 and AMD Opteron Processors
[a8]
Revision Guide for AMD NPT Family 0Fh Processors
[a9]
Revision Guide for AMD Family 10h Processors
[a10]
Revision Guide for AMD Family 11h Processors
[a11]
Revision Guide for AMD Family 12h Processors
[a12]
Revision Guide for AMD Family 14h Models 00h-0Fh Processors
[i1]
IA-32 インテル アーキテクチャ・ソフトウェア・デベロッパーズ・マニュアル 上巻:基本アーキテクチャ
[i2]
IA-32 インテル アーキテクチャ・ソフトウェア・デベロッパーズ・マニュアル 中巻:命令セット・リファレンス
[i3]
IA-32 インテル アーキテクチャ・ソフトウェア・デベロッパーズ・マニュアル 下巻:システム・プログラミング・ガイド

この文章の各章に対する参照箇所は次のとおりである。