[インデックス 16483] ファイルの概要
このコミットは、Goコンパイラのcmd/6c
(32-bit x86アーキテクチャ向け)とcmd/8c
(64-bit x86アーキテクチャ向け)において、不要な長整数乗算(long multiplication)の生成を回避するための最適化を導入しています。具体的には、乗算処理におけるレジスタ割り当てのロジックを修正し、特定の条件下でより効率的なコードが生成されるように改善されています。
コミット
commit 2c0b00744f067c7d9f44a735f77aa0eb0e2e345e
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Tue Jun 4 08:33:00 2013 +0200
cmd/6c, cmd/8c: avoid generating long multiplication when not necessary.
On amd64
benchmark old ns/op new ns/op delta
BenchmarkHashStringSpeed 91 74 -18.49%
BenchmarkHashInt32Speed 54 45 -17.88%
BenchmarkHashInt64Speed 76 58 -23.53%
BenchmarkHashStringArraySpeed 231 188 -18.61%
Fixes #5367.
R=golang-dev, iant, dave, daniel.morsing, ality, rsc
CC=golang-dev
https://golang.org/cl/9040043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2c0b00744f067c7d9f44a735f77aa0eb0e2e345e
元コミット内容
このコミットの目的は、Goコンパイラ(特にcmd/6c
とcmd/8c
)が、必要のない場合に長整数乗算(long multiplication)の命令を生成するのを防ぐことです。これにより、生成されるアセンブリコードの効率が向上し、特にハッシュ関連のベンチマークで顕著なパフォーマンス改善が見られます。
コミットメッセージには、amd64
アーキテクチャにおけるベンチマーク結果が示されており、BenchmarkHashStringSpeed
、BenchmarkHashInt32Speed
、BenchmarkHashInt64Speed
、BenchmarkHashStringArraySpeed
の全てで17%から23%の性能向上が確認されています。
この変更は、GoのIssue #5367を修正するものです。
変更の背景
Goコンパイラは、ソースコードを機械語に変換する際に様々な最適化を行います。乗算のような基本的な演算であっても、オペランドのサイズや型、コンテキストによっては複数の異なるアセンブリ命令が選択される可能性があります。
このコミットが修正しようとしている問題は、コンパイラが不必要に「長整数乗算」の命令を生成してしまうケースがあったことです。長整数乗算は、結果が単一のレジスタに収まらないような大きな数値の乗算を行う際に必要となる複雑な操作です。しかし、結果が単一のレジスタに収まる場合や、より単純な乗算命令で済む場合でも、コンパイラが誤って長整数乗算のロジックを選択してしまうと、無駄なレジスタ操作や余分な命令が挿入され、パフォーマンスが低下します。
特に、ハッシュ計算のような処理では、頻繁に数値の乗算が行われます。このようなホットパスで非効率な乗算命令が生成されると、アプリケーション全体のパフォーマンスに大きな影響を与えます。コミットメッセージに示されているベンチマーク結果は、この最適化がハッシュ計算の性能に直接的に寄与していることを裏付けています。
Go Issue #5367のタイトルは "cmd/6g: long multiplication for 32-bit values" であり、32ビット値の乗算で不必要に長整数乗算が使われる問題が報告されていました。このコミットは、その問題に対する直接的な解決策を提供しています。
前提知識の解説
このコミットを理解するためには、以下の概念が役立ちます。
- Goコンパイラ (
cmd/6c
,cmd/8c
):cmd/6c
: Goのコンパイラの一つで、主に32-bit x86 (Intel/AMD) アーキテクチャ向けのコードを生成します。Go 1.x系の初期には、各アーキテクチャに対応するコンパイラが6g
(amd64),8g
(386),5g
(arm) のように命名されていましたが、このコミットの文脈では6c
と8c
が言及されており、これらはGoのコンパイラバックエンドの一部、特にC言語で書かれたコード生成部分を指している可能性があります。Goのツールチェインは進化しており、gc
(Go compiler) が主要なコンパイラとして統合されていますが、内部的には異なるアーキテクチャ向けのコード生成ロジックがモジュール化されています。cmd/8c
: 同様に、64-bit x86 (amd64) アーキテクチャ向けのコードを生成するコンパイラの一部です。
- レジスタ割り当て (Register Allocation): コンパイラの重要な最適化フェーズの一つで、プログラムの変数をCPUのレジスタに割り当てるプロセスです。レジスタはメモリよりもはるかに高速なため、適切にレジスタを使用することでプログラムの実行速度が向上します。乗算のような演算では、オペランドや結果を特定のレジスタ(例:
AX
,DX
)に配置する必要がある場合があります。 OMUL
とOLMUL
:- これらはGoコンパイラの内部表現における演算子の種類です。
OMUL
: 通常の乗算操作を表します。OLMUL
: "Long Multiplication"、つまり長整数乗算を表します。これは、結果が通常のレジスタサイズを超える可能性がある場合に、複数のレジスタを使用して乗算結果を保持するような、より複雑なアセンブリ命令シーケンスを生成する必要があることを示唆します。例えば、32ビットの数値同士の乗算結果が64ビットになる場合などです。
reg[D_DX]
: コンパイラのレジスタ管理システムにおける内部変数です。D_DX
は、x86アーキテクチャにおけるDX
レジスタ(またはその64ビット版であるRDX
レジスタ)に対応するインデックスです。乗算命令(特にMUL
命令)では、結果の一部(上位ビット)がDX
レジスタに格納されることが多いため、コンパイラはDX
レジスタの使用状況を追跡する必要があります。reg[D_DX]++
はDX
レジスタが使用中であることをマークし、reg[D_DX]--
は使用が終了したことをマークします。gopcode
: Goコンパイラのコード生成関数の一つで、特定の演算子(opcode)に対応するアセンブリ命令を生成します。addable
/INDEXED
/hardconst
: これらはコンパイラがノード(式や変数を表す内部データ構造)の特性を判断するために使用するフラグや関数です。addable >= INDEXED
: オペランドがインデックス付きアドレス指定(例:[base + index*scale]
)に適しているかどうかを示します。hardconst(r)
: オペランドr
がハードウェアで直接扱える定数(例: 即値)であるかどうかをチェックします。
技術的詳細
このコミットの核心は、src/cmd/6c/cgen.c
とsrc/cmd/8c/cgen.c
内のcgen
関数における乗算処理のロジック変更です。cgen
関数は、Goの抽象構文木(AST)のノードを巡回し、対応する機械語命令を生成する役割を担っています。
変更前は、OMUL
(通常の乗算)の場合のみ、特定のレジスタ割り当てロジックが適用されていました。しかし、OLMUL
(長整数乗算)の場合も同様の最適化が必要であることが判明しました。
具体的な変更点は以下の通りです。
-
条件式の拡張: 変更前:
if(o == OMUL)
変更後:if(o == OMUL || o == OLMUL)
これにより、通常の乗算(OMUL
)だけでなく、長整数乗算(OLMUL
)の場合も、続くレジスタ割り当てとコード生成の最適化パスが適用されるようになりました。これは、コンパイラがOLMUL
と判断した場合でも、実際にはより単純なOMUL
命令で処理できるケースがあることを示唆しています。 -
DX
レジスタの管理: 変更前:/* should favour AX */
というコメントのみで、DX
レジスタの明示的な管理は行われていませんでした。 変更後:reg[D_DX]++; // for gopcode case OMUL // ... reg[D_DX]--;
これは、乗算命令(特にx86の
MUL
命令)が結果の一部をDX
レジスタに書き込む可能性があるため、DX
レジスタが一時的に使用されることをコンパイラに通知するものです。reg[D_DX]++
でDX
レジスタが使用中であることをマークし、乗算命令のコード生成後にreg[D_DX]--
で解放しています。 この変更の意図は、コンパイラがDX
レジスタの利用状況を正確に把握し、不必要なレジスタの退避・復元(spill/reload)を避けることにあります。もしDX
レジスタが乗算のために必要であるにもかかわらず、コンパイラがその事実を認識していなければ、他の目的でDX
レジスタを割り当ててしまい、結果的にレジスタの競合や非効率なコード生成につながる可能性があります。この明示的な管理により、コンパイラはDX
レジスタを適切に予約し、より効率的なコードパスを選択できるようになります。
この修正により、コンパイラはOMUL
とOLMUL
の両方に対して、オペランドの特性(例: addable
やhardconst
)に基づいて、より適切な乗算命令(単一レジスタに収まる通常の乗算か、複数レジスタを必要とする長整数乗算か)を選択できるようになります。特に、結果が32ビットや64ビットの単一レジスタに収まるような乗算において、不必要に複雑な長整数乗算のロジックを回避し、より高速なコードを生成することが可能になりました。
コアとなるコードの変更箇所
変更はsrc/cmd/6c/cgen.c
とsrc/cmd/8c/cgen.c
のcgen
関数内、OMUL
(およびOLMUL
)演算子を処理するブロックに集中しています。
src/cmd/6c/cgen.c
--- a/src/cmd/6c/cgen.c
+++ b/src/cmd/6c/cgen.c
@@ -392,13 +392,13 @@ cgen(Node *n, Node *nn)\
}
}
- if(o == OMUL) {
+ if(o == OMUL || o == OLMUL) {
if(l->addable >= INDEXED) {
t = l;
l = r;
r = t;
}
- /* should favour AX */
+ reg[D_DX]++; // for gopcode case OMUL
regalloc(&nod, l, nn);
cgen(l, &nod);
if(r->addable < INDEXED || hardconst(r)) {
@@ -410,6 +410,7 @@ cgen(Node *n, Node *nn)\
gopcode(OMUL, n->type, r, &nod); /* addressible */
gmove(&nod, nn);
regfree(&nod);
+ reg[D_DX]--;
break;
}
src/cmd/8c/cgen.c
--- a/src/cmd/8c/cgen.c
+++ b/src/cmd/8c/cgen.c
@@ -404,13 +404,13 @@ cgen(Node *n, Node *nn)\
}
}
- if(o == OMUL) {
+ if(o == OMUL || o == OLMUL) {
if(l->addable >= INDEXED) {
t = l;
l = r;
r = t;
}
- /* should favour AX */
+ reg[D_DX]++; // for gopcode case OMUL
regalloc(&nod, l, nn);
cgen(l, &nod);
if(r->addable < INDEXED) {
@@ -422,6 +422,7 @@ cgen(Node *n, Node *nn)\
gopcode(OMUL, n->type, r, &nod); /* addressible */
gmove(&nod, nn);
regfree(&nod);
+ reg[D_DX]--;
break;
}
コアとなるコードの解説
両ファイルにおける変更は同一であり、cgen
関数内の乗算処理ブロックにあります。
-
if(o == OMUL)
からif(o == OMUL || o == OLMUL)
への変更: これは、コンパイラが乗算ノードを処理する際に、従来のOMUL
(通常の乗算)だけでなく、OLMUL
(長整数乗算)の場合も、この最適化パスに入るように拡張したものです。これにより、OLMUL
と判断された場合でも、オペランドの特性によっては、より効率的な単一レジスタ乗算命令(例えば、IMUL
命令など)を生成する機会が生まれます。これは、コンパイラが型システムや中間表現の段階でOLMUL
と判断しても、実際のコード生成段階でより効率的な命令を選択できる柔軟性を持たせるための変更です。 -
reg[D_DX]++;
の追加: この行は、乗算処理のためにDX
レジスタ(またはRDX
)が一時的に使用されることをレジスタ割り当てシステムに通知します。x86アーキテクチャのMUL
命令やIMUL
命令(単一オペランド形式)は、乗算結果の上位ビットをDX
レジスタに格納するという特性があります。このreg[D_DX]++
は、gopcode(OMUL, ...)
が呼び出される前にDX
レジスタが予約されることを保証し、他の目的でDX
レジスタが誤って割り当てられることを防ぎます。 -
reg[D_DX]--;
の追加: 乗算処理が完了し、結果がnn
に移動された後、DX
レジスタの予約を解除します。これにより、DX
レジスタが他の用途で利用可能になります。
これらの変更は、コンパイラが乗算命令を生成する際に、DX
レジスタの利用をより正確に管理し、不必要なレジスタの退避・復元を避けることで、生成されるアセンブリコードの効率を向上させることを目的としています。特に、32ビットや64ビットの乗算で結果が単一のレジスタに収まる場合でも、コンパイラが誤って長整数乗算のロジックを選択してしまう問題を解決し、よりコンパクトで高速なコードパスを生成できるようになりました。
関連リンク
- Go Issue #5367: https://github.com/golang/go/issues/5367 このコミットが修正した具体的な問題について議論されています。
- Go CL 9040043: https://golang.org/cl/9040043 このコミットに対応するGoのコードレビュー(Change List)ページです。より詳細な議論やレビューコメントが含まれている可能性があります。
参考にした情報源リンク
- Go Issue Tracker: https://github.com/golang/go/issues
- Go Code Review: https://go.googlesource.com/go/+/refs/heads/master
- x86 Assembly Language Programming (for MUL/IMUL instruction details)
- Compiler Design Principles (for register allocation and code generation concepts)
- Go Compiler Internals (general knowledge about
cmd/6c
,cmd/8c
,cgen.c
)
[インデックス 16483] ファイルの概要
このコミットは、Goコンパイラのcmd/6c
(32-bit x86アーキテクチャ向け)とcmd/8c
(64-bit x86アーキテクチャ向け)において、不要な長整数乗算(long multiplication)の生成を回避するための最適化を導入しています。具体的には、乗算処理におけるレジスタ割り当てのロジックを修正し、特定の条件下でより効率的なコードが生成されるように改善されています。
コミット
commit 2c0b00744f067c7d9f44a735f77aa0eb0e2e345e
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Tue Jun 4 08:33:00 2013 +0200
cmd/6c, cmd/8c: avoid generating long multiplication when not necessary.
On amd64
benchmark old ns/op new ns/op delta
BenchmarkHashStringSpeed 91 74 -18.49%
BenchmarkHashInt32Speed 54 45 -17.88%
BenchmarkHashInt64Speed 76 58 -23.53%
BenchmarkHashStringArraySpeed 231 188 -18.61%
Fixes #5367.
R=golang-dev, iant, dave, daniel.morsing, ality, rsc
CC=golang-dev
https://golang.org/cl/9040043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2c0b00744f067c7d9f44a735f77aa0eb0e2e345e
元コミット内容
このコミットの目的は、Goコンパイラ(特にcmd/6c
とcmd/8c
)が、必要のない場合に長整数乗算(long multiplication)の命令を生成するのを防ぐことです。これにより、生成されるアセンブリコードの効率が向上し、特にハッシュ関連のベンチマークで顕著なパフォーマンス改善が見られます。
コミットメッセージには、amd64
アーキテクチャにおけるベンチマーク結果が示されており、BenchmarkHashStringSpeed
、BenchmarkHashInt32Speed
、BenchmarkHashInt64Speed
、BenchmarkHashStringArraySpeed
の全てで17%から23%の性能向上が確認されています。
この変更は、GoのIssue #5367を修正するものです。
変更の背景
Goコンパイラは、ソースコードを機械語に変換する際に様々な最適化を行います。乗算のような基本的な演算であっても、オペランドのサイズや型、コンテキストによっては複数の異なるアセンブリ命令が選択される可能性があります。
このコミットが修正しようとしている問題は、コンパイラが不必要に「長整数乗算」の命令を生成してしまうケースがあったことです。長整数乗算は、結果が単一のレジスタに収まらないような大きな数値の乗算を行う際に必要となる複雑な操作です。しかし、結果が単一のレジスタに収まる場合や、より単純な乗算命令で済む場合でも、コンパイラが誤って長整数乗算のロジックを選択してしまうと、無駄なレジスタ操作や余分な命令が挿入され、パフォーマンスが低下します。
特に、ハッシュ計算のような処理では、頻繁に数値の乗算が行われます。このようなホットパスで非効率な乗算命令が生成されると、アプリケーション全体のパフォーマンスに大きな影響を与えます。コミットメッセージに示されているベンチマーク結果は、この最適化がハッシュ計算の性能に直接的に寄与していることを裏付けています。
Go Issue #5367のタイトルは "cmd/6g: long multiplication for 32-bit values" であり、32ビット値の乗算で不必要に長整数乗算が使われる問題が報告されていました。このコミットは、その問題に対する直接的な解決策を提供しています。
前提知識の解説
このコミットを理解するためには、以下の概念が役立ちます。
- Goコンパイラ (
cmd/6c
,cmd/8c
):cmd/6c
: Goのコンパイラの一つで、主に32-bit x86 (Intel/AMD) アーキテクチャ向けのコードを生成します。Go 1.x系の初期には、各アーキテクチャに対応するコンパイラが6g
(amd64),8g
(386),5g
(arm) のように命名されていましたが、このコミットの文脈では6c
と8c
が言及されており、これらはGoのコンパイラバックエンドの一部、特にC言語で書かれたコード生成部分を指している可能性があります。Goのツールチェインは進化しており、gc
(Go compiler) が主要なコンパイラとして統合されていますが、内部的には異なるアーキテクチャ向けのコード生成ロジックがモジュール化されています。cmd/8c
: 同様に、64-bit x86 (amd64) アーキテクチャ向けのコードを生成するコンパイラの一部です。- 補足: Web検索の結果によると、
cmd/6c
とcmd/8c
はGo 1.5でGo自身で書き直される前の、C言語で書かれた初期のGoコンパイラの一部でした。現在ではcmd/compile
に統合されていますが、このコミットが作成された2013年時点では、これらのファイルが直接コード生成ロジックを担っていました。
- レジスタ割り当て (Register Allocation): コンパイラの重要な最適化フェーズの一つで、プログラムの変数をCPUのレジスタに割り当てるプロセスです。レジスタはメモリよりもはるかに高速なため、適切にレジスタを使用することでプログラムの実行速度が向上します。乗算のような演算では、オペランドや結果を特定のレジスタ(例:
AX
,DX
)に配置する必要がある場合があります。 OMUL
とOLMUL
:- これらはGoコンパイラの内部表現における演算子の種類です。
OMUL
: 通常の乗算操作を表します。OLMUL
: "Long Multiplication"、つまり長整数乗算を表します。これは、結果が通常のレジスタサイズを超える可能性がある場合に、複数のレジスタを使用して乗算結果を保持するような、より複雑なアセンブリ命令シーケンスを生成する必要があることを示唆します。例えば、32ビットの数値同士の乗算結果が64ビットになる場合などです。
reg[D_DX]
: コンパイラのレジスタ管理システムにおける内部変数です。D_DX
は、x86アーキテクチャにおけるDX
レジスタ(またはその64ビット版であるRDX
レジスタ)に対応するインデックスです。乗算命令(特にMUL
命令)では、結果の一部(上位ビット)がDX
レジスタに格納されることが多いため、コンパイラはDX
レジスタの使用状況を追跡する必要があります。reg[D_DX]++
はDX
レジスタが使用中であることをマークし、reg[D_DX]--
は使用が終了したことをマークします。gopcode
: Goコンパイラのコード生成関数の一つで、特定の演算子(opcode)に対応するアセンブリ命令を生成します。addable
/INDEXED
/hardconst
: これらはコンパイラがノード(式や変数を表す内部データ構造)の特性を判断するために使用するフラグや関数です。addable >= INDEXED
: オペランドがインデックス付きアドレス指定(例:[base + index*scale]
)に適しているかどうかを示します。hardconst(r)
: オペランドr
がハードウェアで直接扱える定数(例: 即値)であるかどうかをチェックします。
技術的詳細
このコミットの核心は、src/cmd/6c/cgen.c
とsrc/cmd/8c/cgen.c
内のcgen
関数における乗算処理のロジック変更です。cgen
関数は、Goの抽象構文木(AST)のノードを巡回し、対応する機械語命令を生成する役割を担っています。
変更前は、OMUL
(通常の乗算)の場合のみ、特定のレジスタ割り当てロジックが適用されていました。しかし、OLMUL
(長整数乗算)の場合も同様の最適化が必要であることが判明しました。
具体的な変更点は以下の通りです。
-
条件式の拡張: 変更前:
if(o == OMUL)
変更後:if(o == OMUL || o == OLMUL)
これにより、通常の乗算(OMUL
)だけでなく、長整数乗算(OLMUL
)の場合も、続くレジスタ割り当てとコード生成の最適化パスが適用されるようになりました。これは、コンパイラがOLMUL
と判断した場合でも、実際にはより単純なOMUL
命令で処理できるケースがあることを示唆しています。例えば、32ビット整数同士の乗算で結果が64ビットになる可能性がある場合でも、結果が32ビットに収まることが保証されるようなコンテキスト(例: 型キャストやマスク処理が後続する場合)では、長整数乗算命令ではなく、より効率的な単一レジスタ乗算命令を使用できる可能性があります。 -
DX
レジスタの管理: 変更前:/* should favour AX */
というコメントのみで、DX
レジスタの明示的な管理は行われていませんでした。 変更後:reg[D_DX]++; // for gopcode case OMUL // ... reg[D_DX]--;
これは、乗算処理のために
DX
レジスタ(またはRDX
)が一時的に使用されることをレジスタ割り当てシステムに通知するものです。x86アーキテクチャのMUL
命令やIMUL
命令(単一オペランド形式)は、乗算結果の上位ビットをDX
レジスタに格納するという特性があります。具体的には、MUL src
命令はAX * src
(16ビット)、EAX * src
(32ビット)、RAX * src
(64ビット)の乗算を行い、結果をそれぞれDX:AX
、EDX:EAX
、RDX:RAX
に格納します。 このreg[D_DX]++
は、gopcode(OMUL, ...)
が呼び出される前にDX
レジスタが予約されることを保証し、他の目的でDX
レジスタが誤って割り当てられることを防ぎます。これにより、コンパイラはDX
レジスタの利用状況を正確に把握し、不必要なレジスタの退避・復元(spill/reload)を避けることができます。reg[D_DX]--
は、乗算処理が完了し、結果がnn
に移動された後、DX
レジスタの予約を解除し、他の用途で利用可能にします。
これらの変更は、コンパイラが乗算命令を生成する際に、DX
レジスタの利用をより正確に管理し、不必要なレジスタの退避・復元を避けることで、生成されるアセンブリコードの効率を向上させることを目的としています。特に、32ビットや64ビットの乗算で結果が単一のレジスタに収まる場合でも、コンパイラが誤って長整数乗算のロジックを選択してしまう問題を解決し、よりコンパクトで高速なコードパスを生成できるようになりました。これにより、特にハッシュ計算のような頻繁に乗算が行われる処理において、顕著なパフォーマンス改善が実現されました。
コアとなるコードの変更箇所
変更はsrc/cmd/6c/cgen.c
とsrc/cmd/8c/cgen.c
のcgen
関数内、OMUL
(およびOLMUL
)演算子を処理するブロックに集中しています。
src/cmd/6c/cgen.c
--- a/src/cmd/6c/cgen.c
+++ b/src/cmd/6c/cgen.c
@@ -392,13 +392,13 @@ cgen(Node *n, Node *nn)\
}
}
- if(o == OMUL) {
+ if(o == OMUL || o == OLMUL) {
if(l->addable >= INDEXED) {
t = l;
l = r;
r = t;
}
- /* should favour AX */
+ reg[D_DX]++; // for gopcode case OMUL
regalloc(&nod, l, nn);
cgen(l, &nod);
if(r->addable < INDEXED || hardconst(r)) {
@@ -410,6 +410,7 @@ cgen(Node *n, Node *nn)\
gopcode(OMUL, n->type, r, &nod); /* addressible */
gmove(&nod, nn);
regfree(&nod);
+ reg[D_DX]--;
break;
}
src/cmd/8c/cgen.c
--- a/src/cmd/8c/cgen.c
+++ b/src/cmd/8c/cgen.c
@@ -404,13 +404,13 @@ cgen(Node *n, Node *nn)\
}
}
- if(o == OMUL) {
+ if(o == OMUL || o == OLMUL) {
if(l->addable >= INDEXED) {
t = l;
l = r;
r = t;
}
- /* should favour AX */
+ reg[D_DX]++; // for gopcode case OMUL
regalloc(&nod, l, nn);
cgen(l, &nod);
if(r->addable < INDEXED) {
@@ -422,6 +422,7 @@ cgen(Node *n, Node *nn)\
gopcode(OMUL, n->type, r, &nod); /* addressible */
gmove(&nod, nn);
regfree(&nod);
+ reg[D_DX]--;
break;
}
コアとなるコードの解説
両ファイルにおける変更は同一であり、cgen
関数内の乗算処理ブロックにあります。
-
if(o == OMUL)
からif(o == OMUL || o == OLMUL)
への変更: これは、コンパイラが乗算ノードを処理する際に、従来のOMUL
(通常の乗算)だけでなく、OLMUL
(長整数乗算)の場合も、この最適化パスに入るように拡張したものです。これにより、OLMUL
と判断された場合でも、オペランドの特性(例:addable >= INDEXED
やhardconst(r)
)に基づいて、より効率的な単一レジスタ乗算命令(例えば、IMUL
命令など)を生成する機会が生まれます。これは、コンパイラが型システムや中間表現の段階でOLMUL
と判断しても、実際のコード生成段階でより効率的な命令を選択できる柔軟性を持たせるための変更です。 -
reg[D_DX]++;
の追加: この行は、乗算処理のためにDX
レジスタ(またはRDX
)が一時的に使用されることをレジスタ割り当てシステムに通知します。x86アーキテクチャのMUL
命令やIMUL
命令(単一オペランド形式)は、乗算結果の上位ビットをDX
レジスタに格納するという特性があります。このreg[D_DX]++
は、gopcode(OMUL, ...)
が呼び出される前にDX
レジスタが予約されることを保証し、他の目的でDX
レジスタが誤って割り当てられることを防ぎます。これにより、コンパイラはDX
レジスタの利用状況を正確に把握し、不必要なレジスタの退避・復元(spill/reload)を避けることができます。 -
reg[D_DX]--;
の追加: 乗算処理が完了し、結果がnn
に移動された後、DX
レジスタの予約を解除します。これにより、DX
レジスタが他の用途で利用可能になります。
これらの変更は、コンパイラが乗算命令を生成する際に、DX
レジスタの利用をより正確に管理し、不必要なレジスタの退避・復元を避けることで、生成されるアセンブリコードの効率を向上させることを目的としています。特に、32ビットや64ビットの乗算で結果が単一のレジスタに収まる場合でも、コンパイラが誤って長整数乗算のロジックを選択してしまう問題を解決し、よりコンパクトで高速なコードパスを生成できるようになりました。
関連リンク
- Go Issue #5367: https://github.com/golang/go/issues/5367 このコミットが修正した具体的な問題について議論されています。
- Go CL 9040043: https://golang.org/cl/9040043 このコミットに対応するGoのコードレビュー(Change List)ページです。より詳細な議論やレビューコメントが含まれている可能性があります。
参考にした情報源リンク
- Go Issue Tracker: https://github.com/golang/go/issues
- Go Code Review: https://go.googlesource.com/go/+/refs/heads/master
- x86 Assembly Language Programming (for MUL/IMUL instruction details)
- Compiler Design Principles (for register allocation and code generation concepts)
- Go Compiler Internals (general knowledge about
cmd/6c
,cmd/8c
,cgen.c
) - Go 1.5 Bootstrapping: https://blog.golang.org/go1.5-bootstrapping (GoコンパイラがGo自身で書き直された経緯に関する情報)