[インデックス 10036] ファイルの概要
コミット
コミットハッシュ: d604cf780814f1f216f653a677d7a3e5eea7d88b
作成者: Russ Cox rsc@golang.org
作成日: 2011年10月18日 14:55:28 -0400
タイトル: 5g, 6g: comment out uses of -r
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d604cf780814f1f216f653a677d7a3e5eea7d88b
元コミット内容
このコミットは、GoコンパイラのARMアーキテクチャ用コンパイラ(5g)とamd64アーキテクチャ用コンパイラ(6g)において、デバッグフラグ-r
の使用を無効化する変更を行いました。
変更されたファイル:
src/cmd/5g/gsubr.c
: 4行変更(2追加、2削除)src/cmd/6g/gobj.c
: 2行変更(1追加、1削除)
具体的な変更:
if(debug['r'])
→if(0 && debug['r'])
の形で、デバッグフラグの条件を無効化- レジスタ割り当て関数
regalloc()
とregfree()
でのデバッグ出力を無効化 - 埋め込み型トランポリン生成関数
genembedtramp()
でのデバッグ出力を無効化
変更の背景
2011年当時、Goコンパイラは急速に開発が進んでいた時期で、様々なデバッグ機能やフラグが実装されていました。特にレジスタ割り当てに関するデバッグ機能は、コンパイラの最適化や動作確認において重要な役割を果たしていました。
しかし、この-r
フラグによるデバッグ出力は、以下の理由で無効化される必要がありました:
- パフォーマンス上の問題: デバッグ出力が大量に発生し、コンパイル時間が大幅に増加
- 出力の混乱: 通常のコンパイル過程で不要な情報が出力される
- 保守性の問題: デバッグコードが本番環境でも動作し、予期しない副作用を引き起こす可能性
前提知識の解説
Plan 9からの継承
GoコンパイラはPlan 9オペレーティングシステムのコンパイラツールチェーンを基盤として開発されました。Plan 9のコンパイラは、アーキテクチャ別に異なる名前が付けられており、以下の命名規則を持っていました:
- 5系: ARM アーキテクチャ(5a, 5c, 5l, 5g)
- 6系: amd64 アーキテクチャ(6a, 6c, 6l, 6g)
- 8系: 386 アーキテクチャ(8a, 8c, 8l, 8g)
デバッグフラグシステム
Plan 9コンパイラでは、デバッグフラグを配列debug[]
で管理しており、文字をインデックスとして使用していました。debug['r']
は、レジスタ割り当て(Register allocation)に関するデバッグ情報を出力するフラグでした。
レジスタ割り当てとは
レジスタ割り当て(Register allocation)は、コンパイラの最適化において最も重要な処理の一つで、プログラムの変数をCPUのレジスタに効率的に割り当てる処理です。適切なレジスタ割り当ては、メモリアクセスを削減し、プログラムの実行速度を大幅に向上させます。
技術的詳細
変更手法の技術的意味
if(debug['r'])
を if(0 && debug['r'])
に変更することで、以下の技術的効果が得られます:
- 条件短絡評価:
0 && debug['r']
は常に0
(偽)となり、デバッグコードが実行されません - コンパイル時最適化: 最適化コンパイラは、到達不可能なコードとして削除します
- 保守性の確保: 元のデバッグコードは残されているため、後で簡単に復活できます
レジスタ割り当てアルゴリズム
当時のGoコンパイラは、局所レジスタ割り当て(Local Register Allocation)を採用していました。これは関数内でのレジスタ使用を最適化する手法で、以下の特徴があります:
- グラフ彩色問題: レジスタ割り当てをグラフの頂点彩色問題として扱う
- 生存期間分析: 変数の生存期間を分析し、重複しない変数を同じレジスタに割り当て
- スピル処理: レジスタが不足した場合、変数をメモリに退避
コアとなるコードの変更箇所
src/cmd/5g/gsubr.c の変更
// 変更前
if(debug['r']) {
fixfree = 0;
for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
if(reg[i] == 0)
fixfree++;
// レジスタ使用状況の詳細デバッグ出力
}
// 変更後
if(0 && debug['r']) {
fixfree = 0;
for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
if(reg[i] == 0)
fixfree++;
// デバッグ出力は実行されない
}
src/cmd/6g/gobj.c の変更
// 変更前
if(debug['r'])
print("genembedtramp %T %T %S\n", rcvr, method, newnam);
// 変更後
if(0 && debug['r'])
print("genembedtramp %T %T %S\n", rcvr, method, newnam);
コアとなるコードの解説
regalloc() 関数
この関数は新しいレジスタを割り当てる際に呼び出されます。デバッグモードでは、以下の情報を出力していました:
- fixfree: 利用可能な汎用レジスタの数
- floatfree: 利用可能な浮動小数点レジスタの数
- レジスタ使用状況: 各レジスタの使用状態
regfree() 関数
この関数はレジスタを解放する際に呼び出されます。デバッグモードでは、レジスタ解放後の状態を出力していました。
genembedtramp() 関数
この関数は埋め込み型のメソッド呼び出しのためのトランポリンコードを生成します。デバッグモードでは、以下の情報を出力していました:
- rcvr: レシーバ型の情報
- method: メソッド型の情報
- newnam: 生成される新しいシンボル名
関連リンク
- Go Programming Language FAQ
- Plan 9 C Compilers Documentation
- Go Wiki: Go on ARM
- Go Assembler Documentation
- Register Allocation in the Go Compiler