[インデックス 18849] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)がPlan 9オペレーティングシステム上で浮動小数点例外(FPU exceptions)を適切に処理するように変更を加えるものです。具体的には、コンパイラが浮動小数点例外によって中断されないように、FPUの例外処理設定を調整しています。
コミット
commit b53d2f5ba7a09b60a3f3dda71a47149f41482290
Author: Anthony Martin <ality@pbrane.org>
Date: Wed Mar 12 19:41:36 2014 -0700
cmd/gc: make the fpu handle all exceptions on Plan 9
The compilers expect to not be interrupted by floating
point exceptions. On Plan 9, every process starts with
interrupts enabled for invalid operation, stack overflow,
and divide by zero exceptions.
LGTM=rsc
R=rsc, 0intro
CC=golang-codereviews
https://golang.org/cl/72750043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b53d2f5ba7a09b60a3f3dda71a47149f41482290
元コミット内容
このコミットの目的は、GoコンパイラがPlan 9環境で浮動小数点例外によって中断されないようにすることです。Plan 9では、デフォルトで「無効な操作 (invalid operation)」、「スタックオーバーフロー (stack overflow)」、「ゼロ除算 (divide by zero)」の浮動小数点例外に対して割り込みが有効になっています。コンパイラはこれらの例外によって処理が中断されることを想定していないため、FPUの例外処理設定を変更して、これらの例外をFPU自身が処理するようにします。
変更点としては、src/cmd/gc/lex.c
ファイルにおいて、catcher
関数内のstrncmp
による浮動小数点例外のチェックを削除し、main
関数内でsetfcr(FPPDBL|FPRNR);
を呼び出すことで、FPUが全ての例外を処理するように設定しています。
変更の背景
Goコンパイラは、その性質上、数値計算を多用する場合がありますが、その過程で発生する可能性のある浮動小数点例外によって、コンパイル処理が予期せず中断されることは望ましくありません。特に、Plan 9オペレーティングシステムでは、プロセスの起動時に特定の浮動小数点例外(無効な操作、スタックオーバーフロー、ゼロ除算)に対する割り込みがデフォルトで有効になっています。これは、これらの例外が発生した際にOSが介入し、プログラムの実行を中断する可能性があることを意味します。
コンパイラは、これらの例外をエラーとして扱うのではなく、FPUが内部的に処理し、計算を継続することを期待しています。例えば、NaN(Not a Number)や無限大といった結果は、浮動小数点演算の仕様上正当な結果であり、それらによってコンパイラが停止することは、コンパイルの安定性を損ないます。
このコミットは、このようなPlan 9特有のFPU例外処理のデフォルト設定が、Goコンパイラの動作に悪影響を与えることを防ぐために導入されました。コンパイラが安定して動作し、浮動小数点演算の結果がIEEE 754標準に則って適切に扱われるようにすることが目的です。
前提知識の解説
浮動小数点数とIEEE 754標準
浮動小数点数は、コンピュータで実数を表現するための形式です。IEEE 754は、浮動小数点数の表現形式と演算方法に関する国際標準であり、ほとんどの現代のコンピュータシステムで採用されています。この標準は、単精度(32ビット)と倍精度(64ビット)の浮動小数点数を定義し、以下の特殊な値も規定しています。
- NaN (Not a Number): 不定形な結果(例: 0/0、sqrt(-1))を表します。
- 無限大 (Infinity): オーバーフローなどによって非常に大きな値や小さな値になった場合を表します。
- 正規化数 (Normalized numbers): 通常の浮動小数点数を表します。
- 非正規化数 (Denormalized numbers): 非常に小さい数を表現するために使われます。
浮動小数点例外 (FPU Exceptions)
IEEE 754標準では、浮動小数点演算中に発生する可能性のある以下の5種類の例外を定義しています。
- Invalid Operation (無効な操作): 結果が数学的に定義できない場合(例: 0/0、sqrt(-1))。
- Division by Zero (ゼロ除算): 非ゼロの数をゼロで割った場合。
- Overflow (オーバーフロー): 結果が表現可能な最大値を超えた場合。
- Underflow (アンダーフロー): 結果が表現可能な最小の非ゼロ値よりも小さく、精度が失われる場合。
- Inexact (不正確): 結果が正確に表現できず、丸めが必要な場合。
これらの例外が発生すると、FPUのステータスワードに「スティッキーフラグ」が設定されます。これは、例外が発生したことを示すビットであり、明示的にクリアされるまで設定されたままになります。また、多くのFPUは、これらの例外に対して「トラップ」をサポートしています。トラップが有効になっている場合、例外が発生すると、OSがシグナル(Unix系システムではSIGFPE
など)を生成し、プログラムがその例外をプログラム的に処理できるようにします。
FPU制御レジスタ (FPU Control Register)
FPU制御レジスタ(または制御ワード)は、FPUの動作を制御するための特別なレジスタです。これには、以下の設定が含まれます。
- 丸めモード (Rounding Mode): 不正確な結果をどのように丸めるかを指定します。IEEE 754では、以下の丸めモードが定義されています。
- Round to Nearest (最近接偶数への丸め): 最も近い表現可能な値に丸めます。ちょうど中間の場合は、偶数に丸めます。これがデフォルトであり、最も推奨される丸めモードです。
- Round toward Zero (ゼロへの丸め): ゼロに近い方向に丸めます(切り捨て)。
- Round toward Positive Infinity (正の無限大への丸め): 正の無限大の方向に丸めます(切り上げ)。
- Round toward Negative Infinity (負の無限大への丸め): 負の無限大の方向に丸めます(切り下げ)。
- 精度 (Precision): 浮動小数点演算の精度を指定します。通常、単精度(32ビット)または倍精度(64ビット)が選択できます。
- 例外マスク (Exception Masks): 各浮動小数点例外に対してトラップを有効にするか無効にするかを設定します。マスクが設定されている場合(トラップが無効)、例外が発生してもシグナルは生成されず、FPUはデフォルトの処理(例: NaNや無限大を生成)を行います。マスクが設定されていない場合(トラップが有効)、例外が発生するとシグナルが生成され、プログラムが中断される可能性があります。
Plan 9オペレーティングシステム
Plan 9は、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、全てのリソースをファイルとして扱うという特徴を持っています。低レベルのシステム制御に優れており、FPUの制御も直接的に行えるようになっています。
技術的詳細
このコミットの核心は、Plan 9環境におけるGoコンパイラのFPU例外処理の挙動を調整することにあります。
catcher
関数の変更
元のコードでは、src/cmd/gc/lex.c
内のcatcher
関数が、notify
システムコールによって登録されたシグナルハンドラとして機能していました。この関数は、特定のシステムメッセージ(特に浮動小数点例外に関連するもの)をインターセプトし、処理していました。
変更前のコードには、以下の行がありました。
if(strncmp(s, "sys: fp: invalid operation", 26) == 0) {
noted(NCONT);
return;
}
このコードは、「sys: fp: invalid operation
」というメッセージが検出された場合、noted(NCONT)
を呼び出してシグナルを継続させ、関数からリターンしていました。これは、invalid operation
例外が発生した場合に、コンパイラが中断されないようにするための試みでした。しかし、コミットメッセージによると、Plan 9では「invalid operation
」、「stack overflow
」、「divide by zero
」の例外に対してデフォルトで割り込みが有効になっているため、この部分的な処理では不十分でした。
このコミットでは、上記のif
文が削除されました。これは、個別の例外メッセージをインターセプトして処理するのではなく、FPU自体に全ての例外を処理させるという、より包括的なアプローチに切り替えたことを意味します。
setfcr(FPPDBL|FPRNR);
の追加
main
関数内の#ifdef PLAN9
ブロックに、以下の行が追加されました。
// Tell the FPU to handle all exceptions.
setfcr(FPPDBL|FPRNR);
この行がこのコミットの主要な変更点です。
setfcr
: これはPlan 9のシステムコールまたはライブラリ関数であり、FPUの制御レジスタを設定するために使用されます。setfcr
は "set FPU control register" の略であると推測されます。FPPDBL
: これは、FPUの精度を倍精度(Double Precision)に設定するためのフラグまたは定数です。IEEE 754標準では、倍精度浮動小数点数は64ビットで表現され、単精度(32ビット)よりも高い精度を提供します。コンパイラのような数値計算を多用するアプリケーションでは、精度の高い計算が求められることが多いため、倍精度に設定することは理にかなっています。FPRNR
: これは、FPUの丸めモードを「Round to Nearest (最近接偶数への丸め)」に設定するためのフラグまたは定数です。これはIEEE 754標準で推奨されるデフォルトの丸めモードであり、数値の丸め誤差を最小限に抑えるのに役立ちます。
これらのフラグをビットOR演算子(|
)で結合してsetfcr
に渡すことで、FPUは以下の設定で動作するようになります。
- 倍精度演算: 全ての浮動小数点演算が倍精度で行われます。
- 最近接偶数への丸め: 不正確な結果は、最も近い表現可能な値に丸められます。
- 全ての例外の内部処理: 最も重要な点として、この設定はFPUの例外マスクを調整し、全ての浮動小数点例外(Invalid Operation, Division by Zero, Overflow, Underflow, Inexact)に対するトラップを無効にします。これにより、例外が発生してもOSがシグナルを生成してコンパイラの実行を中断することはなく、FPUが内部的にNaNや無限大といった結果を生成して計算を継続します。
この変更により、GoコンパイラはPlan 9環境においても、浮動小数点例外によって予期せず中断されることなく、安定して動作することが保証されます。
コアとなるコードの変更箇所
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -172,10 +172,6 @@ catcher(void *v, char *s)
{
USED(v);
- if(strncmp(s, "sys: fp: invalid operation", 26) == 0) {
- noted(NCONT);
- return;
- }
if(strncmp(s, "sys: trap: fault read", 21) == 0) {
if(nsavederrors + nerrors > 0)
errorexit();
@@ -211,6 +207,8 @@ main(int argc, char *argv[])
#ifdef PLAN9
notify(catcher);
+ // Tell the FPU to handle all exceptions.
+ setfcr(FPPDBL|FPRNR);
#endif
// Allow GOARCH=thestring or GOARCH=thestringsuffix,
// but not other values.
コアとなるコードの解説
catcher
関数の変更
- 削除された行:
このコードは、if(strncmp(s, "sys: fp: invalid operation", 26) == 0) { noted(NCONT); return; }
"sys: fp: invalid operation"
という文字列がシグナルメッセージに含まれている場合に、そのシグナルを継続(noted(NCONT)
)して処理を終了していました。これは、invalid operation
例外に対する部分的なハンドリングでした。この行が削除されたのは、FPU自体に全ての例外を処理させるという、より一般的なアプローチに移行したためです。これにより、catcher
関数は、浮動小数点例外ではなく、"sys: trap: fault read"
のような他の重要なシステムトラップに焦点を当てることになります。
main
関数の変更
- 追加された行:
このコードブロックは、コンパイラがPlan 9上でビルドされる場合にのみ有効になります(#ifdef PLAN9 notify(catcher); // Tell the FPU to handle all exceptions. setfcr(FPPDBL|FPRNR); #endif
#ifdef PLAN9
プリプロセッサディレクティブによって制御されます)。notify(catcher);
: これは、catcher
関数をシステムシグナルハンドラとして登録するPlan 9のシステムコールです。これにより、特定のシステムイベント(トラップや例外など)が発生した際にcatcher
関数が呼び出されます。setfcr(FPPDBL|FPRNR);
: この行がこのコミットの主要な機能を提供します。setfcr
: FPUの制御レジスタを設定する関数です。FPPDBL
: "Floating Point Precision Double" を意味し、FPUの演算精度を倍精度(64ビット)に設定します。これにより、浮動小数点計算の精度が向上します。FPRNR
: "Floating Point Round Nearest" を意味し、FPUの丸めモードを「最近接偶数への丸め」に設定します。これはIEEE 754標準で推奨されるデフォルトの丸めモードであり、丸め誤差を最小限に抑えるのに役立ちます。- これらのフラグを
|
(ビットOR)で結合してsetfcr
に渡すことで、FPUは倍精度で、最近接偶数への丸めモードで動作し、同時に全ての浮動小数点例外に対するトラップを無効にします。トラップが無効になることで、例外が発生してもOSがシグナルを生成してプログラムを中断することはなく、FPUが内部的にNaNや無限大といった結果を生成して計算を継続します。これにより、コンパイラは浮動小数点例外によって中断されることなく、安定して動作するようになります。
関連リンク
- Go言語の公式ウェブサイト: https://golang.org/
- Plan 9 from Bell Labs: https://9p.io/plan9/
- IEEE 754 - Wikipedia: https://ja.wikipedia.org/wiki/IEEE_754
参考にした情報源リンク
- Web検索結果: "Plan 9 FPU exceptions handling setfcr FPPDBL FPRNR"
- IEEE 754標準に関する一般的な情報源(例: Wikipedia、技術文書)
- Plan 9のドキュメント(
setfcr
、notify
などのシステムコールに関する情報)