[インデックス 14856] ファイルの概要
このコミットは、Goコンパイラの一部である cmd/5g
(ARMアーキテクチャ向けのGoコンパイラ) において、関数の戻り値レジスタの最適化を可能にする変更を導入しています。具体的には、関数が値を返す際に使用するレジスタ (ARMでは通常 R0
や F0
など) の扱いを改善し、不要なレジスタ間のデータ移動を削減することで、生成されるアセンブリコードの効率を向上させています。
コミット
commit bdd9f29780f7045f6e4c3782f38386d869542eef
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Fri Jan 11 12:29:14 2013 +0800
cmd/5g: allow optimization of return registers.
Modeled after CL 7030046 by daniel.morsing.
example program:
func f(x int) int { x -= 10; return x }
5g -S difference:
--- prog list "f" ---
0011 (x.go:7) TEXT add+0(SB),$0-8
0012 (x.go:7) MOVW x+0(FP),R0
-0013 (x.go:7) SUB $10,R0,R2
-0014 (x.go:7) MOVW R2,R0
-0015 (x.go:7) MOVW R2,.noname+4(FP)
-0016 (x.go:7) RET ,
+0013 (x.go:7) SUB $10,R0
+0014 (x.go:7) MOVW R0,.noname+4(FP)
+0015 (x.go:7) RET ,
R=dave, rsc
CC=golang-dev
https://golang.org/cl/7030047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bdd9f29780f7045f6e4c3782f38386d869542eef
元コミット内容
cmd/5g: allow optimization of return registers.
Modeled after CL 7030046 by daniel.morsing.
example program:
func f(x int) int { x -= 10; return x }
5g -S difference:
--- prog list "f" ---
0011 (x.go:7) TEXT add+0(SB),$0-8
0012 (x.go:7) MOVW x+0(FP),R0
-0013 (x.go:7) SUB $10,R0,R2
-0014 (x.go:7) MOVW R2,R0
-0015 (x.go:7) MOVW R2,.noname+4(FP)
-0016 (x.go:7) RET ,
+0013 (x.go:7) SUB $10,R0
+0014 (x.go:7) MOVW R0,.noname+4(FP)
+0015 (x.go:7) RET ,
R=dave, rsc
CC=golang-dev
https://golang.org/cl/7030047
変更の背景
この変更の背景には、Goコンパイラが生成するアセンブリコードの効率化があります。特に、関数が値を返す際に、その値が既に適切な戻り値レジスタに存在する場合、余分な MOVW
(Move Word) 命令を省略することで、コードサイズを削減し、実行速度を向上させることができます。
元のコードでは、SUB $10,R0,R2
のように計算結果を一時的に R2
レジスタに格納し、その後 MOVW R2,R0
で戻り値レジスタ R0
に移動させていました。これは、計算結果が直接戻り値レジスタに格納されるべきであるにもかかわらず、中間レジスタを介する非効率な処理でした。
この最適化は、daniel.morsing
による CL 7030046
をモデルにしていると記載されています。CL 7030046
は、Go言語におけるジェネリクスの実装に関連する go/types
パッケージへの型パラメータのサポート追加に関する変更であり、直接的なコードの最適化とは異なりますが、コンパイラの内部的な改善や設計思想の共有という点で関連性があると考えられます。このコミットは、その設計思想を cmd/5g
に適用したものです。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
- Go言語のコンパイラとアセンブリ: Go言語のプログラムは、Goコンパイラによって機械語に変換されます。この過程で、Goのソースコードは中間表現を経て、最終的に特定のアセンブリ言語に変換されます。
cmd/5g
は、ARMアーキテクチャ向けのアセンブリコードを生成するコンパイラの一部です。 - レジスタ: CPU内部にある高速な記憶領域で、計算やデータ転送に一時的に使用されます。ARMアーキテクチャでは、
R0
からR15
までの汎用レジスタや、浮動小数点演算用のF0
からF15
までの浮動小数点レジスタなどがあります。 - 戻り値レジスタ: 関数が値を返す際に、その値を格納するために慣習的に使用される特定のレジスタです。ARMアーキテクチャでは、通常、整数値の戻り値には
R0
が、浮動小数点値の戻り値にはF0
が使用されます。 - アセンブリ命令:
TEXT
: 関数の開始を示す命令。MOVW
: ワード(通常32ビット)のデータを移動する命令。SUB
: 減算命令。RET
: 関数の戻り命令。FP
(Frame Pointer): 現在のスタックフレームの基準点を示すレジスタ。関数引数やローカル変数は、このフレームポインタからのオフセットでアクセスされます。SB
(Static Base): グローバル変数や関数への参照の基準点を示すレジスタ。
- 最適化: コンパイラが行う処理の一つで、生成される機械語コードのサイズを小さくしたり、実行速度を速くしたりすることを目指します。レジスタ割り当ての最適化は、その重要な側面の一つです。
- CL (Change List): Goプロジェクトにおける変更の単位。Gitのコミットに相当しますが、Goコミュニティでは伝統的にCLという用語が使われます。
技術的詳細
このコミットの技術的詳細は、src/cmd/5g/peep.c
ファイル内の copyu
関数における ARET
(Return命令) の処理変更にあります。
copyu
関数は、コンパイラの「peephole optimizer」の一部であると考えられます。peephole optimizer は、生成されたアセンブリコードを短い範囲(peephole)で見て、より効率的な命令シーケンスに置き換える最適化手法です。
変更前の copyu
関数では、ARET
命令の処理において、戻り値がレジスタ (D_REG
または D_FREG
) であり、それが戻り値レジスタ (REGRET
または FREGRET
) である場合に return 2
を返していました。これは、特定の条件でコピー操作が不要であることを示唆している可能性があります。
変更後では、ARET
のケースで s != A
(ソースオペランドが存在する場合) に return 1
を返し、それ以外の場合に return 3
を返すように変更されています。
s != A
: これは、RET
命令にソースオペランドが指定されている場合を指します。つまり、戻り値が特定のレジスタから直接返されるようなケースです。この場合、return 1
は、コピー操作が1つ必要であることを示唆している可能性があります。return 3
: ソースオペランドがない場合、つまり戻り値が既に適切なレジスタに存在するか、あるいは特別な処理が必要な場合にreturn 3
を返します。この3
という値が具体的に何を意味するかは、copyu
関数の呼び出し元やコンパイラの他の部分のロジックに依存しますが、この変更によって、戻り値レジスタへの不要なMOVW
命令が抑制されるようになったと推測されます。
コミットメッセージの例で示されているアセンブリコードの差分は、この最適化の効果を明確に示しています。
変更前のアセンブリ:
0013 (x.go:7) SUB $10,R0,R2 ; R0から10を引いてR2に格納
0014 (x.go:7) MOVW R2,R0 ; R2の値をR0(戻り値レジスタ)に移動
0015 (x.go:7) MOVW R2,.noname+4(FP) ; R2の値をスタック上の戻り値領域に移動
0016 (x.go:7) RET , ; 関数から戻る
ここでは、SUB
命令の結果が R2
に格納され、その後 R0
に移動するという余分な MOVW
命令が存在します。また、スタック上の戻り値領域への移動も R2
から行われています。
変更後のアセンブリ:
0013 (x.go:7) SUB $10,R0 ; R0から10を引いてR0に格納 (R0は破壊される)
0014 (x.go:7) MOVW R0,.noname+4(FP) ; R0の値をスタック上の戻り値領域に移動
0015 (x.go:7) RET , ; 関数から戻る
変更後では、SUB
命令が直接 R0
を操作し、結果を R0
に格納しています。これにより、MOVW R2,R0
という中間的な命令が不要になり、コードが簡潔かつ効率的になっています。スタックへの移動も R0
から直接行われます。
この最適化は、レジスタ割り当ての改善と、命令の冗長性の排除に貢献しています。
コアとなるコードの変更箇所
変更は src/cmd/5g/peep.c
ファイルの copyu
関数内、case ARET:
ブロックにあります。
--- a/src/cmd/5g/peep.c
+++ b/src/cmd/5g/peep.c
@@ -1160,12 +1160,9 @@ copyu(Prog *p, Adr *v, Adr *s)\n \t\treturn 0;\n \n \tcase ARET:\t/* funny */
-\t\tif(v->type == D_REG)\n-\t\tif(v->reg == REGRET)\n-\t\t\treturn 2;\n-\t\tif(v->type == D_FREG)\n-\t\tif(v->reg == FREGRET)\n-\t\t\treturn 2;\
+\t\tif(s != A)\n+\t\t\treturn 1;\
+\t\treturn 3;\
\n \tcase ABL:\t/* funny */
\t\tif(v->type == D_REG) {\
コアとなるコードの解説
src/cmd/5g/peep.c
は、GoコンパイラのARMアーキテクチャ向けバックエンドにおけるpeephole optimizerの実装の一部です。copyu
関数は、特定の命令シーケンスにおいて、オペランドのコピーが不要であるか、あるいは最適化可能であるかを判断するために使用されます。
変更前のコードでは、ARET
(Return命令) の場合、戻り値が汎用レジスタ (D_REG
) か浮動小数点レジスタ (D_FREG
) であり、かつそれが戻り値レジスタ (REGRET
または FREGRET
) である場合に return 2
を返していました。これは、戻り値が既に適切なレジスタにあるため、追加のコピー操作が不要であることを示唆するロジックでした。
しかし、このロジックは、SUB $10,R0,R2
のように、計算結果が一時レジスタに格納され、その後戻り値レジスタに移動されるようなケースを十分に最適化できていませんでした。
変更後のコードでは、より一般的な条件で最適化を適用できるようにロジックが簡素化されています。
if(s != A)
: これは、RET
命令にソースオペランド (s
) が指定されているかどうかをチェックします。A
は通常、アドレスが指定されていないことを示すNULLのような値です。もしソースオペランドが存在する場合、つまり戻り値が特定の場所からコピーされる必要がある場合はreturn 1
を返します。return 3
: ソースオペランドがない場合、つまり戻り値が既に適切なレジスタに存在するか、あるいはコンパイラが直接戻り値レジスタに値を書き込むことができるようなケースではreturn 3
を返します。この3
という値が具体的にどのような最適化ヒントとして使われるかは、copyu
関数の呼び出し元であるpeephole optimizerの他の部分のロジックに依存しますが、結果として不要なMOVW
命令が削除されるようになりました。
この変更により、コンパイラは戻り値レジスタの利用をより賢く判断し、冗長なデータ移動を排除することで、より効率的なアセンブリコードを生成できるようになりました。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語のコンパイラに関するドキュメント (一般的な情報): Goの公式ドキュメントやブログ記事には、コンパイラの内部構造や最適化に関する情報が掲載されていることがあります。
参考にした情報源リンク
- コミットメッセージに記載されている
CL 7030046
に関する情報:- Web検索結果: "CL 7030046 is a Go change (Change List) that adds support for type parameters to the
go/types
package. This change is related to the implementation of generics in the Go language." - この情報から、
CL 7030046
は直接的なコード最適化ではなく、Go言語のジェネリクス実装に関連する型システムへの変更であることがわかります。今回のコミットが「Modeled after CL 7030046」とあるのは、特定のコードパターンや最適化の考え方を参考にしているという意味合いが強いと解釈できます。
- Web検索結果: "CL 7030046 is a Go change (Change List) that adds support for type parameters to the
- Go言語のアセンブリに関する一般的な情報:
- Go Assembly Language (公式ドキュメント): https://go.dev/doc/asm
- The Go Programming Language Specification (Go言語の仕様): https://go.dev/ref/spec
- ARMアーキテクチャのレジスタ規約に関する情報 (一般的な知識):
- ARM Architecture Reference Manual (ARMの公式ドキュメント)
- Calling Conventions for ARM Architecture (ARMの関数呼び出し規約に関する情報)
- コンパイラの最適化に関する一般的な情報 (peephole optimizationなど):
- Compiler Design (コンパイラ設計に関する教科書やオンラインリソース)
- Peephole Optimization (Wikipediaなど)
- GoのCL (Change List) の概念:
- Go Contribution Guide (Goへの貢献ガイド): https://go.dev/doc/contribute
- Gerrit Code Review (Goプロジェクトで使われているコードレビューシステム)# [インデックス 14856] ファイルの概要
このコミットは、Goコンパイラの一部である cmd/5g
(ARMアーキテクチャ向けのGoコンパイラ) において、関数の戻り値レジスタの最適化を可能にする変更を導入しています。具体的には、関数が値を返す際に使用するレジスタ (ARMでは通常 R0
や F0
など) の扱いを改善し、不要なレジスタ間のデータ移動を削減することで、生成されるアセンブリコードの効率を向上させています。
コミット
commit bdd9f29780f7045f6e4c3782f38386d869542eef
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Fri Jan 11 12:29:14 2013 +0800
cmd/5g: allow optimization of return registers.
Modeled after CL 7030046 by daniel.morsing.
example program:
func f(x int) int { x -= 10; return x }
5g -S difference:
--- prog list "f" ---
0011 (x.go:7) TEXT add+0(SB),$0-8
0012 (x.go:7) MOVW x+0(FP),R0
-0013 (x.go:7) SUB $10,R0,R2
-0014 (x.go:7) MOVW R2,R0
-0015 (x.go:7) MOVW R2,.noname+4(FP)
-0016 (x.go:7) RET ,
+0013 (x.go:7) SUB $10,R0
+0014 (x.go:7) MOVW R0,.noname+4(FP)
+0015 (x.go:7) RET ,
R=dave, rsc
CC=golang-dev
https://golang.org/cl/7030047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bdd9f29780f7045f6e4c3782f38386d869542eef
元コミット内容
cmd/5g: allow optimization of return registers.
Modeled after CL 7030046 by daniel.morsing.
example program:
func f(x int) int { x -= 10; return x }
5g -S difference:
--- prog list "f" ---
0011 (x.go:7) TEXT add+0(SB),$0-8
0012 (x.go:7) MOVW x+0(FP),R0
-0013 (x.go:7) SUB $10,R0,R2
-0014 (x.go:7) MOVW R2,R0
-0015 (x.go:7) MOVW R2,.noname+4(FP)
-0016 (x.go:7) RET ,
+0013 (x.go:7) SUB $10,R0
+0014 (x.go:7) MOVW R0,.noname+4(FP)
+0015 (x.go:7) RET ,
R=dave, rsc
CC=golang-dev
https://golang.org/cl/7030047
変更の背景
この変更の背景には、Goコンパイラが生成するアセンブリコードの効率化があります。特に、関数が値を返す際に、その値が既に適切な戻り値レジスタに存在する場合、余分な MOVW
(Move Word) 命令を省略することで、コードサイズを削減し、実行速度を向上させることができます。
元のコードでは、SUB $10,R0,R2
のように計算結果を一時的に R2
レジスタに格納し、その後 MOVW R2,R0
で戻り値レジスタ R0
に移動させていました。これは、計算結果が直接戻り値レジスタに格納されるべきであるにもかかわらず、中間レジスタを介する非効率な処理でした。
この最適化は、daniel.morsing
による CL 7030046
をモデルにしていると記載されています。CL 7030046
は、Go言語におけるジェネリクスの実装に関連する go/types
パッケージへの型パラメータのサポート追加に関する変更であり、直接的なコードの最適化とは異なりますが、コンパイラの内部的な改善や設計思想の共有という点で関連性があると考えられます。このコミットは、その設計思想を cmd/5g
に適用したものです。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
- Go言語のコンパイラとアセンブリ: Go言語のプログラムは、Goコンパイラによって機械語に変換されます。この過程で、Goのソースコードは中間表現を経て、最終的に特定のアセンブリ言語に変換されます。
cmd/5g
は、ARMアーキテクチャ向けのアセンブリコードを生成するコンパイラの一部です。 - レジスタ: CPU内部にある高速な記憶領域で、計算やデータ転送に一時的に使用されます。ARMアーキテクチャでは、
R0
からR15
までの汎用レジスタや、浮動小数点演算用のF0
からF15
までの浮動小数点レジスタなどがあります。 - 戻り値レジスタ: 関数が値を返す際に、その値を格納するために慣習的に使用される特定のレジスタです。ARMアーキテクチャでは、通常、整数値の戻り値には
R0
が、浮動小数点値の戻り値にはF0
が使用されます。 - アセンブリ命令:
TEXT
: 関数の開始を示す命令。MOVW
: ワード(通常32ビット)のデータを移動する命令。SUB
: 減算命令。RET
: 関数の戻り命令。FP
(Frame Pointer): 現在のスタックフレームの基準点を示すレジスタ。関数引数やローカル変数は、このフレームポインタからのオフセットでアクセスされます。SB
(Static Base): グローバル変数や関数への参照の基準点を示すレジスタ。
- 最適化: コンパイラが行う処理の一つで、生成される機械語コードのサイズを小さくしたり、実行速度を速くしたりすることを目指します。レジスタ割り当ての最適化は、その重要な側面の一つです。
- CL (Change List): Goプロジェクトにおける変更の単位。Gitのコミットに相当しますが、Goコミュニティでは伝統的にCLという用語が使われます。
技術的詳細
このコミットの技術的詳細は、src/cmd/5g/peep.c
ファイル内の copyu
関数における ARET
(Return命令) の処理変更にあります。
copyu
関数は、コンパイラの「peephole optimizer」の一部であると考えられます。peephole optimizer は、生成されたアセンブリコードを短い範囲(peephole)で見て、より効率的な命令シーケンスに置き換える最適化手法です。
変更前の copyu
関数では、ARET
命令の処理において、戻り値がレジスタ (D_REG
または D_FREG
) であり、それが戻り値レジスタ (REGRET
または FREGRET
) である場合に return 2
を返していました。これは、特定の条件でコピー操作が不要であることを示唆している可能性があります。
変更後では、ARET
のケースで s != A
(ソースオペランドが存在する場合) に return 1
を返し、それ以外の場合に return 3
を返すように変更されています。
s != A
: これは、RET
命令にソースオペランドが指定されている場合を指します。つまり、戻り値が特定のレジスタから直接返されるようなケースです。この場合、return 1
は、コピー操作が1つ必要であることを示唆している可能性があります。return 3
: ソースオペランドがない場合、つまり戻り値が既に適切なレジスタに存在するか、あるいは特別な処理が必要な場合にreturn 3
を返します。この3
という値が具体的に何を意味するかは、copyu
関数の呼び出し元やコンパイラの他の部分のロジックに依存しますが、この変更によって、戻り値レジスタへの不要なMOVW
命令が抑制されるようになったと推測されます。
コミットメッセージの例で示されているアセンブリコードの差分は、この最適化の効果を明確に示しています。
変更前のアセンブリ:
0013 (x.go:7) SUB $10,R0,R2 ; R0から10を引いてR2に格納
0014 (x.go:7) MOVW R2,R0 ; R2の値をR0(戻り値レジスタ)に移動
0015 (x.go:7) MOVW R2,.noname+4(FP) ; R2の値をスタック上の戻り値領域に移動
0016 (x.go:7) RET , ; 関数から戻る
ここでは、SUB
命令の結果が R2
に格納され、その後 R0
に移動するという余分な MOVW
命令が存在します。また、スタック上の戻り値領域への移動も R2
から行われています。
変更後のアセンブリ:
0013 (x.go:7) SUB $10,R0 ; R0から10を引いてR0に格納 (R0は破壊される)
0014 (x.go:7) MOVW R0,.noname+4(FP) ; R0の値をスタック上の戻り値領域に移動
0015 (x.go:7) RET , ; 関数から戻る
変更後では、SUB
命令が直接 R0
を操作し、結果を R0
に格納しています。これにより、MOVW R2,R0
という中間的な命令が不要になり、コードが簡潔かつ効率的になっています。スタックへの移動も R0
から直接行われます。
この最適化は、レジスタ割り当ての改善と、命令の冗長性の排除に貢献しています。
コアとなるコードの変更箇所
変更は src/cmd/5g/peep.c
ファイルの copyu
関数内、case ARET:
ブロックにあります。
--- a/src/cmd/5g/peep.c
+++ b/src/cmd/5g/peep.c
@@ -1160,12 +1160,9 @@ copyu(Prog *p, Adr *v, Adr *s)\n \t\treturn 0;\n \n \tcase ARET:\t/* funny */
-\t\tif(v->type == D_REG)\n-\t\tif(v->reg == REGRET)\n-\t\t\treturn 2;\n-\t\tif(v->type == D_FREG)\n-\t\tif(v->reg == FREGRET)\n-\t\t\treturn 2;\
+\t\tif(s != A)\n+\t\t\treturn 1;\
+\t\treturn 3;\
\n \tcase ABL:\t/* funny */
\t\tif(v->type == D_REG) {\
コアとなるコードの解説
src/cmd/5g/peep.c
は、GoコンパイラのARMアーキテクチャ向けバックエンドにおけるpeephole optimizerの実装の一部です。copyu
関数は、特定の命令シーケンスにおいて、オペランドのコピーが不要であるか、あるいは最適化可能であるかを判断するために使用されます。
変更前のコードでは、ARET
(Return命令) の場合、戻り値が汎用レジスタ (D_REG
) か浮動小数点レジスタ (D_FREG
) であり、かつそれが戻り値レジスタ (REGRET
または FREGRET
) である場合に return 2
を返していました。これは、戻り値が既に適切なレジスタにあるため、追加のコピー操作が不要であることを示唆するロジックでした。
しかし、このロジックは、SUB $10,R0,R2
のように、計算結果が一時レジスタに格納され、その後戻り値レジスタに移動されるようなケースを十分に最適化できていませんでした。
変更後のコードでは、より一般的な条件で最適化を適用できるようにロジックが簡素化されています。
if(s != A)
: これは、RET
命令にソースオペランド (s
) が指定されているかどうかをチェックします。A
は通常、アドレスが指定されていないことを示すNULLのような値です。もしソースオペランドが存在する場合、つまり戻り値が特定の場所からコピーされる必要がある場合はreturn 1
を返します。return 3
: ソースオペランドがない場合、つまり戻り値が既に適切なレジスタに存在するか、あるいはコンパイラが直接戻り値レジスタに値を書き込むことができるようなケースではreturn 3
を返します。この3
という値が具体的にどのような最適化ヒントとして使われるかは、copyu
関数の呼び出し元であるpeephole optimizerの他の部分のロジックに依存しますが、結果として不要なMOVW
命令が削除されるようになりました。
この変更により、コンパイラは戻り値レジスタの利用をより賢く判断し、冗長なデータ移動を排除することで、より効率的なアセンブリコードを生成できるようになりました。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語のコンパイラに関するドキュメント (一般的な情報): Goの公式ドキュメントやブログ記事には、コンパイラの内部構造や最適化に関する情報が掲載されていることがあります。
参考にした情報源リンク
- コミットメッセージに記載されている
CL 7030046
に関する情報:- Web検索結果: "CL 7030046 is a Go change (Change List) that adds support for type parameters to the
go/types
package. This change is related to the implementation of generics in the Go language." - この情報から、
CL 7030046
は直接的なコード最適化ではなく、Go言語のジェネリクス実装に関連する型システムへの変更であることがわかります。今回のコミットが「Modeled after CL 7030046」とあるのは、特定のコードパターンや最適化の考え方を参考にしているという意味合いが強いと解釈できます。
- Web検索結果: "CL 7030046 is a Go change (Change List) that adds support for type parameters to the
- Go言語のアセンブリに関する一般的な情報:
- Go Assembly Language (公式ドキュメント): https://go.dev/doc/asm
- The Go Programming Language Specification (Go言語の仕様): https://go.dev/ref/spec
- ARMアーキテクチャのレジスタ規約に関する情報 (一般的な知識):
- ARM Architecture Reference Manual (ARMの公式ドキュメント)
- Calling Conventions for ARM Architecture (ARMの関数呼び出し規約に関する情報)
- コンパイラの最適化に関する一般的な情報 (peephole optimizationなど):
- Compiler Design (コンパイラ設計に関する教科書やオンラインリソース)
- Peephole Optimization (Wikipediaなど)
- GoのCL (Change List) の概念:
- Go Contribution Guide (Goへの貢献ガイド): https://go.dev/doc/contribute
- Gerrit Code Review (Goプロジェクトで使われているコードレビューシステム)