[インデックス 14383] ファイルの概要
このコミットは、Goコンパイラ(cmd/5g
とcmd/6g
)が「torture test」を完全にパスできるようにするための修正です。具体的には、レジスタ割り当てのロジックを改善し、複雑なレジスタの保存・復元シーケンスを回避することで、生成されるコードの効率と正確性を向上させています。これにより、特定の複雑なコードパターン(特に深いネストされた構造体や配列のアクセス)においてコンパイラが正しく動作するようになります。
コミット
commit eb4f4d16ae6333171c14f8da304f2bfa2829e4ff
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Mon Nov 12 23:56:11 2012 +0100
cmd/5g, cmd/6g: pass the full torture test.
The patch adds more cases to agenr to allocate registers later,
and makes 6g generate addresses for sgen in something else than
SI and DI. It avoids a complex save/restore sequence that
amounts to allocate a register before descending in subtrees.
Fixes #4207.
R=golang-dev, dave, rsc
CC=golang-dev
https://golang.org/cl/6817080
---
src/cmd/5g/cgen.c | 15 +++++++++++++++
src/cmd/5g/gsubr.c | 1 +
src/cmd/6g/cgen.c | 44 ++++++++++++++++++++++++++------------------
test/torture.go | 31 +++++++++++++++----------------
4 files changed, 57 insertions(+), 34 deletions(-)
diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c
index b7abc9e4ec..008d1b9489 100644
--- a/src/cmd/5g/cgen.c
+++ b/src/cmd/5g/cgen.c
@@ -893,6 +893,21 @@ agenr(Node *n, Node *a, Node *res)
nr = n->right;
switch(n->op) {
+ case ODOT:
+ case ODOTPTR:
+ case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
+ igen(n, &n1, res);
+ regalloc(a, types[tptr], &n1);
+ agen(&n1, a);
+ regfree(&n1);
+ break;
+
+ case OIND:
+ cgenr(n->left, a, res);
+ break;
+
case OINDEX:
p2 = nil; // to be patched to panicindex.
w = n->type->width;
diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c
index 8340e8a98b..916d2a7453 100644
--- a/src/cmd/5g/gsubr.c
+++ b/src/cmd/5g/gsubr.c
@@ -361,6 +361,7 @@ regalloc(Node *n, Type *t, Node *o)
regpc[i] = (uintptr)getcallerpc(&n);
goto out;
}
+ print("registers allocated at\n");
for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
print("%d %p\n", i, regpc[i]);
yyerror("out of fixed registers");
diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c
index 751a5b7f13..1333dc194f 100644
--- a/src/cmd/6g/cgen.c
+++ b/src/cmd/6g/cgen.c
@@ -576,6 +576,21 @@ agenr(Node *n, Node *a, Node *res)
nr = n->right;
switch(n->op) {
+ case ODOT:
+ case ODOTPTR:
+ case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
+ igen(n, &n1, res);
+ regalloc(a, types[tptr], &n1);
+ agen(&n1, a);
+ regfree(&n1);
+ break;
+
+ case OIND:
+ cgenr(n->left, a, res);
+ break;
+
case OINDEX:
freelen = 0;
w = n->type->width;
@@ -1309,7 +1324,7 @@ stkof(Node *n)
void
sgen(Node *n, Node *ns, int64 w)
{
- Node nodl, nodr, oldl, oldr, cx, oldcx, tmp;
+ Node nodl, nodr, nodsi, noddi, cx, oldcx, tmp;
int32 c, q, odst, osrc;
if(debug['g']) {
@@ -1353,22 +1368,18 @@ sgen(Node *n, Node *ns, int64 w)
}
if(n->ullman >= ns->ullman) {
-\t\tsavex(D_SI, &nodr, &oldr, N, types[tptr]);
-\t\tagen(n, &nodr);
-\
-\t\tregalloc(&nodr, types[tptr], &nodr);\t// mark nodr as live
-\t\tsavex(D_DI, &nodl, &oldl, N, types[tptr]);
-\t\tagen(ns, &nodl);
-\t\tregfree(&nodr);
+\t\tagenr(n, &nodr, N);\n+\t\tagenr(ns, &nodl, N);
} else {
-\t\tsavex(D_DI, &nodl, &oldl, N, types[tptr]);
-\t\tagen(ns, &nodl);
-\
-\t\tregalloc(&nodl, types[tptr], &nodl);\t// mark nodl as live
-\t\tsavex(D_SI, &nodr, &oldr, N, types[tptr]);
-\t\tagen(n, &nodr);
-\t\tregfree(&nodl);
+\t\tagenr(ns, &nodl, N);\n+\t\tagenr(n, &nodr, N);
}
+\tnodreg(&noddi, types[tptr], D_DI);\n+\tnodreg(&nodsi, types[tptr], D_SI);\n+\tgmove(&nodl, &noddi);\n+\tgmove(&nodr, &nodsi);\n+\tregfree(&nodl);\n+\tregfree(&nodr);\n
c = w % 8; // bytes
q = w / 8; // quads
@@ -1425,9 +1436,6 @@ sgen(Node *n, Node *ns, int64 w)
}
}
-\n-\trestx(&nodl, &oldl);\n-\trestx(&nodr, &oldr);\n \trestx(&cx, &oldcx);\n }\n
diff --git a/test/torture.go b/test/torture.go
index c510bb9237..d14d78fd14 100644
--- a/test/torture.go
+++ b/test/torture.go
@@ -279,12 +279,12 @@ func ChainAssertArrayIndex(u *UArr) J {
Children[0].(*UArr).
Children[0].(*UArr).
Children[0].(*UArr).
-\t\t// Children[0].(*UArr).\n-\t\t// Children[0].(*UArr).\n-\t\t// Children[0].(*UArr).\n-\t\t// Children[0].(*UArr).\n-\t\t// Children[0].(*UArr).\n-\t\t// Children[0].(*UArr).\n+\t\tChildren[0].(*UArr).\n+\t\tChildren[0].(*UArr).\n+\t\tChildren[0].(*UArr).\n+\t\tChildren[0].(*UArr).\n+\t\tChildren[0].(*UArr).\n+\t\tChildren[0].(*UArr).\n Children[0]
}
func ChainAssertArrayptrIndex(u *UArrPtr) J {
-\t// TODO: don\'t crash on longer chains.\n \treturn u.\n \t\tChildren[0].(*UArrPtr).\n \t\tChildren[0].(*UArrPtr).\n \t\tChildren[0].(*UArrPtr).\n \t\tChildren[0].(*UArrPtr).\n-\t\t// Children[0].(*UArrPtr).\n-\t\t// Children[0].(*UArrPtr).\n-\t\t// Children[0].(*UArrPtr).\n-\t\t// Children[0].(*UArrPtr).\n-\t\t// Children[0].(*UArrPtr).\n-\t\t// Children[0].(*UArrPtr).\n-\t\t// Children[0].(*UArrPtr).\n-\t\t// Children[0].(*UArrPtr).\n-\t\t// Children[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n \t\tChildren[0]\n }
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/eb4f4d16ae6333171c14f8da304f2bfa2829e4ff
元コミット内容
cmd/5g, cmd/6g: pass the full torture test.
The patch adds more cases to agenr to allocate registers later,
and makes 6g generate addresses for sgen in something else than
SI and DI. It avoids a complex save/restore sequence that
amounts to allocate a register before descending in subtrees.
Fixes #4207.
変更の背景
このコミットの主な背景は、Goコンパイラ(特に5g
と6g
)が「torture test」と呼ばれる非常に厳格なテストスイートを完全にパスできないという問題に対処することでした。torture.go
ファイルに見られるように、このテストは深いネストを持つ構造体やポインタのチェインアクセスなど、コンパイラにとって複雑なシナリオを網羅しています。
以前のコンパイラのレジスタ割り当てロジックでは、これらの複雑なケースにおいて非効率なコードが生成されたり、場合によっては誤ったコードが生成されたりする可能性がありました。特に、サブツリー(式の部分)を処理する前にレジスタを割り当てる必要があり、その結果として複雑なレジスタの保存(savex
)と復元(restx
)シーケンスが発生していました。これは、レジスタが不足したり、不必要なメモリへの退避・復元が発生したりする原因となっていました。
コミットメッセージにある「Fixes #4207」は、この変更が特定の課題を解決したことを示唆していますが、Goプロジェクトの公開Issueトラッカーで「Issue #4207」を検索しても、このコミットと直接関連する具体的なGoのIssueは見つかりませんでした。これは、内部的なトラッキング番号であるか、あるいは非常に古いIssueであり、現在の公開データベースには存在しない可能性があります。しかし、コミットメッセージの残りの部分から、コンパイラのレジスタ割り当てとコード生成の効率化が目的であったことは明らかです。
前提知識の解説
このコミットを理解するためには、以下のGoコンパイラの内部構造とコンピュータサイエンスの基本的な概念を理解しておく必要があります。
- Goコンパイラ (
cmd/5g
,cmd/6g
):- Go言語の初期のコンパイラは、ターゲットアーキテクチャごとに独立したバイナリとして提供されていました。
cmd/5g
: ARMアーキテクチャ向けのGoコンパイラです。cmd/6g
: AMD64 (x86-64) アーキテクチャ向けのGoコンパイラです。- これらのコンパイラは、Goのソースコードを中間表現(AST: Abstract Syntax Tree)に変換し、その後、ターゲットアーキテクチャの機械語に変換する役割を担います。
- レジスタ割り当て (Register Allocation):
- CPUには、高速なデータアクセスが可能な「レジスタ」と呼ばれる一時的な記憶領域があります。
- コンパイラの重要な最適化の一つに、プログラムの変数をこれらのレジスタに効率的に割り当てる「レジスタ割り当て」があります。
- レジスタに割り当てられた変数は、メモリから読み書きするよりもはるかに高速にアクセスできるため、プログラムの実行速度が向上します。
- レジスタの数は限られているため、どの変数をどのタイミングでレジスタに割り当てるか、またレジスタが不足した場合にどの変数をメモリに退避(スピル)させるか、という決定がコンパイラの性能に大きく影響します。
- 抽象構文木 (AST: Abstract Syntax Tree):
- コンパイラがソースコードを解析する際に生成する、プログラムの構造を木構造で表現したものです。
ODOT
,ODOTPTR
,OCALLFUNC
,OCALLMETH
,OCALLINTER
,OIND
,OINDEX
などは、Goコンパイラ内部で定義されているASTノードのオペレーションタイプです。ODOT
: 構造体のフィールドアクセス(例:obj.field
)ODOTPTR
: ポインタを介した構造体のフィールドアクセス(例:ptr.field
)OCALLFUNC
: 通常の関数呼び出しOCALLMETH
: メソッド呼び出しOCALLINTER
: インターフェースメソッド呼び出しOIND
: ポインタのデリファレンス(間接参照、例:*ptr
)OINDEX
: 配列やスライスのインデックスアクセス(例:arr[i]
)
agenr
関数:- "Address Generation Register" の略である可能性が高いです。
- Goコンパイラにおいて、メモリアドレスを計算し、その結果をレジスタに格納するためのコードを生成する役割を持つ関数です。
- 特に、複雑なアドレス計算(構造体フィールドへのアクセス、配列要素へのアクセスなど)が必要な場合に利用されます。
sgen
関数:- "Structure Generation" または "Store Generation" の略である可能性が高いです。
- Goコンパイラにおいて、構造体や配列などの複合データ型の値をコピーしたり、メモリ間でデータを移動したりするためのコードを生成する役割を持つ関数です。
- 特に、大きなデータブロックの移動や、関数呼び出しにおける引数の受け渡しなどで使用されます。
D_SI
(Source Index) およびD_DI
(Destination Index) レジスタ:- x86/x64アーキテクチャにおける汎用レジスタの一部です。
ESI
(Extended Source Index) およびEDI
(Extended Destination Index) レジスタは、特に文字列操作命令やメモリブロック操作命令(例:MOVSB
,STOSB
)において、ソースデータとデスティネーションデータのメモリアドレスを指すために慣習的に使用されます。- コンパイラがこれらのレジスタを特定の目的のために使用する場合、他の用途でこれらのレジスタが必要になった際に、その内容を保存・復元するオーバーヘッドが発生する可能性があります。
- Ullman Number (ウルマン数):
- コンパイラ最適化の分野で用いられる概念で、式ツリー(ASTの一部)を評価するために必要な最小レジスタ数を決定するために使用されます。
- Ullman数は、ツリーの各ノードに対して計算され、そのノードを評価するのに必要なレジスタの「深さ」を示します。
sgen
関数内でn->ullman >= ns->ullman
のような比較が行われていることから、このコミットがUllman数に基づいたレジスタ割り当て戦略を考慮していることがわかります。
技術的詳細
このコミットは、Goコンパイラのコード生成バックエンドにおけるレジスタ割り当てとコード生成の戦略を根本的に改善しています。
agenr
関数の変更 (src/cmd/5g/cgen.c
, src/cmd/6g/cgen.c
)
agenr
関数は、アドレスを計算し、その結果をレジスタに格納するコードを生成します。このコミットでは、agenr
が処理するASTノードのケースが拡張されました。
ODOT
,ODOTPTR
,OCALLFUNC
,OCALLMETH
,OCALLINTER
の追加:- これらのノードタイプは、構造体のフィールドアクセス、ポインタを介したフィールドアクセス、および各種関数呼び出しを表します。
- 以前は、これらの複雑な式のアドレスを計算する際に、レジスタの割り当てが早期に行われ、その後の処理でレジスタが不足したり、不必要な保存・復元が発生したりする可能性がありました。
- 新しいロジックでは、
igen
("Intermediate Generation" または "Indirect Generation" の略で、おそらく中間的なアドレス計算を行う関数)を呼び出して一時的なノードn1
を生成し、そのn1
に対してレジスタを割り当て(regalloc
)、アドレスを生成(agen
)し、最後にレジスタを解放(regfree
)しています。 - この変更により、レジスタの割り当てがより遅延され、必要な時に必要なレジスタが割り当てられるようになり、レジスタの利用効率が向上し、複雑な保存・復元シーケンスが回避されます。
OIND
の追加:- ポインタのデリファレンス(
*ptr
)を表すOIND
ノードもagenr
で処理されるようになりました。 cgenr(n->left, a, res)
を呼び出すことで、デリファレンスされるポインタのアドレスを計算し、その結果をa
に格納します。これにより、ポインタのデリファレンスも効率的にレジスタに割り当てられるようになります。
- ポインタのデリファレンス(
gsubr.c
の変更 (src/cmd/5g/gsubr.c
)
regalloc
関数はレジスタを割り当てる際に使用されます。このコミットでは、デバッグ目的でregalloc
が呼び出された際に、割り当てられているレジスタの情報を出力するprint("registers allocated at\n");
という行が追加されました。これは、レジスタ割り当ての問題をデバッグする際に役立つ情報を提供します。
sgen
関数の変更 (src/cmd/6g/cgen.c
)
sgen
関数は、主にメモリ間のデータ移動(構造体や配列のコピーなど)のためのコードを生成します。この関数における変更は、特にAMD64 (6g
) コンパイラにおけるレジスタの使用方法を最適化しています。
savex
/restx
の削除とagenr
の直接利用:- 以前の
sgen
では、D_SI
とD_DI
レジスタを使用する際に、その内容をsavex
で一時的に保存し、処理後にrestx
で復元するという複雑なシーケンスが用いられていました。これは、sgen
がD_SI
とD_DI
を特定の目的(ソースとデスティネーションのアドレス)に利用する際に、これらのレジスタが既に他の用途で使われている場合に競合を避けるためのものでした。 - このコミットでは、
savex
とrestx
の呼び出しが完全に削除され、代わりにagenr
関数が直接呼び出されています。 agenr(n, &nodr, N)
とagenr(ns, &nodl, N)
を呼び出すことで、ソースとデスティネーションのアドレスをそれぞれnodr
とnodl
に格納します。agenr
はレジスタ割り当てをより賢く行うため、不必要な保存・復元が回避されます。
- 以前の
D_SI
とD_DI
レジスタの明示的な利用:nodreg(&noddi, types[tptr], D_DI);
とnodreg(&nodsi, types[tptr], D_SI);
によって、D_DI
とD_SI
レジスタを表すノードを明示的に作成しています。- その後、
gmove(&nodl, &noddi);
とgmove(&nodr, &nodsi);
によって、計算されたソースとデスティネーションのアドレスをそれぞれD_DI
とD_SI
レジスタに移動させています。 - このアプローチにより、
sgen
はD_SI
とD_DI
を必要に応じて利用しつつ、レジスタの保存・復元のオーバーヘッドを最小限に抑えることができます。
regfree
の追加:regfree(&nodl);
とregfree(&nodr);
によって、nodl
とnodr
に割り当てられたレジスタを解放しています。これにより、レジスタが不要になった時点で速やかに解放され、他の用途に再利用できるようになります。
torture.go
の変更 (test/torture.go
)
torture.go
は、コンパイラの堅牢性をテストするためのGoのソースファイルです。
- ネストの深さの増加:
ChainAssertArrayIndex
とChainAssertArrayptrIndex
という関数において、コメントアウトされていたChildren[0].(*UArr).
やChildren[0].(*UArrPtr).
の行がコメント解除され、実際のコードとして有効になりました。- これにより、配列やポインタのチェインアクセスにおけるネストの深さが大幅に増加しました。
- この変更は、コンパイラが非常に深いネスト構造を正しく処理できるかどうかを検証するためのものです。以前のコンパイラでは、このような深いネストがクラッシュや誤ったコード生成の原因となっていた可能性があります。
- 特に
ChainAssertArrayptrIndex
のコメント// TODO: don't crash on longer chains.
が削除されていることから、このコミットによってこの問題が解決されたことが示唆されます。
これらの変更は、Goコンパイラがより複雑なGoコードパターンを効率的かつ正確にコンパイルできるようにするための重要な改善です。特にレジスタ割り当ての遅延と、特定のレジスタ(SI/DI)の利用方法の最適化が、コード生成の品質向上に寄与しています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は以下のファイルに集中しています。
src/cmd/5g/cgen.c
:agenr
関数にODOT
,ODOTPTR
,OCALLFUNC
,OCALLMETH
,OCALLINTER
,OIND
の各ASTノードタイプに対する新しいケースが追加されました。これにより、これらの操作のアドレス生成とレジスタ割り当てが改善されました。
src/cmd/5g/gsubr.c
:regalloc
関数にデバッグ用のprint
文が追加されました。
src/cmd/6g/cgen.c
:agenr
関数に5g
と同様の変更が加えられました。sgen
関数において、savex
とrestx
の呼び出しが削除され、agenr
の直接利用とD_SI
/D_DI
レジスタの明示的な割り当て・解放ロジックが導入されました。
test/torture.go
:ChainAssertArrayIndex
とChainAssertArrayptrIndex
関数内のネストされたフィールドアクセスがコメント解除され、テストの深さが増加しました。
コアとなるコードの解説
src/cmd/5g/cgen.c
および src/cmd/6g/cgen.c
の agenr
関数
switch(n->op) {
+ case ODOT:
+ case ODOTPTR:
+ case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
+ igen(n, &n1, res); // nから中間ノードn1を生成
+ regalloc(a, types[tptr], &n1); // n1にレジスタaを割り当て
+ agen(&n1, a); // n1のアドレスをaに生成
+ regfree(&n1); // n1のレジスタを解放
+ break;
+
+ case OIND:
+ cgenr(n->left, a, res); // ポインタの左辺(ポインタ自体)のアドレスをaに生成
+ break;
このコードは、agenr
関数が特定のASTノードタイプ(ODOT
, ODOTPTR
, OCALLFUNC
, OCALLMETH
, OCALLINTER
, OIND
)をどのように処理するかを示しています。
ODOT
などのケースでは、まずigen
を呼び出して、元のノードn
からアドレス計算のための中間ノードn1
を生成します。- 次に、
regalloc
でn1
にレジスタa
を割り当てます。これは、アドレス計算の結果を格納するためのレジスタです。 agen(&n1, a)
で、n1
が表すアドレスを実際にa
に生成します。- 最後に
regfree(&n1)
で、n1
に割り当てられたレジスタを解放します。このシーケンスにより、レジスタの割り当てがより遅延され、必要な期間だけレジスタが保持されるようになります。 OIND
(ポインタのデリファレンス)のケースでは、cgenr
を呼び出して、ポインタの左辺(ポインタ自体)のアドレスをa
に生成します。これにより、デリファレンスされた値のアドレスが効率的に取得されます。
src/cmd/6g/cgen.c
の sgen
関数
if(n->ullman >= ns->ullman) {
-\t\tsavex(D_SI, &nodr, &oldr, N, types[tptr]);
-\t\tagen(n, &nodr);
-\
-\t\tregalloc(&nodr, types[tptr], &nodr);\t// mark nodr as live
-\t\tsavex(D_DI, &nodl, &oldl, N, types[tptr]);
-\t\tagen(ns, &nodl);
-\t\tregfree(&nodr);
+\t\tagenr(n, &nodr, N);\n+\t\tagenr(ns, &nodl, N);
} else {
-\t\tsavex(D_DI, &nodl, &oldl, N, types[tptr]);
-\t\tagen(ns, &nodl);
-\
-\t\tregalloc(&nodl, types[tptr], &nodl);\t// mark nodl as live
-\t\tsavex(D_SI, &nodr, &oldr, N, types[tptr]);
-\t\tagen(n, &nodr);
-\t\tregfree(&nodl);
+\t\tagenr(ns, &nodl, N);\n+\t\tagenr(n, &nodr, N);
}
+\tnodreg(&noddi, types[tptr], D_DI);\n+\tnodreg(&nodsi, types[tptr], D_SI);\n+\tgmove(&nodl, &noddi);\n+\tgmove(&nodr, &nodsi);\n+\tregfree(&nodl);\n+\tregfree(&nodr);\n
この部分では、sgen
関数がソースn
とデスティネーションns
のUllman数を比較し、どちらを先に処理するかを決定しています。
- 以前のコードでは、
savex
とrestx
を使ってD_SI
とD_DI
レジスタの内容を一時的に保存・復元していました。これは、これらのレジスタがsgen
の処理中に他の目的で使われる可能性があったためです。 - 新しいコードでは、
savex
とrestx
の呼び出しが削除され、代わりにagenr
が直接呼び出されています。agenr
はレジスタ割り当てをより効率的に行うため、不必要な保存・復元が回避されます。 - その後、
nodreg
でD_DI
とD_SI
レジスタを表すノードを作成し、gmove
で計算されたアドレス(nodl
とnodr
)をこれらのレジスタに移動させています。 - 最後に
regfree
でnodl
とnodr
に割り当てられたレジスタを解放しています。この変更により、sgen
はD_SI
とD_DI
を必要に応じて利用しつつ、レジスタの保存・復元のオーバーヘッドを最小限に抑えることができます。
test/torture.go
func ChainAssertArrayIndex(u *UArr) J {
return u.
Children[0].(*UArr).
Children[0].(*UArr).
Children[0].(*UArr).
Children[0].(*UArr).
Children[0].(*UArr).
Children[0].(*UArr).
Children[0]
}
func ChainAssertArrayptrIndex(u *UArrPtr) J {
-\t// TODO: don\'t crash on longer chains.\n \treturn u.\n \t\tChildren[0].(*UArrPtr).\n \t\tChildren[0].(*UArrPtr).\n \t\tChildren[0].(*UArrPtr).\n \t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n+\t\tChildren[0].(*UArrPtr).\n \t\tChildren[0]\n }
このテストコードの変更は、Children[0].(*UArr).
やChildren[0].(*UArrPtr).
のようなネストされたフィールドアクセスをさらに深くすることで、コンパイラが非常に長いチェインアクセスを正しく処理できるかを検証しています。特にChainAssertArrayptrIndex
のTODO
コメントが削除されていることから、このコミットによって以前のクラッシュ問題が解決されたことが示唆されます。
関連リンク
- Go言語のコンパイラ設計に関する一般的な情報: https://go.dev/doc/articles/go-compiler-front-end (Goコンパイラのフロントエンドに関する記事ですが、ASTなどの概念理解に役立ちます)
- Go言語のコンパイラソースコード: https://github.com/golang/go/tree/master/src/cmd (Goコンパイラのソースコードリポジトリ)
参考にした情報源リンク
- GitHubコミットページ: https://github.com/golang/go/commit/eb4f4d16ae6333171c14f8da304f2bfa2829e4ff
- Go CL (Change List) 6817080: https://golang.org/cl/6817080
- 「Fixes #4207」に関するWeb検索結果: Goプロジェクトの公開Issueトラッカーでは、このコミットと直接関連する「Issue #4207」は見つかりませんでした。この番号は、Go以外の様々なソフトウェアやシステムで異なる意味を持つ一般的なエラーコードやIssue番号として報告されています。