Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 17403] ファイルの概要

このコミットは、Goコンパイラの一部であるcmd/5g(ARMアーキテクチャ向けコンパイラ)におけるレジスタ割り当てのバグ修正に関するものです。具体的には、汎用レジスタ(R13)と浮動小数点レジスタ(F3)の間で番号が衝突し、コンパイラが誤ったコードを生成する問題に対処しています。

コミット

commit 4fc7ff497d4133899afed2a3f866d32cb7df5df0
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Tue Aug 27 21:09:16 2013 +0200

    cmd/5g: avoid clash between R13 and F3 registers.
    
    Fixes #6247.
    
    R=golang-dev, lucio.dere, bradfitz
    CC=golang-dev
    https://golang.org/cl/13216043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/4fc7ff497d4133899afed2a3f866d32cb7df5df0

元コミット内容

cmd/5g: avoid clash between R13 and F3 registers. Fixes #6247.

このコミットは、cmd/5gコンパイラがR13レジスタとF3レジスタの間で発生する衝突を回避するためのものです。これはGoのIssue 6247を修正します。

変更の背景

Goコンパイラは、ソースコードを機械語に変換する際に、CPUのレジスタを効率的に利用してパフォーマンスを最大化します。レジスタ割り当てはコンパイラの重要な部分であり、どの値をどのレジスタに格納するかを決定します。

このコミットの背景には、ARMアーキテクチャ向けのGoコンパイラであるcmd/5gにおけるレジスタ割り当てのバグがありました。具体的には、汎用レジスタ(Rレジスタ)と浮動小数点レジスタ(Fレジスタ)の内部的な番号付けに問題があり、R13レジスタとF3レジスタが同じ内部インデックスを共有してしまうという状況が発生していました。

このようなレジスタの衝突が発生すると、コンパイラは異なる種類のデータ(例えば整数と浮動小数点数)を同じ物理レジスタに割り当てようとする可能性があります。これにより、誤った値が使用されたり、プログラムがクラッシュしたりするなどの予期せぬ動作を引き起こす可能性がありました。

Issue 6247の報告では、特にcomplex128型(複素数型)の演算においてこの問題が顕在化しました。complex128は2つのfloat64値(実部と虚部)で構成されるため、浮動小数点レジスタを多用します。この型の演算が絡むと、レジスタ割り当てのバグが露呈しやすかったと考えられます。

前提知識の解説

  • Goコンパイラとcmd/5g: Go言語のソースコードを機械語に変換するプログラムがGoコンパイラです。Goはクロスコンパイルをサポートしており、異なるアーキテクチャ向けのコンパイラが用意されています。cmd/5gは、ARMアーキテクチャ(例: Raspberry Piなどの組み込みシステムや一部のモバイルデバイス)向けのGoコンパイラを指します。5はARMアーキテクチャのGoにおける慣例的なプレフィックスです。
  • CPUレジスタ: CPU内部にある高速な記憶領域です。プログラムの実行中にデータや命令を一時的に保持するために使われます。レジスタには様々な種類があり、それぞれ特定の用途があります。
    • 汎用レジスタ (General Purpose Registers - GPRs): 整数演算やアドレス計算など、幅広い用途に使われるレジスタです。ARMアーキテクチャではR0からR15などのレジスタがあります。
    • 浮動小数点レジスタ (Floating Point Registers - FPRs): 浮動小数点数(実数)の演算に特化したレジスタです。ARMアーキテクチャではF0からF31などのレジスタがあります(VFP/NEON拡張による)。
  • レジスタ割り当て (Register Allocation): コンパイラの最適化フェーズの一つで、プログラム中の変数や中間結果をどのCPUレジスタに割り当てるかを決定するプロセスです。レジスタは数が限られているため、効率的な割り当てはプログラムの実行速度に大きく影響します。
  • レジスタの番号付け: コンパイラの内部では、各レジスタに一意の番号(インデックス)を割り当てて管理することが一般的です。この番号付けが適切でないと、異なる種類のレジスタが同じ番号を持ってしまい、衝突が発生する可能性があります。
  • complex128: Go言語の組み込み型の一つで、倍精度浮動小数点数(float64)の実部と虚部を持つ複素数型です。複素数演算は、科学技術計算や信号処理などで広く用いられます。

技術的詳細

このバグは、src/cmd/5g/gg.hというヘッダファイル内のレジスタ割り当てに関するマクロ定義に起因していました。

Goコンパイラのcmd/5gは、レジスタを内部的に管理するために、汎用レジスタと浮動小数点レジスタにそれぞれ開始インデックスを定義していました。

変更前のコードでは、浮動小数点レジスタの開始インデックスREGALLOC_F0が以下のように定義されていました。

#define REGALLOC_R0 0
#define REGALLOC_RMAX REGEXT
#define REGALLOC_F0 (REGALLOC_RMAX+1) // 問題の箇所
#define REGALLOC_FMAX (REGALLOC_F0 + FREGEXT)

ここで、REGALLOC_RMAXは汎用レジスタの最大インデックスを表し、REGEXTは汎用レジスタの数を定義する定数です。REGALLOC_F0は、汎用レジスタの直後のインデックスから浮動小数点レジスタの番号付けを開始するように意図されていました。

しかし、ARMアーキテクチャのレジスタセットの特性や、コンパイラ内部でのレジスタの扱い方によっては、このREGALLOC_RMAX+1という計算が、特定の汎用レジスタのインデックスと浮動小数点レジスタのインデックスを重複させてしまう可能性がありました。具体的には、R13レジスタとF3レジスタが同じ内部インデックスを持つことになり、コンパイラがこれらを区別できなくなっていました。

この衝突は、特にcomplex128のような浮動小数点演算を多用するコードで問題を引き起こしました。コンパイラがcomplex128の値を処理する際に、F3レジスタを使用しようとすると、それが誤ってR13レジスタとして扱われ、結果として不正な機械語が生成される可能性がありました。

修正は、REGALLOC_F0の定義をNREGに変更することでした。

#define REGALLOC_F0 NREG // 修正後の箇所

NREGは、Goコンパイラが内部的に認識する「すべての汎用レジスタの総数」または「汎用レジスタの次の利用可能なインデックス」を示す定数です。この変更により、浮動小数点レジスタの開始インデックスが汎用レジスタのインデックス空間と完全に分離され、R13とF3の間の衝突が解消されました。

この修正は、レジスタ割り当ての内部ロジックにおける根本的な問題を解決し、cmd/5gがARMアーキテクチャ上でより正確なコードを生成できるようにしました。

コアとなるコードの変更箇所

src/cmd/5g/gg.hファイルの以下の行が変更されました。

--- a/src/cmd/5g/gg.h
+++ b/src/cmd/5g/gg.h
@@ -51,7 +51,7 @@ struct	Prog
 
 #define REGALLOC_R0 0
 #define REGALLOC_RMAX REGEXT
-#define REGALLOC_F0 (REGALLOC_RMAX+1)
+#define REGALLOC_F0 NREG
 #define REGALLOC_FMAX (REGALLOC_F0 + FREGEXT)
 
 EXTERN	int32	dynloc;

また、この修正を検証するための新しいテストケースが追加されました。

test/fixedbugs/issue6247.go

// compile

// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Issue 6247: 5g used to be confused by the numbering
// of floating-point registers.

package main

var p map[string]interface{}
var v interface{}

func F() {
	p["hello"] = v.(complex128) * v.(complex128)
}

コアとなるコードの解説

変更の中心は、src/cmd/5g/gg.hにおけるREGALLOC_F0マクロの定義です。

  • 変更前: #define REGALLOC_F0 (REGALLOC_RMAX+1)
    • これは、浮動小数点レジスタの内部的な開始インデックスを、汎用レジスタの最大インデックスの直後として定義していました。このアプローチは、汎用レジスタと浮動小数点レジスタの内部的な番号付けが完全に独立していない場合に、レジスタ番号の衝突を引き起こす可能性がありました。特に、ARMアーキテクチャの特定のレジスタ(R13とF3)が、この計算によって同じ内部インデックスにマッピングされてしまう問題がありました。
  • 変更後: #define REGALLOC_F0 NREG
    • NREGは、Goコンパイラの内部で定義されている定数で、通常は汎用レジスタの総数、または汎用レジスタのインデックス空間の終わりを示す値です。この変更により、浮動小数点レジスタの開始インデックスが、汎用レジスタのインデックス空間とは完全に異なる、より安全な値に設定されます。これにより、R13とF3のようなレジスタ間の意図しない衝突が確実に回避され、コンパイラが浮動小数点演算を正しく処理できるようになります。

追加されたテストケースtest/fixedbugs/issue6247.goは、このバグがcomplex128型の演算で顕在化したことを反映しています。このテストは、v.(complex128) * v.(complex128)という複素数乗算を含む関数Fを定義しています。このコードがcmd/5gによって正しくコンパイルされることを確認することで、レジスタ割り当ての修正が期待通りに機能していることを検証します。テストファイルには// compileディレクティブが含まれており、これはGoのテストフレームワークに対して、このファイルをコンパイルするだけでテストが成功することを示すものです(つまり、コンパイルエラーが発生しないことが成功条件)。

関連リンク

参考にした情報源リンク

  • Go Issue 6247のGitHubページ
  • Go CL 13216043のGerritページ
  • Go言語のドキュメント(特にcomplex128型について)
  • ARMアーキテクチャのレジスタセットに関する一般的な情報
  • コンパイラのレジスタ割り当てに関する一般的な情報