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

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

このコミットは、Goコンパイラ(cmd/gc)における多倍長整数演算のオーバーフロー検出に関する問題を修正し、誤解を招くエラーメッセージ「ovf in mpaddxx」の発生を回避することを目的としています。具体的には、mparith2.c内のmpmulfixfix関数において、中間演算でのオーバーフローをより適切に検出し、早期に処理を終了させることで、より分かりやすいエラー報告を可能にしています。また、この修正に関連する新しいテストケースが追加されています。

コミット

commit 397f129daf81a74997397aa7d097d1c3039a5333
Author: Jan Ziak <0xe2.0x9a.0x9b@gmail.com>
Date:   Wed Apr 9 08:36:27 2014 +0200

    cmd/gc: avoid confusing error message "ovf in mpaddxx"
    
    Fixes #6889
    
    LGTM=rsc
    R=gri, rsc
    CC=golang-codereviews
    https://golang.org/cl/85080044

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

https://github.com/golang/go/commit/397f129daf81a74997397aa7d097d1c3039a5333

元コミット内容

cmd/gc: 誤解を招くエラーメッセージ「ovf in mpaddxx」を回避する。

Issue 6889を修正。

変更の背景

このコミットは、Goコンパイラ(cmd/gc)が特定の状況下で多倍長整数(Mpint)の演算中にオーバーフローが発生した際に、「ovf in mpaddxx」という誤解を招くエラーメッセージを出力する問題を解決するために行われました。

Go言語では、コンパイル時に定数式を評価する際、通常のCPUのレジスタサイズを超える大きな数値(多倍長整数)を扱うことがあります。これらの演算は、cmd/gc内部のMpint型とそれに関連する関数群によって処理されます。

問題の根本は、mpmulfixfix関数(多倍長整数の乗算を行う関数)が、中間計算でオーバーフローが発生した際に、そのオーバーフロー状態を適切に伝播させず、結果として後続のmpaddfixfix関数で「ovf in mpaddxx」という、ユーザーにとっては意味不明なエラーメッセージが発生していたことにあります。このエラーは、本来のオーバーフローの原因が乗算にあるにもかかわらず、加算関数で報告されるため、デバッグを困難にしていました。

Go CL 85080044の議論によると、この問題は、非常に大きな定数式を扱う際に顕著になり、コンパイラが無限ループに陥る可能性も示唆されていました。この修正は、オーバーフローの検出と伝播を改善することで、より正確で分かりやすいエラーメッセージを提供し、コンパイラの堅牢性を向上させることを目的としています。

前提知識の解説

cmd/gc

cmd/gcは、Go言語の公式コンパイラツールチェーンの一部であり、Goソースコードを機械語に変換する主要なコンポーネントです。Go言語のコンパイラは、フロントエンド、ミドルエンド、バックエンドといった複数のステージで構成されており、cmd/gcはその中核を担います。定数評価、型チェック、最適化、コード生成など、Goプログラムのコンパイルプロセスにおける多くの重要なタスクを実行します。

Mpint (Multi-precision Integer)

Mpintは、Goコンパイラ内部で使用される多倍長整数型です。Go言語では、intint64のような組み込みの整数型にはサイズ制限がありますが、コンパイル時の定数式では、これらの制限を超える非常に大きな整数値を扱う必要があります。例えば、1 << 100のような式は、通常の64ビット整数では表現できません。Mpintは、このような任意の精度の整数演算を可能にするために設計されており、コンパイラが定数式の正確な評価を行う上で不可欠な要素です。

mparith2.c

mparith2.cは、cmd/gcのソースコードの一部であり、Mpint型に対する様々な算術演算(加算、減算、乗算、除算など)の実装を含んでいます。これらの関数は、Goプログラム内の定数式を評価する際に、多倍長整数の計算を正確に行うために利用されます。このファイルはC言語で書かれており、Goコンパイラの低レベルな部分を構成しています。

固定小数点演算 (Fixed-point arithmetic)

固定小数点演算は、浮動小数点演算とは異なり、小数点以下の桁数が固定されている数値表現と演算方法です。このコミットで修正されているmpmulfixfix関数は、fixという接尾辞から、固定小数点数のような内部表現を扱っている可能性を示唆しています。コンパイラが定数評価を行う際に、精度を保ちつつ効率的に計算するために、このような内部表現が用いられることがあります。

オーバーフロー (Overflow)

オーバーフローとは、数値演算の結果が、その数値を格納するために割り当てられたデータ型の最大値を超えてしまう現象です。例えば、8ビット符号なし整数で255に1を加えると、結果は0になる(ラップアラウンドする)か、オーバーフローエラーが発生します。コンパイラにおける定数評価では、オーバーフローはプログラムの意図しない動作や、コンパイルエラーの原因となるため、正確に検出して報告する必要があります。

技術的詳細

このコミットの技術的詳細は、src/cmd/gc/mparith2.c内のmpmulfixfix関数の変更に集約されます。mpmulfixfixは、2つの多倍長整数abの乗算を行い、結果をaに格納する関数です。

元のコードでは、乗算の途中でmpaddfixfix(多倍長整数の加算)が呼び出されていました。このmpaddfixfix関数自体はオーバーフローを検出する機能を持っていましたが、mpmulfixfixがそのオーバーフロー状態を適切にチェックし、早期に処理を中断するメカニズムが不足していました。

具体的には、mpmulfixfixの内部ループでmpaddfixfix(&q, &s, 1)が呼び出された後、q(結果を保持するMpint構造体)のovf(オーバーフローフラグ)がセットされても、mpmulfixfixはループを継続していました。これにより、最終的にmpmulfixfixが終了する際に、q.ovfがセットされているにもかかわらず、そのオーバーフロー状態が適切に伝播されず、呼び出し元に誤った結果や、後続の処理で「ovf in mpaddxx」という誤解を招くエラーが発生していました。

修正は、この問題を解決するために、mpaddfixfixの呼び出し後にq.ovfを即座にチェックし、オーバーフローが発生していればgoto out;を使って関数の終端にジャンプするように変更されました。これにより、乗算の途中でオーバーフローが検出された場合、それ以上無駄な計算を続けることなく、関数の終了処理(q.negの設定とmpmovefixfixによる結果のコピー)に進むことができます。

また、if(x & 1)ブロックの内部で、mpaddfixfixを呼び出す前にs.ovf(加算される側のMpintのオーバーフローフラグ)もチェックするようになりました。これは、加算される値自体が既にオーバーフロー状態にある場合、その後の加算処理を行う必要がないため、早期にオーバーフローを伝播させるための最適化です。

この変更により、コンパイラは多倍長整数の乗算におけるオーバーフローをより正確かつ早期に検出し、ユーザーに対してより適切なエラーメッセージ(例えば、「定数オーバーフロー」など)を報告できるようになります。

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

src/cmd/gc/mparith2.cmpmulfixfix関数における変更点です。

--- a/src/cmd/gc/mparith2.c
+++ b/src/cmd/gc/mparith2.c
@@ -300,13 +300,21 @@ mpmulfixfix(Mpint *a, Mpint *b)\n \tfor(i=0; i<na; i++) {\n \t\tx = *a1++;\n \t\tfor(j=0; j<Mpscale; j++) {\n-\t\t\tif(x & 1)\n+\t\t\t\tif(x & 1) {\n+\t\t\t\t\tif(s.ovf) {\n+\t\t\t\t\t\tq.ovf = 1;\n+\t\t\t\t\t\tgoto out;\n+\t\t\t\t\t}\n \t\t\t\t\tmpaddfixfix(&q, &s, 1);\n+\t\t\t\t\tif(q.ovf)\n+\t\t\t\t\t\tgoto out;\n+\t\t\t}\n \t\t\tmplsh(&s, 1);\n \t\t\tx >>= 1;\n \t\t}\n \t}\n \n+out:\n \tq.neg = a->neg ^ b->neg;\n \tmpmovefixfix(a, &q);\
 \tif(a->ovf)\

また、この修正の検証のために、test/fixedbugs/issue6889.goという新しいテストファイルが追加されました。このテストファイルは、非常に大きな定数式の乗算を意図的に行い、コンパイラが正しくオーバーフローを検出してエラーを報告することを確認します。

// errorcheck

// Copyright 2014 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 6889: confusing error message: ovf in mpaddxx

package main

const (
	f1  = 1
	f2  = f1 * 2
	// ... (中略) ...
	f90 = f89 * 90
	f91 = f90 * 91 // ERROR "overflow"
)

このテストは、f91の計算がオーバーフローを引き起こし、コンパイラが「overflow」というエラーメッセージを正しく出力することを期待しています。

コアとなるコードの解説

変更されたmpmulfixfix関数内の主要なロジックは以下の通りです。

  1. if(x & 1)ブロック内の変更:

    • xの最下位ビットが1の場合(つまり、現在のビットが乗算に寄与する場合)に実行されるブロックです。
    • if(s.ovf)の追加: mpaddfixfixを呼び出す前に、加算される側のMpintであるsが既にオーバーフロー状態であるかをチェックします。もしs.ovfが真であれば、それ以上計算を続ける意味がないため、q.ovf = 1;で結果のオーバーフローフラグをセットし、goto out;で関数を早期に終了させます。これは、冗長な計算を避け、オーバーフロー状態を迅速に伝播させるための最適化です。
    • mpaddfixfix(&q, &s, 1);: 実際の多倍長整数の加算が行われます。qsを加算します。
    • if(q.ovf)の追加: mpaddfixfixの呼び出し後、直ちに結果のqがオーバーフロー状態になったかをチェックします。もしq.ovfが真であれば、乗算のこのステップでオーバーフローが発生したことを意味するため、goto out;で関数を早期に終了させます。これにより、オーバーフローが検出された時点で、それ以上の計算を停止し、誤解を招くエラーメッセージの発生を防ぎます。
  2. out:ラベルの追加:

    • goto out;によってジャンプされる新しいラベルです。
    • このラベルに到達すると、q.neg = a->neg ^ b->neg;(符号の設定)とmpmovefixfix(a, &q);(結果のコピー)という、関数の最終的なクリーンアップ処理が実行されます。これにより、オーバーフローが発生した場合でも、適切なオーバーフローフラグがセットされた状態で関数が終了し、呼び出し元に正確な情報が伝わります。

これらの変更により、mpmulfixfix関数は、中間計算でオーバーフローが発生した際に、その状態を即座に検出し、後続の処理に誤った情報が伝播するのを防ぎます。結果として、コンパイラはより正確なエラーメッセージを出力できるようになり、デバッグが容易になります。

関連リンク

参考にした情報源リンク

  • Go CL 85080044のレビューコメントと議論
  • Go言語のコンパイラに関する一般的な知識
  • 多倍長整数演算とオーバーフロー検出に関する一般的なプログラミング知識
  • C言語のgoto文の使用例と目的I have generated the detailed explanation in Markdown format, covering all the requested sections. I have also incorporated the information from the web search and the CL summary. The output is now ready to be printed to standard output.
# [インデックス 19078] ファイルの概要

このコミットは、Goコンパイラ(`cmd/gc`)における多倍長整数演算のオーバーフロー検出に関する問題を修正し、誤解を招くエラーメッセージ「ovf in mpaddxx」の発生を回避することを目的としています。具体的には、`mparith2.c`内の`mpmulfixfix`関数において、中間演算でのオーバーフローをより適切に検出し、早期に処理を終了させることで、より分かりやすいエラー報告を可能にしています。また、この修正に関連する新しいテストケースが追加されています。

## コミット

commit 397f129daf81a74997397aa7d097d1c3039a5333 Author: Jan Ziak 0xe2.0x9a.0x9b@gmail.com Date: Wed Apr 9 08:36:27 2014 +0200

cmd/gc: avoid confusing error message "ovf in mpaddxx"

Fixes #6889

LGTM=rsc
R=gri, rsc
CC=golang-codereviews
https://golang.org/cl/85080044

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

https://github.com/golang/go/commit/397f129daf81a74997397aa7d097d1c3039a5333

## 元コミット内容

`cmd/gc`: 誤解を招くエラーメッセージ「ovf in mpaddxx」を回避する。

Issue 6889を修正。

## 変更の背景

このコミットは、Goコンパイラ(`cmd/gc`)が特定の状況下で多倍長整数(`Mpint`)の演算中にオーバーフローが発生した際に、「ovf in mpaddxx」という誤解を招くエラーメッセージを出力する問題を解決するために行われました。

Go言語では、コンパイル時に定数式を評価する際、通常のCPUのレジスタサイズを超える大きな数値(多倍長整数)を扱うことがあります。これらの演算は、`cmd/gc`内部の`Mpint`型とそれに関連する関数群によって処理されます。

問題の根本は、`mpmulfixfix`関数(多倍長整数の乗算を行う関数)が、中間計算でオーバーフローが発生した際に、そのオーバーフロー状態を適切に伝播させず、結果として後続の`mpaddfixfix`関数で「ovf in mpaddxx」という、ユーザーにとっては意味不明なエラーメッセージが発生していたことにあります。このエラーは、本来のオーバーフローの原因が乗算にあるにもかかわらず、加算関数で報告されるため、デバッグを困難にしていました。

Go CL 85080044の議論によると、この問題は、非常に大きな定数式を扱う際に顕著になり、コンパイラが無限ループに陥る可能性も示唆されていました。この修正は、オーバーフローの検出と伝播を改善することで、より正確で分かりやすいエラーメッセージを提供し、コンパイラの堅牢性を向上させることを目的としています。

## 前提知識の解説

### `cmd/gc`

`cmd/gc`は、Go言語の公式コンパイラツールチェーンの一部であり、Goソースコードを機械語に変換する主要なコンポーネントです。Go言語のコンパイラは、フロントエンド、ミドルエンド、バックエンドといった複数のステージで構成されており、`cmd/gc`はその中核を担います。定数評価、型チェック、最適化、コード生成など、Goプログラムのコンパイルプロセスにおける多くの重要なタスクを実行します。

### `Mpint` (Multi-precision Integer)

`Mpint`は、Goコンパイラ内部で使用される多倍長整数型です。Go言語では、`int`や`int64`のような組み込みの整数型にはサイズ制限がありますが、コンパイル時の定数式では、これらの制限を超える非常に大きな整数値を扱う必要があります。例えば、`1 << 100`のような式は、通常の64ビット整数では表現できません。`Mpint`は、このような任意の精度の整数演算を可能にするために設計されており、コンパイラが定数式の正確な評価を行う上で不可欠な要素です。

### `mparith2.c`

`mparith2.c`は、`cmd/gc`のソースコードの一部であり、`Mpint`型に対する様々な算術演算(加算、減算、乗算、除算など)の実装を含んでいます。これらの関数は、Goプログラム内の定数式を評価する際に、多倍長整数の計算を正確に行うために利用されます。このファイルはC言語で書かれており、Goコンパイラの低レベルな部分を構成しています。

### 固定小数点演算 (Fixed-point arithmetic)

固定小数点演算は、浮動小数点演算とは異なり、小数点以下の桁数が固定されている数値表現と演算方法です。このコミットで修正されている`mpmulfixfix`関数は、`fix`という接尾辞から、固定小数点数のような内部表現を扱っている可能性を示唆しています。コンパイラが定数評価を行う際に、精度を保ちつつ効率的に計算するために、このような内部表現が用いられることがあります。

### オーバーフロー (Overflow)

オーバーフローとは、数値演算の結果が、その数値を格納するために割り当てられたデータ型の最大値を超えてしまう現象です。例えば、8ビット符号なし整数で255に1を加えると、結果は0になる(ラップアラウンドする)か、オーバーフローエラーが発生します。コンパイラにおける定数評価では、オーバーフローはプログラムの意図しない動作や、コンパイルエラーの原因となるため、正確に検出して報告する必要があります。

## 技術的詳細

このコミットの技術的詳細は、`src/cmd/gc/mparith2.c`内の`mpmulfixfix`関数の変更に集約されます。`mpmulfixfix`は、2つの多倍長整数`a`と`b`の乗算を行い、結果を`a`に格納する関数です。

元のコードでは、乗算の途中で`mpaddfixfix`(多倍長整数の加算)が呼び出されていました。この`mpaddfixfix`関数自体はオーバーフローを検出する機能を持っていましたが、`mpmulfixfix`がそのオーバーフロー状態を適切にチェックし、早期に処理を中断するメカニズムが不足していました。

具体的には、`mpmulfixfix`の内部ループで`mpaddfixfix(&q, &s, 1)`が呼び出された後、`q`(結果を保持する`Mpint`構造体)の`ovf`(オーバーフローフラグ)がセットされても、`mpmulfixfix`はループを継続していました。これにより、最終的に`mpmulfixfix`が終了する際に、`q.ovf`がセットされているにもかかわらず、そのオーバーフロー状態が適切に伝播されず、呼び出し元に誤った結果や、後続の処理で「ovf in mpaddxx」という誤解を招くエラーが発生していました。

修正は、この問題を解決するために、`mpaddfixfix`の呼び出し後に`q.ovf`を即座にチェックし、オーバーフローが発生していれば`goto out;`を使って関数の終端にジャンプするように変更されました。これにより、乗算の途中でオーバーフローが検出された場合、それ以上無駄な計算を続けることなく、関数の終了処理(`q.neg`の設定と`mpmovefixfix`による結果のコピー)に進むことができます。

また、`if(x & 1)`ブロックの内部で、`s.ovf`(加算される側の`Mpint`のオーバーフローフラグ)もチェックするようになりました。これは、加算される値自体が既にオーバーフロー状態にある場合、その後の加算処理を行う必要がないため、早期にオーバーフローを伝播させるための最適化です。

この変更により、コンパイラは多倍長整数の乗算におけるオーバーフローをより正確かつ早期に検出し、ユーザーに対してより適切なエラーメッセージ(例えば、「定数オーバーフロー」など)を報告できるようになります。

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

`src/cmd/gc/mparith2.c`の`mpmulfixfix`関数における変更点です。

```diff
--- a/src/cmd/gc/mparith2.c
+++ b/src/cmd/gc/mparith2.c
@@ -300,13 +300,21 @@ mpmulfixfix(Mpint *a, Mpint *b)\n \tfor(i=0; i<na; i++) {\n \t\tx = *a1++;\n \t\tfor(j=0; j<Mpscale; j++) {\n-\t\t\tif(x & 1)\n+\t\t\t\tif(x & 1) {\n+\t\t\t\t\tif(s.ovf) {\n+\t\t\t\t\t\tq.ovf = 1;\n+\t\t\t\t\t\tgoto out;\n+\t\t\t\t\t}\n \t\t\t\t\tmpaddfixfix(&q, &s, 1);\n+\t\t\t\t\tif(q.ovf)\n+\t\t\t\t\t\tgoto out;\n+\t\t\t}\n \t\t\tmplsh(&s, 1);\n \t\t\tx >>= 1;\n \t\t}\n \t}\n \n+out:\n \tq.neg = a->neg ^ b->neg;\n \tmpmovefixfix(a, &q);\
 \tif(a->ovf)\

また、この修正の検証のために、test/fixedbugs/issue6889.goという新しいテストファイルが追加されました。このテストファイルは、非常に大きな定数式の乗算を意図的に行い、コンパイラが正しくオーバーフローを検出してエラーを報告することを確認します。

// errorcheck

// Copyright 2014 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 6889: confusing error message: ovf in mpaddxx

package main

const (
	f1  = 1
	f2  = f1 * 2
	// ... (中略) ...
	f90 = f89 * 90
	f91 = f90 * 91 // ERROR "overflow"
)

このテストは、f91の計算がオーバーフローを引き起こし、コンパイラが「overflow」というエラーメッセージを正しく出力することを期待しています。

コアとなるコードの解説

変更されたmpmulfixfix関数内の主要なロジックは以下の通りです。

  1. if(x & 1)ブロック内の変更:

    • xの最下位ビットが1の場合(つまり、現在のビットが乗算に寄与する場合)に実行されるブロックです。
    • if(s.ovf)の追加: mpaddfixfixを呼び出す前に、加算される側のMpintであるsが既にオーバーフロー状態であるかをチェックします。もしs.ovfが真であれば、それ以上計算を続ける意味がないため、q.ovf = 1;で結果のオーバーフローフラグをセットし、goto out;で関数を早期に終了させます。これは、冗長な計算を避け、オーバーフロー状態を迅速に伝播させるための最適化です。
    • mpaddfixfix(&q, &s, 1);: 実際の多倍長整数の加算が行われます。qsを加算します。
    • if(q.ovf)の追加: mpaddfixfixの呼び出し後、直ちに結果のqがオーバーフロー状態になったかをチェックします。もしq.ovfが真であれば、乗算のこのステップでオーバーフローが発生したことを意味するため、goto out;で関数を早期に終了させます。これにより、オーバーフローが検出された時点で、それ以上の計算を停止し、誤解を招くエラーメッセージの発生を防ぎます。
  2. out:ラベルの追加:

    • goto out;によってジャンプされる新しいラベルです。
    • このラベルに到達すると、q.neg = a->neg ^ b->neg;(符号の設定)とmpmovefixfix(a, &q);(結果のコピー)という、関数の最終的なクリーンアップ処理が実行されます。これにより、オーバーフローが発生した場合でも、適切なオーバーフローフラグがセットされた状態で関数が終了し、呼び出し元に正確な情報が伝わります。

これらの変更により、mpmulfixfix関数は、中間計算でオーバーフローが発生した際に、その状態を即座に検出し、後続の処理に誤った情報が伝播するのを防ぎます。結果として、コンパイラはより正確なエラーメッセージを出力できるようになり、デバッグが容易になります。

関連リンク

参考にした情報源リンク

  • Go CL 85080044のレビューコメントと議論
  • Go言語のコンパイラに関する一般的な知識
  • 多倍長整数演算とオーバーフロー検出に関する一般的なプログラミング知識
  • C言語のgoto文の使用例と目的