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

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

このコミットは、Go言語のテストファイル test/fixedbugs/issue4348.go を再有効化し、特定のバグ(Issue 4348)のテストが適切に実行されるように修正するものです。具体的には、大きすぎる配列がスタックフレームに収まらない問題を、その配列をグローバル変数として宣言することで解決しています。

コミット

commit 09cb91eddcfa3b4a6da6ec1ef774721e72585b48
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Fri Jan 18 22:54:27 2013 +0100

    test: re-enable issue4348.go.
    
    The test array is too large to fit a stack frame
    but can be a global.
    
    R=golang-dev, minux.ma
    CC=golang-dev
    https://golang.org/cl/7127059

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

https://github.com/golang/go/commit/09cb91eddcfa3b4a6da6ec1ef774721e72585b48

元コミット内容

test: re-enable issue4348.go.

The test array is too large to fit a stack frame
but can be a global.

変更の背景

このコミットの背景には、Goコンパイラにおける特定のバグ、すなわち「Issue 4348」が存在します。この問題は、Goコンパイラが64ビット整数への切り替えを行った後、非常に大きな配列の境界やインデックスを使用する際に、不正な命令を生成してしまうというものでした。

test/fixedbugs/issue4348.go は、このバグを検出するためのテストファイルでしたが、以前のコミット(おそらくIssue 4666の修正に関連して)で一時的に無効化されていました。元のファイルには // skip ディレクティブがあり、「このテストは、関連するコードがIssue 4666の修正後に拒否されるため、現在スキップされています」というコメントがありました。

しかし、このテストが本来検出するべき問題(大きな配列がスタックに収まらないこと)は依然として重要であり、テストを再有効化する必要がありました。特に、関数内で宣言された大きな配列がスタックメモリを圧迫し、スタックオーバーフローや不正なメモリアクセスを引き起こす可能性があったため、このテストを修正し、再びコンパイル可能にすることが求められました。

このコミットは、テストの目的を達成しつつ、Goのコンパイラが大きな配列を適切に扱えるようにするための修正の一環として行われました。

前提知識の解説

1. スタックとヒープ

プログラムが実行される際、メモリは主に「スタック」と「ヒープ」という2つの領域に分けられます。

  • スタック (Stack):

    • 関数呼び出しやローカル変数(関数内で宣言される変数)の格納に使われるメモリ領域です。
    • LIFO (Last-In, First-Out) の構造を持ち、高速なアクセスが可能です。
    • メモリの割り当てと解放が自動的に行われます(関数が呼び出されるとスタックフレームが積まれ、関数が終了すると解放される)。
    • サイズが比較的限られており、大きなデータを格納しようとすると「スタックオーバーフロー」が発生する可能性があります。
  • ヒープ (Heap):

    • プログラムの実行中に動的にメモリを割り当てるために使われる領域です。
    • スタックよりもはるかに大きなデータを格納できます。
    • メモリの割り当てと解放はプログラマが明示的に行うか、ガベージコレクタによって管理されます。Go言語ではガベージコレクタがヒープメモリを管理します。
    • スタックに比べてアクセス速度は遅いですが、柔軟性があります。

2. Go言語における配列とメモリ割り当て

Go言語では、配列は値型です。つまり、配列変数を別の変数に代入したり、関数に引数として渡したりすると、配列全体のコピーが作成されます。

  • ローカル配列: 関数内で var a [N]int のように宣言された配列は、通常、その関数のスタックフレームに割り当てられます。N が非常に大きい場合、スタックの容量を超えてしまい、スタックオーバーフローを引き起こす可能性があります。
  • グローバル配列: パッケージレベルで var a [N]int のように宣言された配列は、プログラムのデータセグメント(静的メモリ領域)に割り当てられるか、またはヒープに割り当てられます。これにより、スタックの制約を受けずに大きな配列を扱うことができます。

3. Goのテストディレクティブ // skip// compile

Goのテストファイルには、特別なコメントディレクティブを記述することで、テストの挙動を制御できます。

  • // skip: このディレクティブがファイルの先頭に記述されている場合、go test コマンドはそのファイルをテスト対象から除外します。つまり、コンパイルも実行もされません。これは、一時的にテストを無効にしたい場合や、特定の環境でのみ実行されるべきテストをマークする際に使用されます。
  • // compile: このディレクティブがファイルの先頭に記述されている場合、go test コマンドはそのファイルをコンパイルしようとしますが、実行はしません。これは、コンパイルエラーを検出するためのテスト(例えば、特定のコードがコンパイル時にエラーになるべきであることを確認するテスト)によく使用されます。

4. Issue 4348と64ビット整数

Go言語のコンパイラが64ビット整数を扱うようになった際、大きな配列のインデックス計算や境界チェックにおいて、特定の最適化やコード生成に問題が生じました。これにより、コンパイラが不正な機械語命令を生成し、プログラムがクラッシュしたり、予期せぬ動作をしたりするバグが発生しました。Issue 4348は、この問題に対処するためのものでした。

技術的詳細

このコミットの技術的な核心は、Go言語における大きな配列のメモリ割り当て戦略の変更です。

元の issue4348.go テストファイルでは、func B(i int) の中で var b [LARGE]int という形で非常に大きな配列 b がローカル変数として宣言されていました。ここで LARGE はおそらく非常に大きな定数(例えば 1 << 20 やそれ以上)を指しており、このサイズの配列を関数のスタックフレームに割り当てようとすると、スタックの容量を簡単に超えてしまいます。

一般的なシステムでは、スタックのサイズは数MB程度に制限されています。例えば、1MBのスタックを持つシステムで [1000000]int (約8MB) の配列をローカル変数として宣言しようとすると、スタックオーバーフローが発生し、プログラムが異常終了する原因となります。

このコミットでは、この問題を解決するために、配列 b の宣言を func B の内部からパッケージレベル(グローバルスコープ)に移動しました。

// 変更前:
func B(i int) int {
    var b [LARGE]int // ローカル変数としてスタックに割り当てられる
    return b[i]
}

// 変更後:
var b [LARGE]int // グローバル変数としてデータセグメントまたはヒープに割り当てられる

func B(i int) int {
    return b[i] // グローバル変数bを参照
}

グローバル変数として宣言された配列は、スタックではなく、プログラムのデータセグメント(静的メモリ領域)またはヒープに割り当てられます。これにより、スタックサイズの制約を受けずに非常に大きな配列を扱うことが可能になります。

また、テストファイルの冒頭のディレクティブが // skip から // compile に変更されたことも重要です。これは、このテストがもはや実行をスキップするのではなく、コンパイルが成功することを確認するためのテストになったことを意味します。つまり、大きな配列をグローバルに配置することで、コンパイラがこのコードを正しく処理できるようになり、不正な命令生成の問題が解決されたことを示唆しています。

この変更により、Goコンパイラが64ビット整数を扱う際の大きな配列の処理に関するバグが、テストによって適切に検証できるようになりました。

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

変更は test/fixedbugs/issue4348.go ファイルに対して行われました。

--- a/test/fixedbugs/issue4348.go
+++ b/test/fixedbugs/issue4348.go
@@ -1,13 +1,10 @@
-// skip
-
-// NOTE: this test is now skipped because the relevant code
-// is rejected after fixing issue 4666.
+// compile
 
 // Copyright 2012 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 4238. After switch to 64-bit ints the compiler generates
+// Issue 4348. After switch to 64-bit ints the compiler generates
 // illegal instructions when using large array bounds or indexes.
 
 package main
@@ -20,8 +17,9 @@ func A() int {\n 	return a[LARGE]\n }\n \n+var b [LARGE]int\n+\n func B(i int) int {\n-\tvar b [LARGE]int\n \treturn b[i]\n }\n \n```

具体的な変更点は以下の通りです。

1.  **ファイルの先頭のディレクティブの変更**:
    *   `- // skip` が削除されました。
    *   `- // NOTE: this test is now skipped because the relevant code`
    *   `- // is rejected after fixing issue 4666.` が削除されました。
    *   `+ // compile` が追加されました。
2.  **コメントの修正**:
    *   `- // Issue 4238. After switch to 64-bit ints the compiler generates` が削除されました。
    *   `+ // Issue 4348. After switch to 64-bit ints the compiler generates` が追加されました。
    *   これは、関連するIssue番号が4238から4348に修正されたことを示しています。
3.  **配列 `b` の宣言位置の変更**:
    *   `func B(i int)` の内部にあった `-\tvar b [LARGE]int` が削除されました。
    *   その代わりに、`func A()` と `func B()` の間に `+var b [LARGE]int` が追加されました。これにより、`b` はグローバル変数として宣言されるようになりました。

## コアとなるコードの解説

このコミットの主要な目的は、`issue4348.go` テストがGoコンパイラのバグ(Issue 4348)を正しく検出できるようにすることです。

*   **`// skip` から `// compile` への変更**:
    *   元の `// skip` ディレクティブは、このテストファイルがコンパイルも実行もされないようにしていました。これは、以前のGoコンパイラのバージョンでは、このテストが意図しないコンパイルエラーを引き起こしたり、Issue 4666の修正によって関連コードが拒否されたりしたためと考えられます。
    *   `// compile` への変更は、このテストがコンパイル可能であることを確認する目的で実行されるようになったことを意味します。つまり、大きな配列をグローバルに配置する修正によって、コンパイラがこのコードを正しく処理できるようになったため、テストを再有効化できるようになったのです。

*   **`Issue 4238` から `Issue 4348` へのコメント修正**:
    *   これは単なるコメントの修正であり、このテストが対象とするバグの正式なIssue番号が4348であることを明確にしています。

*   **`var b [LARGE]int` の移動**:
    *   これが最も重要な変更点です。
    *   変更前は、`func B(i int)` の内部で `var b [LARGE]int` と宣言されていました。`LARGE` が非常に大きな値であるため、この配列は関数のスタックフレームに割り当てられようとします。しかし、スタックの容量には限りがあるため、このような大きな配列をスタックに置くとスタックオーバーフローが発生し、プログラムがクラッシュする原因となります。Goコンパイラが64ビット整数を扱うようになった際に、この問題が顕在化したと考えられます。
    *   変更後は、`var b [LARGE]int` がパッケージレベル(グローバルスコープ)に移動されました。これにより、配列 `b` はスタックではなく、プログラムのデータセグメント(静的メモリ領域)またはヒープに割り当てられるようになります。グローバル変数として宣言された配列は、プログラムの寿命全体にわたって存在し、スタックサイズの制約を受けないため、非常に大きなデータを安全に格納できます。
    *   `func B(i int)` 内では、もはや `b` を宣言する必要はなく、グローバルに宣言された `b` を直接参照するようになります。

このコミットは、Goコンパイラが大きな配列を扱う際のメモリ割り当てとコード生成の問題を解決し、関連するテストが正しく機能するようにするための重要なステップでした。

## 関連リンク

*   Go言語の公式Issueトラッカー: [https://github.com/golang/go/issues](https://github.com/golang/go/issues)
*   Go CL (Change List) 7127059: [https://golang.org/cl/7127059](https://golang.org/cl/7127059) (これはGoのコードレビューシステムGerritのリンクであり、コミットメッセージに記載されています)

## 参考にした情報源リンク

*   Go issue 4348に関するWeb検索結果:
    *   [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHBDTSdM2DJRptu5_uMVaTWp6J8h2xmncDwQvze19hh1HNdEm4ustP4vhOX0RW2I6u2wlQNr6djjai16rgROs8r5C9T9Teya6FSMCTVJDlNRx19BFSpfzrVeYMqx54K7V02k17zhs2OdbDaZ9jd3RdeeRCd-OtGa8ehhy2ayWT5](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHBDTSdM2DJRptu5_uMVaTWp6J8h2xmncDwQvze19hh1HNdEm4ustP4vhOX0RW2I6u2wlQNr6djjai16rgROs8r5C9T9Teya6FSMCTVJDlNRx19BFSpfzrVeYMqx54K7V02k17zhs2OdbDaZ9jd3RdeeRCd-OtGa8ehhy2ayWT5)
    *   [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGGBoYHwLCSxHLrm_kL4qnH942LwWf0kYnuaLI0h-MnFj-WeQLt3hjtIDjSL0YQjVRg4GGlQreRahiQA9DALg8cv3bSzkNDDRL0FVKGyPp3E_dqSDreLa31OdTkI9ywekO-xY9_mAGyyZS6ndGx2NMAuu9zvY7hKHL9OAseGw==](https://vertexaisearch.cloud.google.google.com/grounding-api-redirect/AUZIYQGGBoYHwLCSxHLrm_kL4qnH942LwWf0kYnuaLI0h-MnFj-WeQLt3hjtIDjSL0YQjVRg4GGlQreRahiQA9DALg8cv3bSzkNDDRL0FVKGyPp3E_dqSDreLa31OdTkI9ywekO-xY9_mAGyyZS6ndGx2NMAuu9zvY7hKHL9OAseGw==)
*   Go issue 4666に関するWeb検索結果:
    *   [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGJM-FYzAP54_GXCzQsNJ2YYL2P3xkYQu2mPBU5Z8QyCg1MgZiib4vV9pAoNmlV5igE0cq3xJzgWiXJZTh3Gv7wtuccrB1oNdPUnOlE_FrDwTarzwZ--2LiS633FLq3bcGMWllerbpVgbIRCA==](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGJM-FYzAP54_GXCzQsNJ2YYL2P3xkYQu2mPBU5Z8QyCg1MgZiib4vV9pAoNmlV5igE0cq3xJzgWiXJZTh3Gv7wtuccrB1oNdPUnOlE_FrDwTarzwZ--2LiS633FLq3bcGMWllerbpVgbIRCA==)
    *   [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG1a56hNg45WMSBcZ0B96zEEVbh2RhCazMiV4ModY-vHH_62jcDm68Mfln0S3R13nT6H5IgeRLZEvWhCqn-1AfSVU3_2Tx8cBYfG6O1WzwYOd_uuTXQZ-4ouUAoS-U=](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG1a56hNg45WMSBcZ0B96zEEVbh2RhCazMiV4ModY-vHH_62jcDm68Mfln0S3R13nT6H5IgeRLZEvWhCqn-1AfSVU3_2Tx8cBYfG6O1WzwYOd_uuTXQZ-4ouUAoS-U=)