[インデックス 13675] ファイルの概要
このコミットは、Goコンパイラ(cmd/6g
、x86-64アーキテクチャ向け)におけるfloat32
およびfloat64
からuint64
への変換処理に関するバグ修正を扱っています。具体的には、浮動小数点数から符号なし64ビット整数への変換時に、誤った丸めモードが使用されていた問題を修正し、正しい切り捨て(truncate)セマンティクスを保証するように変更されました。
コミット
commit e80f6a4de1a35cab03e7e4d29e26015895ffe04f
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Thu Aug 23 14:35:26 2012 +0800
cmd/6g: fix float32/64->uint64 conversion
CVTSS2SQ's rounding mode is controlled by the RC field of MXCSR;
as we specifically need truncate semantic, we should use CVTTSS2SQ.
Fixes #3804.
R=rsc, r
CC=golang-dev
https://golang.org/cl/6352079
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e80f6a4de1a35cab03e7e4d29e26015895ffe04f
元コミット内容
Goコンパイラ(cmd/6g
)において、float32
およびfloat64
からuint64
への変換が正しく行われていなかったバグを修正します。この問題は、x86命令セットのCVTSS2SQ
命令がMXCSRレジスタのRC(Rounding Control)フィールドによって丸めモードが制御されるのに対し、Goの変換では切り捨て(truncate)セマンティクスが必要であるにもかかわらず、それが保証されていなかったことに起因します。修正として、切り捨てセマンティクスを明示的に提供するCVTTSS2SQ
命令を使用するように変更されました。
この修正は、GoのIssue #3804を解決します。
変更の背景
Go言語では、浮動小数点数型(float32
, float64
)から整数型(int
, uint
など)への変換は、一般的にゼロ方向への切り捨て(truncate towards zero)として定義されています。しかし、x86-64アーキテクチャの特定の命令(CVTSS2SQ
やCVTSD2SQ
)は、デフォルトの丸めモードが「最も近い偶数への丸め」(round to nearest, ties to even)であるか、またはMXCSRレジスタの設定に依存します。
Goコンパイラがこれらの命令を使用して浮動小数点数をuint64
に変換する際、MXCSRレジスタのRCフィールドが適切に設定されていない場合、Go言語の仕様で求められる「ゼロ方向への切り捨て」ではなく、異なる丸めが行われる可能性がありました。これにより、例えば39.7
をuint64
に変換すると39
になるべきところが、異なる値になるなどのバグが発生していました。
このコミットは、この不整合を解消し、Go言語の浮動小数点数から整数への変換セマンティクスをx86-64アーキテクチャ上で正確に実装することを目的としています。
前提知識の解説
浮動小数点数と整数変換の丸めモード
浮動小数点数を整数に変換する際には、小数点以下の部分をどのように処理するかという「丸めモード」が重要になります。一般的な丸めモードには以下のようなものがあります。
- 最も近い偶数への丸め (Round to Nearest, ties to even): 最も一般的な丸めモードで、IEEE 754標準のデフォルトです。値が二つの整数のちょうど中間にある場合、偶数の方に丸めます(例: 2.5 -> 2, 3.5 -> 4)。
- ゼロ方向への切り捨て (Truncate towards Zero): 小数点以下を単純に切り捨てて整数にします。正の数の場合は床関数(floor)、負の数の場合は天井関数(ceil)に相当します(例: 2.7 -> 2, -2.7 -> -2)。Go言語の浮動小数点数から整数への変換はこのセマンティクスに従います。
- 正の無限大への丸め (Round towards Positive Infinity): 値を常に大きい方の整数に丸めます(天井関数)。
- 負の無限大への丸め (Round towards Negative Infinity): 値を常に小さい方の整数に丸めます(床関数)。
x86-64 SSE/AVX命令セットとMXCSRレジスタ
x86-64アーキテクチャでは、浮動小数点演算は主にSSE(Streaming SIMD Extensions)やAVX(Advanced Vector Extensions)命令セットによって行われます。これらの命令は、MXCSR(Media eXtension Control and Status Register)と呼ばれる32ビットの制御/ステータスレジスタによって動作が制御されます。
MXCSRレジスタには、浮動小数点例外マスク、フラグ、そして丸め制御 (RC: Rounding Control) フィールドが含まれています。RCフィールドは、浮動小数点演算の丸めモードを設定するために使用されます。
CVTSS2SQ
, CVTSD2SQ
, CVTTSS2SQ
, CVTTSD2SQ
命令
これらの命令は、x86-64アーキテクチャにおける浮動小数点数から整数への変換命令です。
CVTSS2SQ
(Convert Single-precision Floating-Point to Signed Quadword Integer): 32ビット単精度浮動小数点数(float32
)を64ビット符号付き整数(int64
)に変換します。この命令の丸めモードは、MXCSRレジスタのRCフィールドによって制御されます。CVTSD2SQ
(Convert Scalar Double-precision Floating-Point to Signed Quadword Integer): 64ビット倍精度浮動小数点数(float64
)を64ビット符号付き整数(int64
)に変換します。この命令の丸めモードも、MXCSRレジスタのRCフィールドによって制御されます。CVTTSS2SQ
(Convert Single-precision Floating-Point to Signed Quadword Integer, Truncate): 32ビット単精度浮動小数点数(float32
)を64ビット符号付き整数(int64
)に変換します。この命令は、常にゼロ方向への切り捨てを行います。MXCSRレジスタのRCフィールドの設定に関わらず、切り捨てセマンティクスを強制します。CVTTSD2SQ
(Convert Scalar Double-precision Floating-Point to Signed Quadword Integer, Truncate): 64ビット倍精度浮動小数点数(float64
)を64ビット符号付き整数(int64
)に変換します。この命令も、常にゼロ方向への切り捨てを行います。
Go言語の浮動小数点数から整数への変換は「ゼロ方向への切り捨て」であるため、CVTTSS2SQ
やCVTTSD2SQ
のような明示的に切り捨てを行う命令を使用することが正しい実装となります。
技術的詳細
このコミットの核心は、Goコンパイラが浮動小数点数から整数への変換を行う際に生成するx86アセンブリ命令の変更です。
Goコンパイラのバックエンド(cmd/6g
)は、Goのソースコードを機械語に変換する過程で、浮動小数点数から整数への型変換(キャスト)を検出すると、対応するx86命令を生成します。
修正前は、float32
からuint64
への変換にはACVTSS2SQ
(Goコンパイラ内部のオペコードで、最終的にCVTSS2SQ
にマップされる)が、float64
からuint64
への変換にはACVTSD2SQ
(最終的にCVTSD2SQ
にマップされる)が使用されていました。これらの命令はMXCSRレジスタのRCフィールドに依存するため、Goの期待する「ゼロ方向への切り捨て」が保証されませんでした。特に、GoのランタイムがMXCSRレジスタのRCフィールドをデフォルトの「最も近い偶数への丸め」に設定している場合、変換結果がGoの仕様と異なる可能性がありました。
修正では、これらの命令をそれぞれACVTTSS2SQ
とACVTTSD2SQ
に変更しました。これらの命令は、命令自体が「Truncate」(切り捨て)のセマンティクスを持つため、MXCSRレジスタのRCフィールドの設定に関わらず、常にゼロ方向への切り捨てを行います。これにより、Go言語の浮動小数点数から整数への変換の動作が、x86-64アーキテクチャ上で正確に保証されるようになりました。
また、この修正にはtest/fixedbugs/bug447.go
という新しいテストファイルが追加されています。このテストは、様々な浮動小数点数と整数型の組み合わせに対して、浮動小数点数から整数への変換が正しく行われることを検証するためのものです。特に、境界値や負の値、大きな値など、丸めモードの影響を受けやすいケースを網羅的にテストすることで、このバグが再発しないことを保証しています。
コアとなるコードの変更箇所
変更はsrc/cmd/6g/gsubr.c
ファイル内のgmove
関数に集中しています。この関数は、Goコンパイラが型変換(ムーブ)を処理する際に呼び出される部分です。
--- a/src/cmd/6g/gsubr.c
+++ b/src/cmd/6g/gsubr.c
@@ -796,9 +796,9 @@ gmove(Node *f, Node *t)
// algorithm is:
// if small enough, use native float64 -> int64 conversion.
// otherwise, subtract 2^63, convert, and add it back.
- a = ACVTSS2SQ;
+ a = ACVTTSS2SQ;
if(ft == TFLOAT64)
- a = ACVTSD2SQ;
+ a = ACVTTSD2SQ;
bignodes();
regalloc(&r1, types[ft], N);
regalloc(&r2, types[tt], t);
また、バグの修正を検証するために、test/fixedbugs/bug447.go
という新しいテストファイルが追加されました。
コアとなるコードの解説
src/cmd/6g/gsubr.c
のgmove
関数は、Goコンパイラが異なる型の間で値を移動(変換)する際のコード生成ロジックを含んでいます。
変更された部分のコンテキストは、浮動小数点数型(ft
、TFLOAT32
またはTFLOAT64
)から符号なし64ビット整数型(tt
、TUINT64
)への変換を処理するブロックです。
a = ACVTSS2SQ;
がa = ACVTTSS2SQ;
に変更されました。- これは、
float32
からuint64
への変換に使用される内部オペコードを、MXCSRレジスタの丸めモードに依存するCVTSS2SQ
から、常に切り捨てを行うCVTTSS2SQ
に変更したことを意味します。
- これは、
if(ft == TFLOAT64)
ブロック内のa = ACVTSD2SQ;
がa = ACVTTSD2SQ;
に変更されました。- これは、
float64
からuint64
への変換に使用される内部オペコードを、MXCSRレジスタの丸めモードに依存するCVTSD2SQ
から、常に切り捨てを行うCVTTSD2SQ
に変更したことを意味します。
- これは、
これらの変更により、Go言語の浮動小数点数から整数への変換セマンティクス(ゼロ方向への切り捨て)が、x86-64アーキテクチャ上で正確に反映されるようになりました。
test/fixedbugs/bug447.go
は、この修正が正しく機能することを検証するための包括的なテストケースです。このテストは、Goのfmt
パッケージとbytes
パッケージを使用して、様々な浮動小数点数と整数型の組み合わせに対する変換結果を動的に生成し、期待される値と比較します。これにより、float32
およびfloat64
からint8
, int16
, int32
, int64
, uint8
, uint16
, uint32
, uint64
への変換がすべてGoの仕様通りに切り捨てられることを確認します。特に、負の数や大きな数、小数点以下が.5
であるような境界ケースも含まれており、丸めモードの正確性を厳密に検証しています。
関連リンク
- Go Issue #3804: https://github.com/golang/go/issues/3804
- Go CL 6352079: https://golang.org/cl/6352079 (このコミットに対応するGoの変更リスト)
参考にした情報源リンク
- Intel® 64 and IA-32 Architectures Software Developer’s Manuals (特にVolume 2A: Instruction Set Reference, A-M):
CVTSS2SQ
,CVTSD2SQ
,CVTTSS2SQ
,CVTTSD2SQ
命令の詳細な説明。- MXCSRレジスタのRCフィールドに関する情報。
- https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html
- IEEE 754 浮動小数点標準: 浮動小数点演算と丸めモードに関する基本的な情報。
- Go言語の仕様 (The Go Programming Language Specification): 型変換のセマンティクスに関する情報。
- Goのソースコード(
src/cmd/6g/gsubr.c
およびtest/fixedbugs/bug447.go
) - GoのIssueトラッカー: 関連するバグ報告や議論。
- Goの変更リスト (Gerrit): 過去の変更履歴とレビュー。