[インデックス 16761] ファイルの概要
このコミットは、Go言語のリンカであるcmd/6l
(x86-64アーキテクチャ用) と cmd/8l
(x86アーキテクチャ用) におけるアセンブリ命令のエンコーディングに関する最適化を導入しています。具体的には、XCHG
(交換)命令において、可能な場合に1バイト形式のオペコードを使用するように変更されています。
コミット
commit aad4720b5193221c000892e73615322698170d68
Author: Russ Cox <rsc@golang.org>
Date: Fri Jul 12 20:58:38 2013 -0400
cmd/6l, cmd/8l: use one-byte XCHG forms when possible
Pointed out by khr.
R=ken2
CC=golang-dev
https://golang.org/cl/11145044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/aad4720b5193221c000892e73615322698170d68
元コミット内容
cmd/6l, cmd/8l: use one-byte XCHG forms when possible
このコミットは、cmd/6l
(64ビット版リンカ)とcmd/8l
(32ビット版リンカ)において、XCHG
命令をエンコードする際に、可能な場合は1バイト形式を使用するように変更します。この変更はkhr
によって指摘されたものです。
変更の背景
x86アーキテクチャには、特定のレジスタ(特にアキュムレータレジスタであるAX
/EAX
/RAX
)と他のレジスタ間で値を交換するXCHG
命令に、より短い1バイト形式のオペコードが存在します。例えば、XCHG EAX, EAX
は0x90
という1バイトのオペコードでエンコードされ、これは一般的にNOP
(No Operation)命令としても知られています。
従来のリンカは、XCHG
命令をエンコードする際に、常に汎用的な2バイト以上のオペコード(例: 0x87
)を使用していた可能性があります。しかし、アキュムレータレジスタが関与する特定のXCHG
操作では、より短い1バイトのオペコードを使用できるため、生成されるバイナリのサイズを削減し、命令フェッチの効率を向上させる可能性があります。
このコミットは、このような最適化の機会を捉え、リンカがより効率的な命令エンコーディングを選択できるようにすることで、Goプログラムのバイナリサイズをわずかに削減し、実行時のパフォーマンスに寄与することを目的としています。khr
氏による指摘が、この最適化の実装のきっかけとなりました。
前提知識の解説
x86アセンブリにおけるXCHG
命令
XCHG
(Exchange)命令は、x86アセンブリ言語において、2つのオペランドの値を交換するために使用される命令です。オペランドは、2つの汎用レジスタ、または1つのレジスタと1つのメモリ位置のいずれかです。
例:
XCHG EAX, EBX
:EAX
レジスタとEBX
レジスタの値を交換します。XCHG EAX, [ESI]
:EAX
レジスタとESI
レジスタが指すメモリ位置の値を交換します。
XCHG
命令のオペコードと0x90
(NOP)
XCHG
命令は、オペランドの組み合わせによって異なるオペコード(機械語のバイト列)を持ちます。特に重要なのは、アキュムレータレジスタ(16ビットのAX
、32ビットのEAX
、64ビットのRAX
)が関与する場合です。
XCHG AX, AX
(16ビット): この命令は、AX
レジスタ自身とAX
レジスタの値を交換するため、実質的には何も操作を行いません。この命令のオペコードは**0x90
**という1バイトです。XCHG EAX, EAX
(32ビット): 同様に、EAX
レジスタ自身とEAX
レジスタの値を交換し、オペコードは**0x90
**です。XCHG RAX, RAX
(64ビット): 64ビットモードでも、RAX
レジスタ自身との交換はオペコード**0x90
**となります。
この0x90
というオペコードは、x86アーキテクチャにおいて公式に**NOP
(No Operation)命令**として指定されています。これは、CPUが何もしない命令として認識し、単に次の命令に進むことを意味します。NOP
は、コードのパディング、アライメント調整、デバッグ時のプレースホルダーなど、様々な目的で利用されます。
Goのリンカ (cmd/6l
, cmd/8l
)
Go言語のコンパイラツールチェーンは、ソースコードを機械語に変換する際に、アセンブラとリンカを使用します。
cmd/6l
: x86-64 (AMD64) アーキテクチャ用のリンカです。Goのソースコードから生成されたオブジェクトファイルをリンクし、実行可能なバイナリを生成します。cmd/8l
: x86 (IA-32) アーキテクチャ用のリンカです。同様に、32ビット環境向けのバイナリを生成します。
これらのリンカは、Goのコンパイラが生成した中間表現(アセンブリ命令の抽象的な表現)を、最終的な機械語のバイト列に変換する役割を担っています。この変換プロセスにおいて、命令のエンコーディング(どのオペコードを使用するか)を決定します。
optab.c
ファイル
optab.c
ファイルは、Goのリンカにおいて、各アセンブリ命令に対応するオペコードやオペランドの形式を定義する「命令テーブル」のような役割を果たします。リンカは、このテーブルを参照して、特定のアセンブリ命令をどのように機械語にエンコードするかを決定します。
このファイル内の定義は、命令の種類(例: AXCHGL
はXCHG
の32ビット版)、オペランドのタイプ(例: レジスタ、メモリ)、そしてそれらに対応するオペコードのバイト列を含んでいます。
技術的詳細
このコミットの核心は、XCHG
命令のエンコーディングロジックを改善し、よりコンパクトな1バイト形式のオペコード(0x90
)を優先的に使用するようにすることです。
x86アーキテクチャでは、XCHG
命令は汎用的な形式として0x87
というオペコード(ModR/MバイトやSIBバイトを伴う場合がある)を持ちます。しかし、アキュムレータレジスタ(AX
, EAX
, RAX
)と他の汎用レジスタとの間で交換を行う場合、より短い1バイトのオペコードが存在します。
具体的には、XCHG reg, AX/EAX/RAX
またはXCHG AX/EAX/RAX, reg
の形式は、0x90
にレジスタを示すビットを加えた1バイトのオペコードで表現できます。例えば、XCHG EAX, ECX
は0x87 C8
(2バイト)ですが、XCHG EAX, EBX
は0x93
(1バイト)で表現できます(0x90
+ EBX
レジスタを示すビット)。
このコミットでは、リンカの命令テーブル(optab.c
)に、これらの1バイト形式のXCHG
命令の定義を追加しています。これにより、リンカは、アキュムレータレジスタが関与するXCHG
命令をエンコードする際に、従来の汎用的な複数バイト形式(0x87
)ではなく、より短い1バイト形式(0x90
をベースとしたもの)を選択できるようになります。
この最適化は、生成されるGoバイナリのサイズをわずかに削減し、命令キャッシュの効率を向上させることで、プログラムの起動時間や実行速度に微細な改善をもたらす可能性があります。
コアとなるコードの変更箇所
変更は主にsrc/cmd/6l/optab.c
とsrc/cmd/8l/optab.c
の2つのファイルで行われています。
src/cmd/6l/optab.c
--- a/src/cmd/6l/optab.c
+++ b/src/cmd/6l/optab.c
@@ -247,8 +247,10 @@ uchar yrb_mb[] =
Yrb, Ymb, Zr_m, 1,
0
};
-uchar yml_ml[] =
+uchar yxchg[] =
{
+ Yax, Yrl, Z_rp, 1,
+ Yrl, Yax, Zrp_, 1,
Yrl, Yml, Zr_m, 1,
Yml, Yrl, Zm_r, 1,
0
@@ -1174,9 +1176,9 @@ Optab optab[] =
{ AWAIT, ynone, Px, 0x9b },
{ AWORD, ybyte, Px, 2 },
{ AXCHGB, yml_mb, Pb, 0x86,0x86 },
- { AXCHGL, yml_ml, Px, 0x87,0x87 },
- { AXCHGQ, yml_ml, Pw, 0x87,0x87 },
- { AXCHGW, yml_ml, Pe, 0x87,0x87 },
+ { AXCHGL, yxchg, Px, 0x90,0x90,0x87,0x87 },
+ { AXCHGQ, yxchg, Pw, 0x90,0x90,0x87,0x87 },
+ { AXCHGW, yxchg, Pe, 0x90,0x90,0x87,0x87 },
{ AXLAT, ynone, Px, 0xd7 },
{ AXORB, yxorb, Pb, 0x34,0x80,(06),0x30,0x32 },
{ AXORL, yxorl, Px, 0x83,(06),0x35,0x81,(06),0x31,0x33 },
src/cmd/8l/optab.c
--- a/src/cmd/8l/optab.c
+++ b/src/cmd/8l/optab.c
@@ -196,8 +196,10 @@ uchar yml_mb[] =
Ymb, Yrb, Zm_r, 1,
0
};
-uchar yml_ml[] =
+uchar yxchg[] =
{
+ Yax, Yrl, Z_rp, 1,
+ Yrl, Yax, Zrp_, 1,
Yrl, Yml, Zr_m, 1,
Yml, Yrl, Zm_r, 1,
0
@@ -696,8 +698,8 @@ Optab optab[] =
{ AWAIT, ynone, Px, 0x9b },
{ AWORD, ybyte, Px, 2 },
{ AXCHGB, yml_mb, Pb, 0x86,0x86 },
- { AXCHGL, yml_ml, Px, 0x87,0x87 },
- { AXCHGW, yml_ml, Pe, 0x87,0x87 },
+ { AXCHGL, yxchg, Px, 0x90,0x90,0x87,0x87 },
+ { AXCHGW, yxchg, Pe, 0x90,0x90,0x87,0x87 },
{ AXLAT, ynone, Px, 0xd7 },
{ AXORB, yxorb, Pb, 0x34,0x80,(06),0x30,0x32 },
{ AXORL, yxorl, Px, 0x83,(06),0x35,0x81,(06),0x31,0x33 },
コアとなるコードの解説
このコミットの主要な変更点は、XCHG
命令のオペランドタイプを定義する部分です。
-
yml_ml
からyxchg
への名称変更と新しいエントリの追加:- 以前は
yml_ml
という名前で定義されていたXCHG
命令のオペランドタイプリストが、yxchg
という新しい名前に変更されました。 - そして、この
yxchg
リストの先頭に以下の2つのエントリが追加されました。Yax, Yrl, Z_rp, 1, Yrl, Yax, Zrp_, 1,
Yax
: アキュムレータレジスタ(AX
/EAX
/RAX
)を示します。Yrl
: 汎用レジスタ(AL
/CL
/DL
/BL
など、またはAX
/CX
/DX
/BX
など)を示します。Z_rp
: オペコードのレジスタ部分にエンコードされる形式を示します。Zrp_
: オペコードのレジスタ部分にエンコードされる形式を示します。1
: この組み合わせが1バイトのオペコードで表現可能であることを示唆しています。
これらの新しいエントリは、
XCHG AX, reg
(またはEAX, reg
、RAX, reg
)およびXCHG reg, AX
(またはreg, EAX
、reg, RAX
)のような形式のXCHG
命令が、より短い1バイトのオペコードでエンコードできることをリンカに伝えます。 - 以前は
-
AXCHGL
,AXCHGQ
,AXCHGW
のオペコード定義の変更:-
AXCHGL
(32ビットXCHG
)、AXCHGQ
(64ビットXCHG
)、AXCHGW
(16ビットXCHG
) の各命令の定義が変更されました。 -
変更前:
{ AXCHGL, yml_ml, Px, 0x87,0x87 }, { AXCHGQ, yml_ml, Pw, 0x87,0x87 }, { AXCHGW, yml_ml, Pe, 0x87,0x87 },
ここでは、
yml_ml
というオペランドタイプリストと、汎用的なXCHG
オペコードである0x87
が指定されていました。 -
変更後:
{ AXCHGL, yxchg, Px, 0x90,0x90,0x87,0x87 }, { AXCHGQ, yxchg, Pw, 0x90,0x90,0x87,0x87 }, { AXCHGW, yxchg, Pe, 0x90,0x90,0x87,0x87 },
新しい
yxchg
オペランドタイプリストが使用され、オペコードの定義が0x90,0x90,0x87,0x87
に変更されました。 これは、リンカがXCHG
命令をエンコードする際に、まずyxchg
リストで定義された1バイト形式(0x90
をベースとしたもの)を試み、それが適用できない場合にのみ、従来の汎用的な2バイト形式(0x87
)を使用するという優先順位を示しています。
-
この変更により、Goのリンカは、アキュムレータレジスタが関与するXCHG
命令に対して、より効率的でコンパクトな1バイトの機械語命令を生成できるようになり、結果として生成されるバイナリのサイズが最適化されます。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
- Goのツールチェーンに関する情報: https://golang.org/cmd/
- x86命令セットリファレンス (Intel/AMDの公式ドキュメントを参照することを推奨します。例: Intel® 64 and IA-32 Architectures Software Developer’s Manuals)
参考にした情報源リンク
xchg
instruction in x86 assembly: https://www.felixcloutier.com/x86/xchgNOP
(No Operation) instruction and0x90
: https://en.wikipedia.org/wiki/NOP_(code)- Stack Overflow discussions on
XCHG EAX, EAX
and0x90
: https://stackoverflow.com/questions/1090000/what-is-the-purpose-of-xchg-eax-eax - Various articles and discussions on x86 assembly optimization and NOPs.