[インデックス 1090] ファイルの概要
コミット
コミットハッシュ: 79f5697b0268eb1a84a252aa32def124991d3f34
作成者: Ken Thompson ken@golang.org
日付: 2008年11月7日 16:05:17 -0800
メッセージ: "byte multiply"
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/79f5697b0268eb1a84a252aa32def124991d3f34
元コミット内容
このコミットでは、src/cmd/6g/gen.c の cgen_bmul
関数(byte multiply code generation)に対して重要な最適化が行われています。変更は1ファイルで、22行の追加と10行の削除が含まれています。
変更の背景
2008年当時、Go言語はまだ開発初期段階にあり、Ken Thompson、Rob Pike、Robert Griesemeーによって設計が進められていました。このコミットは、6g(AMD64アーキテクチャ向けGoコンパイラ)におけるバイト単位の乗算処理を最適化する目的で行われました。
当時のGoコンパイラは、各アーキテクチャ用に別々のコンパイラ(6g for AMD64、8g for i386など)を持っていた時代で、まだ現在のような統合されたコンパイラツールチェーンは存在していませんでした。
前提知識の解説
Goコンパイラの歴史的背景
- 6g: AMD64(x86-64)アーキテクチャ向けのGoコンパイラ
- 8g: i386(32-bit x86)アーキテクチャ向けのGoコンパイラ
- 5g: ARM アーキテクチャ向けのGoコンパイラ
レジスタ割り当ての概念
コンパイラにおけるレジスタ割り当て(register allocation)は、プログラムの変数や一時的な値を限られた数のCPUレジスタに効率的に配置する重要な最適化処理です。
コード生成関数の役割
- cgen(): 一般的なコード生成関数
- regalloc(): レジスタ割り当て関数
- regfree(): レジスタ解放関数
- gins(): 機械語命令生成関数
- optoas(): 最適なアセンブリ命令選択関数
技術的詳細
変更前の問題点
- 型の早期固定: 関数の開始時に16bit型(TUINT16/TINT16)に型を固定していた
- レジスタ割り当ての非効率性: 元の型情報を無視してレジスタ割り当てを行っていた
- 中間結果の処理不備: 乗算結果の型変換が適切に行われていなかった
変更後の改善点
- 型情報の保持: 元のオペランドの型情報を保持してレジスタ割り当てを行う
- 段階的な型変換: 必要に応じて16bit型への変換を後から行う
- 適切なレジスタ管理: 追加のレジスタ(n3)を使用してより効率的な処理を実現
コアとなるコードの変更箇所
変更前のコード構造
// 早期の型固定
t = types[TUINT16];
if(issigned[nl->type->etype])
t = types[TINT16];
// 固定された型でレジスタ割り当て
regalloc(&n1, t, nl);
regalloc(&n2, t, nr);
変更後のコード構造
// 元の型でレジスタ割り当て
regalloc(&n1, nl->type, res);
regalloc(&n2, nr->type, N);
// 後から16bit型への変換
t = types[TUINT16];
if(issigned[nl->type->etype])
t = types[TINT16];
regalloc(&n3, t, &n2);
cgen(&n2, &n3);
コアとなるコードの解説
1. レジスタ割り当ての最適化
// 変更前: 強制的に16bit型でレジスタ割り当て
regalloc(&n1, t, nl);
regalloc(&n2, t, nr);
// 変更後: 元の型でレジスタ割り当て
regalloc(&n1, nl->type, res);
regalloc(&n2, nr->type, N);
この変更により、オペランドの元の型情報を保持してレジスタ割り当てを行うことで、不要な型変換を避けることができます。
2. 段階的な型変換処理
// 短いレジスタにコピー
t = types[TUINT16];
if(issigned[nl->type->etype])
t = types[TINT16];
regalloc(&n3, t, &n2);
cgen(&n2, &n3);
regfree(&n3);
regalloc(&n3, t, &n1);
cgen(&n1, &n3);
新しい実装では、必要に応じて16bit型への変換を行い、乗算処理のために適切なレジスタサイズを確保します。
3. 結果の処理改善
// 変更前: 単純な移動
gmove(&n1, res);
// 変更後: 段階的な結果処理
cgen(&n3, &n1);
cgen(&n1, res);
この変更により、乗算結果をより効率的に結果レジスタに移動できるようになりました。
4. リソース管理の改善
regfree(&n1);
regfree(&n2);
regfree(&n3); // 新しく追加されたレジスタの解放
追加されたレジスタ(n3)の適切な解放により、メモリリークを防ぎます。