[インデックス 13248] ファイルの概要
このコミットは、GoコンパイラのARMアーキテクチャ向けバックエンド(cmd/5c
、cmd/5g
、cmd/5l
)において、利用可能なレジスタの範囲を拡張するものです。具体的には、汎用レジスタR12
と、浮動小数点レジスタF8
からF15
の使用を有効にしています。これにより、コンパイラがより多くのレジスタを効率的に利用できるようになり、生成されるコードのパフォーマンス向上に寄与する可能性があります。
コミット
commit d87bc2f0c0e12672592e7dbf30d2c439376891d9
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Sat Jun 2 12:54:28 2012 -0400
cmd/5c, cmd/5g, cmd/5l: enable use of R12, F8-F15
R=dave, rsc
CC=golang-dev
https://golang.org/cl/6248070
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d87bc2f0c0e12672592e7dbf30d2c439376891d9
元コミット内容
cmd/5c, cmd/5g, cmd/5l: enable use of R12, F8-F15
変更の背景
Go言語は、その設計当初からクロスプラットフォーム対応を重視しており、様々なアーキテクチャで動作するようにコンパイラとランタイムが開発されてきました。ARMアーキテクチャは、モバイルデバイスや組み込みシステムで広く利用されており、Go言語がこれらの分野で普及するためには、ARM向けコード生成の効率性が重要でした。
このコミットが行われた2012年頃は、Go言語がまだ比較的新しい言語であり、各アーキテクチャ向けのコンパイラバックエンドが活発に開発・最適化されていた時期にあたります。特に、レジスタ割り当てはコンパイラの性能を左右する重要な要素であり、利用可能なレジスタを最大限に活用することで、メモリへのアクセスを減らし、命令の実行速度を向上させることができます。
以前のGoコンパイラのARMバックエンドでは、特定のレジスタ(R12
やF8-F15
)が利用対象から除外されていた可能性があります。これは、初期の実装の簡素化、特定のABI(Application Binary Interface)の制約、あるいは単にまだ最適化が及んでいなかったためかもしれません。しかし、より高性能なコードを生成するためには、これらの利用可能なレジスタをコンパイラが認識し、適切に割り当てられるようにする必要がありました。
この変更は、GoコンパイラのARMアーキテクチャ向けコード生成において、より多くのレジスタをレジスタ割り当て器が利用できるようにすることで、生成されるバイナリのパフォーマンスを向上させることを目的としています。特に、浮動小数点演算を多用するアプリケーションや、多くのローカル変数を持つ関数において、この変更は顕著な効果をもたらす可能性があります。
前提知識の解説
ARMアーキテクチャのレジスタ
ARMアーキテクチャは、RISC(Reduced Instruction Set Computer)ベースのプロセッサであり、その命令セットとレジスタセットは効率的な実行を重視して設計されています。
-
汎用レジスタ (General-Purpose Registers - GPRs): ARMプロセッサには、通常
R0
からR15
までの汎用レジスタがあります。R0-R12
: 通常のデータ操作やアドレス計算に使用されます。R13 (SP)
: スタックポインタ。R14 (LR)
: リンクレジスタ。関数呼び出しからの戻りアドレスを保持します。R15 (PC)
: プログラムカウンタ。次に実行される命令のアドレスを保持します。R9
、R10
、R11
、R12
は、特定のABIやコンパイラの慣習によって、特別な用途(例: スクラッチレジスタ、フレームポインタ、スレッドローカルストレージポインタなど)に割り当てられることがあります。このコミットではR12
の利用を有効にしています。
-
浮動小数点レジスタ (Floating-Point Registers - FPRs): ARMプロセッサには、VFP(Vector Floating Point)拡張などの浮動小数点ユニット(FPU)が搭載されており、
S0
からS31
(単精度)またはD0
からD31
(倍精度)といった浮動小数点レジスタが提供されます。D0-D7
: 関数呼び出しの引数や戻り値に使用されることが多いです。D8-D15
: 呼び出し元保存(caller-saved)レジスタとして使用されることが多く、関数内で自由に使用できますが、呼び出し元がその値を保持したい場合は、呼び出し元が保存・復元する必要があります。D16-D31
: 呼び出し先保存(callee-saved)レジスタとして使用されることが多く、関数内で使用する場合は、関数がその値を保存・復元する責任があります。 このコミットではF8
からF15
(おそらく倍精度レジスタD8
からD15
に対応)の利用を有効にしています。
Goコンパイラのバックエンド
Goコンパイラは、フロントエンド(Goソースコードの解析、AST生成、型チェックなど)とバックエンド(中間表現からターゲットアーキテクチャの機械語への変換、レジスタ割り当て、最適化など)に分かれています。
cmd/5c
: ARMアーキテクチャ向けのGoコンパイラのC言語部分。レジスタの定義や、レジスタ割り当てに関する低レベルなロジックが含まれることがあります。cmd/5g
: ARMアーキテクチャ向けのGoコンパイラのGo言語部分。Go言語で書かれたコンパイラの主要なロジック、中間表現の処理、レジスタ割り当てアルゴリズムなどが含まれます。cmd/5l
: ARMアーキテクチャ向けのGoリンカ。コンパイルされたオブジェクトファイルを結合し、実行可能なバイナリを生成します。この過程で、レジスタの使用に関する情報(例: レジスタのビットマスク定義など)が必要となる場合があります。
レジスタ割り当て
レジスタ割り当ては、コンパイラの最適化フェーズの一つで、プログラムの変数や中間結果をCPUのレジスタに割り当てるプロセスです。レジスタはメモリよりもはるかに高速にアクセスできるため、レジスタを効率的に使用することはプログラムの実行速度に直結します。
- レジスタの可用性: コンパイラが利用できるレジスタの数が多いほど、より多くの値をレジスタに保持でき、メモリへのアクセスを減らすことができます。
- レジスタのビットマスク: コンパイラ内部では、どのレジスタが利用可能であるか、あるいはどのレジスタが現在使用中であるかを管理するために、ビットマスクがよく使用されます。各ビットが特定のレジスタに対応し、ビットがセットされていればそのレジスタが利用可能/使用中であることを示します。
技術的詳細
このコミットは、GoコンパイラのARMバックエンドにおけるレジスタ管理ロジックを修正し、R12
レジスタとF8-F15
浮動小数点レジスタをレジスタ割り当ての対象に含めるように変更しています。
具体的には、以下のファイルが変更されています。
-
src/cmd/5c/reg.c
:RtoB
関数(Register to Bitmask): 汎用レジスタ番号をビットマスクに変換する関数。R12
が除外されないように条件が変更されています。以前はREGTMP-2
(おそらくR9
とR10
)より大きいレジスタは除外されていましたが、R12
は除外対象から外されました。BtoR
関数(Bitmask to Register): ビットマスクからレジスタ番号を抽出する関数。R12
に対応するビット(0x1000
、つまり1L << 12
)がマスクに追加され、R12
が正しく認識されるようになりました。FtoB
関数(Floating-point Register to Bitmask): 浮動小数点レジスタ番号をビットマスクに変換する関数。F15
まで(つまり31
まで)を有効にするようにコメントとロジックが更新されています。BtoF
関数(Bitmask to Floating-point Register): ビットマスクから浮動小数点レジスタ番号を抽出する関数。浮動小数点レジスタのビットマスク範囲が0xfffc0000L
に拡張され、F8-F15
に対応するビットが認識されるようになりました。
-
src/cmd/5g/reg.c
:NREGVAR
マクロ: レジスタ変数の数を24
から32
に増やしています。これは、利用可能なレジスタの総数が増えたことを反映しています。REGBITS
マクロ: レジスタビットマスクの最大値を0xffffff
から0xffffffff
(32ビット全て)に拡張しています。これにより、より多くのレジスタをビットマスクで表現できるようになります。regname
配列: 浮動小数点レジスタの名称リストに.F8
から.F15
が追加されています。RtoB
、BtoR
、FtoB
、BtoF
関数:src/cmd/5c/reg.c
と同様のロジック変更が適用されています。
-
src/cmd/5l/5.out.h
:REGTMP
マクロ:11
のままですが、REGSB
(Static Base Register)の定義が削除されています。これは、R12
がREGSB
として予約されていた可能性があり、その予約を解除して汎用レジスタとして利用可能にしたことを示唆しています。NFREG
マクロ: 浮動小数点レジスタの数を8
から16
に増やしています。これはF0-F7
に加えてF8-F15
が利用可能になったことを直接的に示しています。FREGTMP
マクロ: 浮動小数点一時レジスタの最大番号が15
に設定されています。
これらの変更により、Goコンパイラのレジスタ割り当て器は、R12
とF8-F15
を通常の汎用レジスタおよび浮動小数点レジスタとして認識し、コード生成時にこれらを活用できるようになります。これにより、レジスタの競合が減少し、メモリへのスピル(レジスタからメモリへの退避)が削減され、結果として生成されるコードの実行効率が向上します。
コアとなるコードの変更箇所
src/cmd/5c/reg.c
--- a/src/cmd/5c/reg.c
+++ b/src/cmd/5c/reg.c
@@ -1150,12 +1150,13 @@ addreg(Adr *a, int rn)
* 1 R1
* ... ...
* 10 R10
+ * 12 R12
*/
int32
RtoB(int r)
{
- if(r < 2 || r >= REGTMP-2) // excluded R9 and R10 for m and g
+ if(r < 2 || (r >= REGTMP-2 && r != 12)) // excluded R9 and R10 for m and g, but not R12
return 0;
return 1L << r;
}
@@ -1163,7 +1164,7 @@ RtoB(int r)
int
BtoR(int32 b)
{
- b &= 0x01fcL; // excluded R9 and R10 for m and g
+ b &= 0x11fcL; // excluded R9 and R10 for m and g, but not R12
if(b == 0)
return 0;
return bitno(b);
@@ -1174,7 +1175,7 @@ BtoR(int32 b)
* 18 F2
* 19 F3
* ... ...
- * 23 F7
+ * 31 F15
*/
int32
FtoB(int f)
@@ -1189,7 +1190,7 @@ int
BtoF(int32 b)
{
- b &= 0xfc0000L;
+ b &= 0xfffc0000L;
if(b == 0)
return 0;
return bitno(b) - 16;
src/cmd/5g/reg.c
--- a/src/cmd/5g/reg.c
+++ b/src/cmd/5g/reg.c
@@ -34,8 +34,8 @@
#include "gg.h"
#include "opt.h"
-#define NREGVAR 24
-#define REGBITS ((uint32)0xffffff)
+#define NREGVAR 32
+#define REGBITS ((uint32)0xffffffff)
#define P2R(p)\t(Reg*)(p->reg)
void addsplits(void);
@@ -160,6 +160,14 @@ static char* regname[] = {
".F5",
".F6",
".F7",
+ ".F8",
+ ".F9",
+ ".F10",
+ ".F11",
+ ".F12",
+ ".F13",
+ ".F14",
+ ".F15",
};
void
@@ -1486,11 +1494,12 @@ addreg(Adr *a, int rn)
* 1 R1
* ... ...
* 10 R10
+ * 12 R12
*/
int32
RtoB(int r)
{
-\tif(r >= REGTMP-2)\t// excluded R9 and R10 for m and g
+\tif(r >= REGTMP-2 && r != 12)\t// excluded R9 and R10 for m and g, but not R12
return 0;
return 1L << r;
}
@@ -1498,7 +1507,7 @@ RtoB(int r)
int
BtoR(int32 b)
{
-\tb &= 0x01fcL;\t// excluded R9 and R10 for m and g
+\tb &= 0x11fcL;\t// excluded R9 and R10 for m and g, but not R12
if(b == 0)
return 0;
return bitno(b);
@@ -1509,7 +1518,7 @@ BtoR(int32 b)
* 18 F2
* 19 F3
* ... ...
- * 23 F7
+ * 31 F15
*/
int32
FtoB(int f)
@@ -1524,7 +1533,7 @@ int
BtoF(int32 b)
{
-\tb &= 0xfc0000L;\n+\tb &= 0xfffc0000L;
+\tb &= 0xfffc0000L;
if(b == 0)
return 0;
return bitno(b) - 16;
src/cmd/5l/5.out.h
--- a/src/cmd/5l/5.out.h
+++ b/src/cmd/5l/5.out.h
@@ -49,12 +49,11 @@
#define REGM (REGEXT-1)
/* compiler allocates external registers R10 down */
#define REGTMP 11
-#define REGSB 12
#define REGSP 13
#define REGLINK 14
#define REGPC 15
-#define NFREG 8
+#define NFREG 16
#define FREGRET 0
#define FREGEXT 7
#define FREGTMP 15
コアとなるコードの解説
src/cmd/5c/reg.c
および src/cmd/5g/reg.c
の変更
これらのファイルは、GoコンパイラのARMバックエンドにおけるレジスタのビットマスク表現と、レジスタ番号とビットマスク間の変換ロジックを定義しています。
RtoB
関数 (R12
の有効化):- 変更前:
if(r < 2 || r >= REGTMP-2)
- これは、レジスタ番号
r
が2
未満(R0
,R1
)であるか、またはREGTMP-2
(おそらくR9
)以上である場合に0
を返していました。つまり、R9
とR10
が除外され、さらにR12
もこの条件に引っかかって除外されていた可能性があります。
- これは、レジスタ番号
- 変更後:
if(r < 2 || (r >= REGTMP-2 && r != 12))
- 新しい条件では、
r >= REGTMP-2
であってもr
が12
(R12
)でない限りは除外するというロジックに変更されています。これにより、R12
がレジスタ割り当ての対象として認識されるようになります。
- 新しい条件では、
- 変更前:
BtoR
関数 (R12
のビットマスク認識):- 変更前:
b &= 0x01fcL;
- このビットマスクは、
R2
からR8
、そしてR10
に対応するビット(0x0004
から0x0100
、0x0200
)のみを抽出していました。R12
に対応するビット(0x1000
)は含まれていませんでした。
- このビットマスクは、
- 変更後:
b &= 0x11fcL;
- 新しいビットマスク
0x11fcL
は、0x01fcL
に0x1000L
(1L << 12
、つまりR12
に対応するビット)が追加されています。これにより、ビットマスクからR12
が正しく抽出できるようになります。
- 新しいビットマスク
- 変更前:
FtoB
関数 (F8-F15
の有効化):- コメントの変更:
* 23 F7
から* 31 F15
へ。これは、浮動小数点レジスタの範囲がF7
までではなくF15
まで拡張されたことを示しています。
- コメントの変更:
BtoF
関数 (F8-F15
のビットマスク認識):- 変更前:
b &= 0xfc0000L;
- このビットマスクは、
F2
からF7
に対応するビット(0x040000
から0x800000
)のみを抽出していました。
- このビットマスクは、
- 変更後:
b &= 0xfffc0000L;
- 新しいビットマスク
0xfffc0000L
は、F8
からF15
に対応するビット(0x1000000
から0x80000000
)を含むように拡張されています。これにより、ビットマスクからF8-F15
が正しく抽出できるようになります。
- 新しいビットマスク
- 変更前:
src/cmd/5g/reg.c
のマクロ変更:NREGVAR
を24
から32
へ: これは、コンパイラが管理するレジスタ変数の総数が増加したことを示します。REGBITS
を0xffffff
から0xffffffff
へ: これは、レジスタのビットマスクが24ビットから32ビットに拡張され、より多くのレジスタを表現できるようになったことを示します。regname
配列に".F8"
から".F15"
を追加: これは、これらの浮動小数点レジスタがコンパイラによって認識され、デバッグ情報やエラーメッセージなどで正しく表示されるようにするための変更です。
src/cmd/5l/5.out.h
の変更
このファイルは、Goリンカが使用するARMアーキテクチャ固有の定数を定義しています。
REGSB
の削除:#define REGSB 12
の行が削除されています。これは、以前はR12
がREGSB
(Static Base Register)として予約されており、汎用レジスタとしては利用できなかったことを示唆しています。この定義を削除することで、R12
が汎用レジスタとして解放され、コンパイラが利用できるようになります。
NFREG
の変更:#define NFREG 8
から#define NFREG 16
へ。これは、利用可能な浮動小数点レジスタの総数が8個(F0-F7
)から16個(F0-F15
)に倍増したことを明確に示しています。
これらの変更は、GoコンパイラがARMアーキテクチャ上でより多くのレジスタを効果的に利用できるようにするための基盤を構築しています。これにより、レジスタ割り当ての効率が向上し、結果として生成されるGoプログラムの実行速度が改善されることが期待されます。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go CL (Change List) 6248070: https://golang.org/cl/6248070 (このコミットに対応するGoのコードレビューシステム上の変更リスト)
参考にした情報源リンク
- ARM Architecture Reference Manual (ARM社の公式ドキュメント)
- Go Compiler Internals (Goコンパイラの内部構造に関するドキュメントやブログ記事)
- Register Allocation in Compilers (コンパイラのレジスタ割り当てに関する一般的な情報)
- Go言語のARMポートに関する議論やメーリングリストのアーカイブ (golang-devなど)