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

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

このコミットは、Goコンパイラの一部であるcmd/6gにおける、大きな整数をインデックスや配列サイズとして使用する際のバグ修正に関するものです。具体的には、比較を生成する前にsmallintconstのチェックが欠落していた問題に対処しています。

コミット

commit 1e233ad0759ad4e824a6bbb4bf2347d33cceba38
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Tue Nov 6 22:53:57 2012 +0100

    cmd/6g: fix use of large integers as indexes or array sizes.
    
    A check for smallintconst was missing before generating the
    comparisons.
    
    Fixes #4348.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6815088

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

https://github.com/golang/go/commit/1e233ad0759ad4e824a6bbb4bf2347d33cceba38

元コミット内容

cmd/6g: fix use of large integers as indexes or array sizes.

A check for smallintconst was missing before generating the
comparisons.

Fixes #4348.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6815088

変更の背景

このコミットは、Goコンパイラ6g(当時の64ビットアーキテクチャ向けGoコンパイラ)が、非常に大きな整数値を配列のインデックスやサイズとして使用する際に、不正な命令を生成してしまうバグを修正するために行われました。具体的には、64ビット整数への切り替え後、コンパイラが大きな配列境界やインデックスを扱う際に問題が発生していました。この問題は、コンパイラが比較命令を生成する際に、オペランドが小さな定数であるかどうかのチェック(smallintconst)を適切に行っていなかったことに起因します。結果として、コンパイルされたコードが実行時にクラッシュする可能性がありました。

前提知識の解説

  • Goコンパイラ (6g): Go言語の初期のコンパイラは、ターゲットアーキテクチャごとに異なる名前を持っていました。6gはAMD64(x86-64)アーキテクチャ向けのコンパイラを指します。Goコンパイラは、Goのソースコードを機械語に変換する役割を担っています。
  • アセンブリコード生成: コンパイラの重要なフェーズの一つに、中間表現からターゲットアーキテクチャのアセンブリコードを生成する部分があります。このコミットで変更されているcgen.cファイルは、このコード生成に関連する部分です。
  • 配列のインデックスとサイズ: プログラミング言語において、配列は固定長または可変長の要素の集まりです。配列の要素にアクセスするにはインデックスを使用し、配列のメモリ割り当てにはサイズが指定されます。これらの値が非常に大きい場合、コンパイラは特別な注意を払って処理する必要があります。
  • 定数伝播と最適化: コンパイラは、プログラムの実行効率を向上させるために様々な最適化を行います。定数伝播(Constant Propagation)はその一つで、コンパイル時に値が確定している定数を、その定数が使用される箇所に直接埋め込むことで、実行時の計算を減らします。smallintconstのようなチェックは、特定の最適化やコード生成パスを決定するために使用されます。例えば、小さな定数であれば直接命令に埋め込めるが、大きな定数であればレジスタにロードする必要がある、といった判断です。
  • panicindex: Go言語では、配列の範囲外アクセスなど、実行時に発生するエラーをpanicとして扱います。panicindexは、配列のインデックスが範囲外である場合に呼び出されるランタイム関数です。
  • Node構造体: Goコンパイラの内部では、ソースコードは抽象構文木(AST)として表現されます。ASTの各ノードはNode構造体で表され、変数、定数、演算子、関数呼び出しなど、プログラムの様々な要素を表現します。
  • Type構造体: Go言語の型システムは、Type構造体で表現されます。これには、型のサイズ(width)、種類(etype)、配列の境界(bound)などが含まれます。

技術的詳細

この修正は、src/cmd/6g/cgen.c内のagenr関数に焦点を当てています。agenr関数は、Goの式からアドレスを生成する役割を担っています。特に、配列のインデックスアクセス(OINDEXオペレーション)を処理する部分に問題がありました。

問題の核心は、配列のインデックスが配列の長さと比較される際に、インデックス値が非常に大きい場合にコンパイラが適切なアセンブリコードを生成できなかった点にあります。以前のコードでは、インデックス値が定数である場合、その定数値を直接比較命令に埋め込もうとしていました。しかし、この定数が64ビットの非常に大きな値である場合、特定のCPU命令では直接扱えず、レジスタにロードしてから比較する必要がありました。

修正では、smallintconst(nr)という新しいチェックが導入されました。これは、比較対象のオペランド(nr)が「小さな」整数定数であるかどうかを判断します。

  • もしnrsmallintconstであれば、以前と同様に直接比較命令(gins(optoas(OCMP, ...), &nlen, &n2))を生成します。
  • もしnrsmallintconstでなければ、つまり大きな整数定数である場合、一時的なレジスタ(tmp)を割り当て、その大きな定数をレジスタに移動(gmove(&n2, &tmp))してから、レジスタと配列の長さを比較する命令(gins(optoas(OCMP, ...), &nlen, &tmp))を生成するように変更されました。これにより、コンパイラは大きな整数定数を正しく扱うことができるようになります。

また、配列の境界チェック(nl->type->bound)においても同様の問題が発生する可能性があったため、nodconst(&nlen, t, nl->type->bound)で定数ノードを生成した後、smallintconst(&nlen)のチェックを追加し、必要に応じて一時レジスタにロードする処理が追加されています。

さらに、freelenという新しいフラグが導入され、nlenノードが一時的に割り当てられたレジスタを使用している場合に、そのレジスタを適切に解放(regfree(&nlen))するように制御しています。これにより、リソースリークを防ぎ、コンパイラの健全性を保っています。

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

src/cmd/6g/cgen.c

--- a/src/cmd/6g/cgen.c
+++ b/src/cmd/6g/cgen.c
@@ -566,6 +566,7 @@ agenr(Node *n, Node *a, Node *res)
 	Type *t;
 	uint32 w;
 	uint64 v;
+	int freelen;
 
 	if(debug['g']) {
 		dump("\nagenr-n", n);
@@ -576,6 +577,7 @@ agenr(Node *n, Node *a, Node *res)
 
 	switch(n->op) {
 	case OINDEX:
+		freelen = 0;
 		w = n->type->width;
 		// Generate the non-addressable child first.
 		if(nr->addable)
@@ -587,6 +589,7 @@ agenr(Node *n, Node *a, Node *res)
 					agenr(nl, &n3, res);
 				} else {
 					igen(nl, &nlen, res);
+					freelen = 1;
 					nlen.type = types[tptr];
 					nlen.xoffset += Array_array;
 					regalloc(&n3, types[tptr], res);
@@ -612,6 +615,7 @@ agenr(Node *n, Node *a, Node *res)
 				}
 			igen(nl, &nlen, res);
+			freelen = 1;
 			nlen.type = types[tptr];
 			nlen.xoffset += Array_array;
 			regalloc(&n3, types[tptr], res);
@@ -651,7 +655,14 @@ agenr(Node *n, Node *a, Node *res)
 			if(isslice(nl->type) || nl->type->etype == TSTRING) {
 				if(!debug['B'] && !n->bounded) {
 					nodconst(&n2, types[simtype[TUINT]], v);
-					gins(optoas(OCMP, types[simtype[TUINT]]), &nlen, &n2);
+					if(smallintconst(nr)) {
+						gins(optoas(OCMP, types[simtype[TUINT]]), &nlen, &n2);
+					} else {
+						regalloc(&tmp, types[simtype[TUINT]], N);
+						gmove(&n2, &tmp);
+						gins(optoas(OCMP, types[simtype[TUINT]]), &nlen, &tmp);
+						regfree(&tmp);
+					}
 					p1 = gbranch(optoas(OGT, types[simtype[TUINT]]), T, +1);
 					ginscall(panicindex, -1);
 					patch(p1, pc);
@@ -690,6 +701,12 @@ agenr(Node *n, Node *a, Node *res)
 				}
 			} else {
 				nodconst(&nlen, t, nl->type->bound);
+				if(!smallintconst(&nlen)) {
+					regalloc(&n5, t, N);
+					gmove(&nlen, &n5);
+					nlen = n5;
+					freelen = 1;
+				}
 			}
 			gins(optoas(OCMP, t), &n2, &nlen);
 			p1 = gbranch(optoas(OLT, t), T, +1);
@@ -721,7 +738,7 @@ agenr(Node *n, Node *a, Node *res)
 	indexdone:
 		*a = n3;
 		regfree(&n2);
-		if(!isconst(nl, CTSTR) && !isfixedarray(nl->type))
+		if(freelen)
 			regfree(&nlen);
 		break;

test/fixedbugs/issue4348.go

--- /dev/null
+++ b/test/fixedbugs/issue4348.go
@@ -0,0 +1,28 @@
+// 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
+// illegal instructions when using large array bounds or indexes.
+
+package main
+
+// 1<<32 on a 64-bit machine, 1 otherwise.
+const LARGE = ^uint(0)>>32 + 1
+
+func A() int {
+	var a []int
+	return a[LARGE]
+}
+
+func B(i int) int {
+	var b [LARGE]int
+	return b[i]
+}
+
+func main() {
+	n := A()
+	B(n)
+}

コアとなるコードの解説

src/cmd/6g/cgen.cの変更点

  1. freelen変数の導入: agenr関数の冒頭でint freelen;が追加され、OINDEXケースの開始時にfreelen = 0;で初期化されます。これは、nlenノードが一時レジスタを使用しているかどうかを追跡するためのフラグです。
  2. igen後のfreelen設定:
    • igen(nl, &nlen, res);の後にfreelen = 1;が追加されています。これは、igennlenにレジスタを割り当てた場合に、そのレジスタを後で解放する必要があることを示します。
  3. smallintconstチェックの追加:
    • スライスや文字列のインデックスアクセスにおける境界チェックの部分(if(isslice(nl->type) || nl->type->etype == TSTRING)ブロック内)で、nodconst(&n2, types[simtype[TUINT]], v);で定数ノードn2を生成した後、smallintconst(nr)のチェックが追加されました。
    • smallintconst(nr)が真の場合(インデックスが小さな定数)、従来のgins(optoas(OCMP, ...), &nlen, &n2);が実行されます。
    • 偽の場合(インデックスが大きな定数)、一時レジスタtmpを割り当て(regalloc(&tmp, ...))、n2の値をtmpに移動(gmove(&n2, &tmp))し、tmpnlenを比較する命令を生成(gins(optoas(OCMP, ...), &nlen, &tmp))します。その後、tmpレジスタは解放されます(regfree(&tmp))。
    • これにより、大きな定数インデックスがレジスタを介して正しく比較されるようになります。
  4. 固定長配列の境界チェックにおけるsmallintconstチェック:
    • 固定長配列の境界チェックの部分(else { nodconst(&nlen, t, nl->type->bound); ... }ブロック内)でも同様に、nodconst(&nlen, t, nl->type->bound);で定数ノードnlenを生成した後、!smallintconst(&nlen)のチェックが追加されました。
    • nlenが大きな定数である場合、一時レジスタn5を割り当て、nlenの値をn5に移動し、nlenn5に置き換えます。この際、freelen = 1;を設定して、後でn5を解放するようにします。
  5. nlenレジスタの条件付き解放:
    • indexdone:ラベルの後のクリーンアップ部分で、if(!isconst(nl, CTSTR) && !isfixedarray(nl->type))if(freelen)に変更されました。
    • これにより、freelenが設定されている場合にのみnlenレジスタが解放されるようになり、レジスタの不適切な解放やリークを防ぎます。

test/fixedbugs/issue4348.goの追加点

このファイルは、修正が正しく機能することを確認するための新しいテストケースです。

  1. // compileディレクティブ: このテストがコンパイル可能であることを示します。
  2. LARGE定数: ^uint(0)>>32 + 1という式で定義されるLARGE定数は、64ビットシステムでは1 << 32、それ以外では1となる非常に大きな符号なし整数を表します。これは、大きなインデックスや配列サイズをシミュレートするために使用されます。
  3. A()関数:
    • var a []intでスライスを宣言します。
    • return a[LARGE]で、LARGEという非常に大きな定数をスライスのインデックスとして使用します。このアクセスは実行時にパニックを引き起こすはずですが、コンパイラが不正なコードを生成しないことを確認します。
  4. B()関数:
    • var b [LARGE]intで、LARGEという非常に大きな定数をサイズとする配列を宣言します。
    • return b[i]で、この大きな配列にインデックスiでアクセスします。これもコンパイラが正しく処理できることを確認します。
  5. main()関数: A()B()を呼び出すことで、これらの関数がコンパイル時に問題なく処理されることを検証します。

このテストケースは、コンパイラが大きな整数定数を配列のインデックスやサイズとして使用する際に、不正なアセンブリ命令を生成しないことを保証します。

関連リンク

  • Go言語の公式Issueトラッカー: https://github.com/golang/go/issues (ただし、Issue #4348は現在のGitHubリポジトリでは直接見つかりませんでした。古いGoのIssueトラッカーやCL (Change List)システムに存在した可能性があります。)
  • Go Change List (CL) 6815088: https://golang.org/cl/6815088 (これはGoのコードレビューシステムGerritのリンクであり、このコミットの元の変更提案です。)

参考にした情報源リンク

  • Go言語のソースコード: https://github.com/golang/go
  • Goコンパイラの内部構造に関するドキュメントや記事 (一般的なGoコンパイラの知識)
  • アセンブリ言語とレジスタの利用に関する一般的な知識
  • コンパイラ最適化に関する一般的な知識
  • Go言語のpanicメカニズムに関する知識I have provided the detailed explanation of the commit as requested, following all the specified sections and formatting. I have also included the relevant links and explained the technical details and background.
# [インデックス 14328] ファイルの概要

このコミットは、Goコンパイラの一部である`cmd/6g`における、大きな整数をインデックスや配列サイズとして使用する際のバグ修正に関するものです。具体的には、比較を生成する前に`smallintconst`のチェックが欠落していた問題に対処しています。

## コミット

commit 1e233ad0759ad4e824a6bbb4bf2347d33cceba38 Author: Rémy Oudompheng oudomphe@phare.normalesup.org Date: Tue Nov 6 22:53:57 2012 +0100

cmd/6g: fix use of large integers as indexes or array sizes.

A check for smallintconst was missing before generating the
comparisons.

Fixes #4348.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6815088

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

[https://github.com/golang/go/commit/1e233ad0759ad4e824a6bbb4bf2347d33cceba38](https://github.com/golang/go/commit/1e233ad0759ad4e824a6bbb4bf2347d33cceba38)

## 元コミット内容

cmd/6g: fix use of large integers as indexes or array sizes.

A check for smallintconst was missing before generating the comparisons.

Fixes #4348.

R=golang-dev, rsc CC=golang-dev https://golang.org/cl/6815088


## 変更の背景

このコミットは、Goコンパイラ`6g`(当時の64ビットアーキテクチャ向けGoコンパイラ)が、非常に大きな整数値を配列のインデックスやサイズとして使用する際に、不正な命令を生成してしまうバグを修正するために行われました。具体的には、64ビット整数への切り替え後、コンパイラが大きな配列境界やインデックスを扱う際に問題が発生していました。この問題は、コンパイラが比較命令を生成する際に、オペランドが小さな定数であるかどうかのチェック(`smallintconst`)を適切に行っていなかったことに起因します。結果として、コンパイルされたコードが実行時にクラッシュする可能性がありました。

## 前提知識の解説

*   **Goコンパイラ (6g)**: Go言語の初期のコンパイラは、ターゲットアーキテクチャごとに異なる名前を持っていました。`6g`はAMD64(x86-64)アーキテクチャ向けのコンパイラを指します。Goコンパイラは、Goのソースコードを機械語に変換する役割を担っています。
*   **アセンブリコード生成**: コンパイラの重要なフェーズの一つに、中間表現からターゲットアーキテクチャのアセンブリコードを生成する部分があります。このコミットで変更されている`cgen.c`ファイルは、このコード生成に関連する部分です。
*   **配列のインデックスとサイズ**: プログラミング言語において、配列は固定長または可変長の要素の集まりです。配列の要素にアクセスするにはインデックスを使用し、配列のメモリ割り当てにはサイズが指定されます。これらの値が非常に大きい場合、コンパイラは特別な注意を払って処理する必要があります。
*   **定数伝播と最適化**: コンパイラは、プログラムの実行効率を向上させるために様々な最適化を行います。定数伝播(Constant Propagation)はその一つで、コンパイル時に値が確定している定数を、その定数が使用される箇所に直接埋め込むことで、実行時の計算を減らします。`smallintconst`のようなチェックは、特定の最適化やコード生成パスを決定するために使用されます。例えば、小さな定数であれば直接命令に埋め込めるが、大きな定数であればレジスタにロードする必要がある、といった判断です。
*   **`panicindex`**: Go言語では、配列の範囲外アクセスなど、実行時に発生するエラーを`panic`として扱います。`panicindex`は、配列のインデックスが範囲外である場合に呼び出されるランタイム関数です。
*   **`Node`構造体**: Goコンパイラの内部では、ソースコードは抽象構文木(AST)として表現されます。ASTの各ノードは`Node`構造体で表され、変数、定数、演算子、関数呼び出しなど、プログラムの様々な要素を表現します。
*   **`Type`構造体**: Go言語の型システムは、`Type`構造体で表現されます。これには、型のサイズ(`width`)、種類(`etype`)、配列の境界(`bound`)などが含まれます。

## 技術的詳細

この修正は、`src/cmd/6g/cgen.c`内の`agenr`関数に焦点を当てています。`agenr`関数は、Goの式からアドレスを生成する役割を担っています。特に、配列のインデックスアクセス(`OINDEX`オペレーション)を処理する部分に問題がありました。

問題の核心は、配列のインデックスが配列の長さと比較される際に、インデックス値が非常に大きい場合にコンパイラが適切なアセンブリコードを生成できなかった点にあります。以前のコードでは、インデックス値が定数である場合、その定数値を直接比較命令に埋め込もうとしていました。しかし、この定数が64ビットの非常に大きな値である場合、特定のCPU命令では直接扱えず、レジスタにロードしてから比較する必要がありました。

修正では、`smallintconst(nr)`という新しいチェックが導入されました。これは、比較対象のオペランド(`nr`)が「小さな」整数定数であるかどうかを判断します。

*   もし`nr`が`smallintconst`であれば、以前と同様に直接比較命令(`gins(optoas(OCMP, ...), &nlen, &n2)`)を生成します。
*   もし`nr`が`smallintconst`でなければ、つまり大きな整数定数である場合、一時的なレジスタ(`tmp`)を割り当て、その大きな定数をレジスタに移動(`gmove(&n2, &tmp)`)してから、レジスタと配列の長さを比較する命令(`gins(optoas(OCMP, ...), &nlen, &tmp)`)を生成するように変更されました。これにより、コンパイラは大きな整数定数を正しく扱うことができるようになります。

また、配列の境界チェック(`nl->type->bound`)においても同様の問題が発生する可能性があったため、`nodconst(&nlen, t, nl->type->bound)`で定数ノードを生成した後、`smallintconst(&nlen)`のチェックを追加し、必要に応じて一時レジスタにロードする処理が追加されています。

さらに、`freelen`という新しいフラグが導入され、`nlen`ノードが一時的に割り当てられたレジスタを使用している場合に、そのレジスタを適切に解放(`regfree(&nlen)`)するように制御しています。これにより、リソースリークを防ぎ、コンパイラの健全性を保っています。

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

### `src/cmd/6g/cgen.c`

```diff
--- a/src/cmd/6g/cgen.c
+++ b/src/cmd/6g/cgen.c
@@ -566,6 +566,7 @@ agenr(Node *n, Node *a, Node *res)
 	Type *t;
 	uint32 w;
 	uint64 v;
+	int freelen;
 
 	if(debug['g']) {
 		dump("\nagenr-n", n);
@@ -576,6 +577,7 @@ agenr(Node *n, Node *a, Node *res)
 
 	switch(n->op) {
 	case OINDEX:
+		freelen = 0;
 		w = n->type->width;
 		// Generate the non-addressable child first.
 		if(nr->addable)
@@ -587,6 +589,7 @@ agenr(Node *n, Node *a, Node *res)
 					agenr(nl, &n3, res);
 				} else {
 					igen(nl, &nlen, res);
+					freelen = 1;
 					nlen.type = types[tptr];
 					nlen.xoffset += Array_array;
 					regalloc(&n3, types[tptr], res);
@@ -612,6 +615,7 @@ agenr(Node *n, Node *a, Node *res)
 				}
 			igen(nl, &nlen, res);
+			freelen = 1;
 			nlen.type = types[tptr];
 			nlen.xoffset += Array_array;
 			regalloc(&n3, types[tptr], res);
@@ -651,7 +655,14 @@ agenr(Node *n, Node *a, Node *res)
 			if(isslice(nl->type) || nl->type->etype == TSTRING) {
 				if(!debug['B'] && !n->bounded) {
 					nodconst(&n2, types[simtype[TUINT]], v);
-					gins(optoas(OCMP, types[simtype[TUINT]]), &nlen, &n2);
+					if(smallintconst(nr)) {
+						gins(optoas(OCMP, types[simtype[TUINT]]), &nlen, &n2);
+					} else {
+						regalloc(&tmp, types[simtype[TUINT]], N);
+						gmove(&n2, &tmp);
+						gins(optoas(OCMP, types[simtype[TUINT]]), &nlen, &tmp);
+						regfree(&tmp);
+					}
 					p1 = gbranch(optoas(OGT, types[simtype[TUINT]]), T, +1);
 					ginscall(panicindex, -1);
 					patch(p1, pc);
@@ -690,6 +701,12 @@ agenr(Node *n, Node *a, Node *res)
 				}
 			} else {
 				nodconst(&nlen, t, nl->type->bound);
+				if(!smallintconst(&nlen)) {
+					regalloc(&n5, t, N);
+					gmove(&nlen, &n5);
+					nlen = n5;
+					freelen = 1;
+				}
 			}
 			gins(optoas(OCMP, t), &n2, &nlen);
 			p1 = gbranch(optoas(OLT, t), T, +1);
@@ -721,7 +738,7 @@ agenr(Node *n, Node *a, Node *res)
 	indexdone:
 		*a = n3;
 		regfree(&n2);
-		if(!isconst(nl, CTSTR) && !isfixedarray(nl->type))
+		if(freelen)
 			regfree(&nlen);
 		break;

test/fixedbugs/issue4348.go

--- /dev/null
+++ b/test/fixedbugs/issue4348.go
@@ -0,0 +1,28 @@
+// 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
+// illegal instructions when using large array bounds or indexes.
+
+package main
+
+// 1<<32 on a 64-bit machine, 1 otherwise.
+const LARGE = ^uint(0)>>32 + 1
+
+func A() int {
+	var a []int
+	return a[LARGE]
+}
+
+func B(i int) int {
+	var b [LARGE]int
+	return b[i]
+}
+
+func main() {
+	n := A()
+	B(n)
+}

コアとなるコードの解説

src/cmd/6g/cgen.cの変更点

  1. freelen変数の導入: agenr関数の冒頭でint freelen;が追加され、OINDEXケースの開始時にfreelen = 0;で初期化されます。これは、nlenノードが一時レジスタを使用しているかどうかを追跡するためのフラグです。
  2. igen後のfreelen設定:
    • igen(nl, &nlen, res);の後にfreelen = 1;が追加されています。これは、igennlenにレジスタを割り当てた場合に、そのレジスタを後で解放する必要があることを示します。
  3. smallintconstチェックの追加:
    • スライスや文字列のインデックスアクセスにおける境界チェックの部分(if(isslice(nl->type) || nl->type->etype == TSTRING)ブロック内)で、nodconst(&n2, types[simtype[TUINT]], v);で定数ノードn2を生成した後、smallintconst(nr)のチェックが追加されました。
    • smallintconst(nr)が真の場合(インデックスが小さな定数)、従来のgins(optoas(OCMP, ...), &nlen, &n2);が実行されます。
    • 偽の場合(インデックスが大きな定数)、一時レジスタtmpを割り当て(regalloc(&tmp, ...))、n2の値をtmpに移動(gmove(&n2, &tmp))し、tmpnlenを比較する命令を生成(gins(optoas(OCMP, ...), &nlen, &tmp))します。その後、tmpレジスタは解放されます(regfree(&tmp))。
    • これにより、大きな定数インデックスがレジスタを介して正しく比較されるようになります。
  4. 固定長配列の境界チェックにおけるsmallintconstチェック:
    • 固定長配列の境界チェックの部分(else { nodconst(&nlen, t, nl->type->bound); ... }ブロック内)でも同様に、nodconst(&nlen, t, nl->type->bound);で定数ノードnlenを生成した後、!smallintconst(&nlen)のチェックが追加されました。
    • nlenが大きな定数である場合、一時レジスタn5を割り当て、nlenの値をn5に移動し、nlenn5に置き換えます。この際、freelen = 1;を設定して、後でn5を解放するようにします。
  5. nlenレジスタの条件付き解放:
    • indexdone:ラベルの後のクリーンアップ部分で、if(!isconst(nl, CTSTR) && !isfixedarray(nl->type))if(freelen)に変更されました。
    • これにより、freelenが設定されている場合にのみnlenレジスタが解放されるようになり、レジスタの不適切な解放やリークを防ぎます。

test/fixedbugs/issue4348.goの追加点

このファイルは、修正が正しく機能することを確認するための新しいテストケースです。

  1. // compileディレクティブ: このテストがコンパイル可能であることを示します。
  2. LARGE定数: ^uint(0)>>32 + 1という式で定義されるLARGE定数は、64ビットシステムでは1 << 32、それ以外では1となる非常に大きな符号なし整数を表します。これは、大きなインデックスや配列サイズをシミュレートするために使用されます。
  3. A()関数:
    • var a []intでスライスを宣言します。
    • return a[LARGE]で、LARGEという非常に大きな定数をスライスのインデックスとして使用します。このアクセスは実行時にパニックを引き起こすはずですが、コンパイラが不正なコードを生成しないことを確認します。
  4. B()関数:
    • var b [LARGE]intで、LARGEという非常に大きな定数をサイズとする配列を宣言します。
    • return b[i]で、この大きな配列にインデックスiでアクセスします。これもコンパイラが正しく処理できることを確認します。
  5. main()関数: A()B()を呼び出すことで、これらの関数がコンパイル時に問題なく処理されることを検証します。

このテストケースは、コンパイラが大きな整数定数を配列のインデックスやサイズとして使用する際に、不正なアセンブリ命令を生成しないことを保証します。

関連リンク

  • Go言語の公式Issueトラッカー: https://github.com/golang/go/issues (ただし、Issue #4348は現在のGitHubリポジトリでは直接見つかりませんでした。古いGoのIssueトラッカーやCL (Change List)システムに存在した可能性があります。)
  • Go Change List (CL) 6815088: https://golang.org/cl/6815088 (これはGoのコードレビューシステムGerritのリンクであり、このコミットの元の変更提案です。)

参考にした情報源リンク

  • Go言語のソースコード: https://github.com/golang/go
  • Goコンパイラの内部構造に関するドキュメントや記事 (一般的なGoコンパイラの知識)
  • アセンブリ言語とレジスタの利用に関する一般的な知識
  • コンパイラ最適化に関する一般的な知識
  • Go言語のpanicメカニズムに関する知識