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

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

このコミットは、Go言語のコンパイラ(cmd/gc)におけるデータ競合検出器(Race Detector)のインストゥルメンテーションに関するバグ修正と機能強化を目的としています。具体的には、append 関数と type switch 文におけるデータ競合の検出が正しく行われるように、コンパイラのコードウォークロジックが修正されました。これにより、これらのGo言語の構文を使用する際に発生しうる潜在的なデータ競合を見逃すリスクが低減されます。

コミット

commit 7c79910cb9abbac2f525c2b2ecad188ff27eb8af
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Wed Mar 27 20:33:17 2013 +0100

    cmd/gc: fix race instrumentation of append and type switches.
    
    The remaining issues are about runtime and sync package
    instrumentation.
    
    Update #4228
    
    R=dvyukov, bradfitz
    CC=golang-dev
    https://golang.org/cl/8041043
---
 src/cmd/gc/racewalk.c                       | 25 ++++++++--------
 src/pkg/runtime/race/testdata/mop_test.go   | 46 +++++++++++++++++++++++++++++
 src/pkg/runtime/race/testdata/slice_test.go |  3 +-\
 3 files changed, 59 insertions(+), 15 deletions(-)

diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c
index fee5cf4226..5d4f62e761 100644
--- a/src/cmd/gc/racewalk.c
+++ b/src/cmd/gc/racewalk.c
@@ -133,7 +133,6 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
 	case OASOP:
 	case OAS:
 	case OAS2:
-\tcase OAS2DOTTYPE:
 	case OAS2RECV:
 	case OAS2FUNC:
 	case OAS2MAPR:
@@ -186,12 +185,6 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
 \t\tracewalknode(&n->left, init, 0, 0);\n \t\tgoto ret;\n \n-\tcase OSWITCH:\n-\t\tif(n->ntest->op == OTYPESW)\n-\t\t\t// TODO(dvyukov): the expression can contain calls or reads.\n-\t\t\treturn;\n-\t\tgoto ret;\n-\n \tcase ONOT:\n \tcase OMINUS:\n \tcase OPLUS:\
@@ -317,6 +310,10 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)\
 \t\tracewalknode(&n->left, init, 0, 0);\n \t\tgoto ret;\n \n+\tcase OTYPESW:\n+\t\tracewalknode(&n->right, init, 0, 0);\n+\t\tgoto ret;\n+\n \t// should not appear in AST by now\n \tcase OSEND:\n \tcase ORECV:\
@@ -334,6 +331,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)\
 \tcase OMAKESLICE:\
 \tcase OCALL:\
 \tcase OCOPY:\
+\tcase OAPPEND:\
 \tcase ORUNESTR:\
 \tcase OARRAYBYTESTR:\
 \tcase OARRAYRUNESTR:\
@@ -344,6 +342,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)\
 \tcase OADDSTR:\
 \tcase ODOTTYPE:\
 \tcase ODOTTYPE2:\
+\tcase OAS2DOTTYPE:\
 \tcase OCALLPART: // lowered to PTRLIT\
 \tcase OCLOSURE:  // lowered to PTRLIT\
 \tcase ORANGE:    // lowered to ordinary for loop\
@@ -364,6 +363,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)\
 \tcase OIF:\
 \tcase OCALLMETH:\
 \tcase ORETURN:\
+\tcase OSWITCH:\
 \tcase OSELECT:\
 \tcase OEMPTY:\
 \tcase OBREAK:\
@@ -389,10 +389,6 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)\
 \tcase OLITERAL:\
 \tcase OSLICESTR:  // always preceded by bounds checking, avoid double instrumentation.\
 \t\tgoto ret;\n-\n-\t// unimplemented\n-\tcase OAPPEND:\
-\t\tgoto ret;\n \t}\n \n ret:\
@@ -448,6 +444,7 @@ callinstr(Node **np, NodeList **init, int wr, int skip)\
 \tif(isartificial(n))\
 \t\treturn 0;\
 \tif(t->etype == TSTRUCT) {\
+\t\t// TODO: instrument arrays similarly.\
 \t\t// PARAMs w/o PHEAP are not interesting.\
 \t\tif(n->class == PPARAM || n->class == PPARAMOUT)\
 \t\t\treturn 0;\
@@ -484,7 +481,7 @@ callinstr(Node **np, NodeList **init, int wr, int skip)\
 \t// that has got a pointer inside. Whether it points to\
 \t// the heap or not is impossible to know at compile time\
 \tif((class&PHEAP) || class == PPARAMREF || class == PEXTERN\
-\t\t|| b->type->etype == TARRAY || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) {\
+\t\t|| b->op == OINDEX || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) {\
 \t\thascalls = 0;\
 \t\tforeach(n, hascallspred, &hascalls);\
 \t\tif(hascalls) {\
@@ -510,6 +507,8 @@ uintptraddr(Node *n)\
 \treturn r;\
 }\n \n+// basenod returns the simplest child node of n pointing to the same\n+// memory area.\n static Node*\n basenod(Node *n)\
 {\n@@ -518,7 +517,7 @@ basenod(Node *n)\
 \t\t\tn = n->left;\
 \t\t\tcontinue;\
 \t\t}\n-\t\tif(n->op == OINDEX) {\
+\t\tif(n->op == OINDEX && isfixedarray(n->type)) {\
 \t\t\tn = n->left;\
 \t\t\tcontinue;\
 \t\t}\ndiff --git a/src/pkg/runtime/race/testdata/mop_test.go b/src/pkg/runtime/race/testdata/mop_test.go
index 1a7ed96249..ae70cbb5f8 100644
--- a/src/pkg/runtime/race/testdata/mop_test.go
+++ b/src/pkg/runtime/race/testdata/mop_test.go
@@ -227,6 +227,37 @@ func TestRaceCaseFallthrough(t *testing.T) {\
 \t<-ch\n }\n \n+func TestRaceCaseType(t *testing.T) {\n+\tvar x, y int\n+\tvar i interface{} = x\n+\tc := make(chan int, 1)\n+\tgo func() {\n+\t\tswitch i.(type) {\n+\t\tcase nil:\n+\t\tcase int:\n+\t\t}\n+\t\tc <- 1\n+\t}()\n+\ti = y\n+\t<-c\n+}\n+\n+func TestRaceCaseTypeBody(t *testing.T) {\n+\tvar x, y int\n+\tvar i interface{} = &x\n+\tc := make(chan int, 1)\n+\tgo func() {\n+\t\tswitch i := i.(type) {\n+\t\tcase nil:\n+\t\tcase *int:\n+\t\t\t*i = y\n+\t\t}\n+\t\tc <- 1\n+\t}()\n+\tx = y\n+\t<-c\n+}\n+\n func TestNoRaceRange(t *testing.T) {\
 \tch := make(chan int, 3)\
 \ta := [...]int{1, 2, 3}\
 \n@@ -1446,6 +1477,21 @@ func TestRaceFailingSliceStruct(t *testing.T) {\
 \t<-c\n }\n \n+func TestRaceAppendSliceStruct(t *testing.T) {\n+\ttype X struct {\n+\t\tx, y int\n+\t}\n+\tc := make(chan bool, 1)\n+\tx := make([]X, 10)\n+\tgo func() {\n+\t\ty := make([]X, 0, 10)\n+\t\ty = append(y, x...)\n+\t\tc <- true\n+\t}()\n+\tx[1].y = 42\n+\t<-c\n+}\n+\n func TestRaceStructInd(t *testing.T) {\
 \tc := make(chan bool, 1)\
 \ttype Item struct {\ndiff --git a/src/pkg/runtime/race/testdata/slice_test.go b/src/pkg/runtime/race/testdata/slice_test.go
index 1fe051b121..c85df5e3d6 100644
--- a/src/pkg/runtime/race/testdata/slice_test.go
+++ b/src/pkg/runtime/race/testdata/slice_test.go
@@ -338,8 +338,7 @@ func TestRaceSliceVarCopy2(t *testing.T) {\
 \t<-c\n }\n \n-// Not implemented.\n-func TestRaceFailingSliceAppend(t *testing.T) {\
+func TestRaceSliceAppend(t *testing.T) {\
 \tc := make(chan bool, 1)\
 \ts := make([]int, 10, 20)\
 \tgo func() {\

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

https://github.com/golang/go/commit/7c79910cb9abbac2f525c2b2ecad188ff27eb8af

元コミット内容

cmd/gc: fix race instrumentation of append and type switches.

The remaining issues are about runtime and sync package
instrumentation.

Update #4228

R=dvyukov, bradfitz
CC=golang-dev
https://golang.org/cl/8041043

変更の背景

Go言語は並行処理を強力にサポートしていますが、並行処理を行う上で最も厄介な問題の一つが「データ競合(Data Race)」です。データ競合とは、複数のゴルーチンが同時に同じメモリ領域にアクセスし、少なくとも一方のアクセスが書き込みであり、かつそれらのアクセスが同期メカニズムによって保護されていない場合に発生します。データ競合は予測不能なプログラムの振る舞いやバグ(例: クラッシュ、不正なデータ、デッドロック)を引き起こす可能性があり、その特定とデバッグは非常に困難です。

Go言語には、このようなデータ競合をコンパイル時または実行時に検出するための「Race Detector」が組み込まれています。これは、プログラムの実行中にメモリアクセスを監視し、競合のパターンを検出すると警告を発するツールです。Race Detectorは、コンパイラが生成するコードに特別なインストゥルメンテーション(計測コード)を挿入することで機能します。

しかし、このコミット以前のRace Detectorの実装では、特定のGo言語の構文、特に append 関数と type switch 文において、インストゥルメンテーションが不完全であったり、全く行われていなかったりする問題がありました。これにより、これらの構文内で発生するデータ競合を見逃してしまう可能性がありました。

この問題は、GoのIssueトラッカーで #4228 として報告されていました。append はスライスの容量が不足した場合に新しい基底配列を割り当てて要素をコピーするため、内部的にメモリの読み書きが発生します。type switch はインターフェースの値の型を動的にチェックし、それに応じて異なるコードパスを実行するため、インターフェースの基底値へのアクセスが発生します。これらの操作が並行して行われる場合、データ競合が発生する可能性があり、Race Detectorがそれを検出できないことは大きな欠陥でした。

このコミットは、これらの特定のケースにおけるRace Detectorの精度と網羅性を向上させ、より堅牢な並行プログラムのデバッグを可能にすることを目的としています。

前提知識の解説

データ競合 (Data Race)

データ競合は、並行プログラミングにおける深刻なバグの一種です。以下の3つの条件がすべて満たされたときに発生します。

  1. 複数のゴルーチンが同じメモリ領域にアクセスする: 2つ以上のゴルーチンが、同じ変数やデータ構造を操作しようとします。
  2. 少なくとも一方のアクセスが書き込みである: アクセスのうち少なくとも1つが、メモリの内容を変更する操作(書き込み)である必要があります。両方が読み込みの場合は競合とはみなされません。
  3. アクセスが同期メカニズムによって保護されていない: ミューテックス、チャネル、アトミック操作などの同期プリミティブが適切に使用されていないため、アクセス順序が保証されません。

データ競合が発生すると、プログラムの実行結果がアクセス順序に依存するようになり、非決定的な振る舞いを引き起こします。これは、デバッグが非常に困難な「Heisenbug」(観測しようとすると消えるバグ)の典型例です。

Go Race Detector

Go言語に組み込まれているRace Detectorは、データ競合を検出するための強力なツールです。Goプログラムを -race フラグ付きでコンパイルすると、コンパイラはメモリアクセスを監視するための追加のコード(インストゥルメンテーション)を生成します。実行時に、このインストゥルメンテーションコードがメモリの読み書きを記録し、競合パターン(例: あるゴルーチンが書き込み、別のゴルーチンが読み書き)を検出すると、詳細なレポート(スタックトレース、アクセスされたメモリ位置など)を出力します。

Race Detectorは、コンパイル時にAST(抽象構文木)をウォークし、メモリアクセスが発生する可能性のある箇所に計測コードを挿入します。このコミットの racewalk.c は、そのインストゥルメンテーションロジックの中核を担うファイルです。

append 関数

append はGoのスライスを操作するための組み込み関数です。スライスに要素を追加するために使用されます。append の重要な特性は、追加後のスライスの容量が元のスライスの容量を超える場合、Goランタイムがより大きな新しい基底配列を割り当て、元の要素を新しい配列にコピーし、その後新しい要素を追加するという動作です。

この「新しい配列の割り当てとコピー」のプロセスは、内部的にメモリの読み書きを伴います。もし複数のゴルーチンが同時に同じスライスに対して append を実行したり、append が行われているスライスの基底配列に他のゴルーチンがアクセスしたりすると、データ競合が発生する可能性があります。

type switch

type switch は、インターフェース変数が保持する具体的な型に基づいて異なるコードブロックを実行するためのGoの制御構造です。

var i interface{} = someValue

switch v := i.(type) {
case int:
    // i は int 型
case string:
    // i は string 型
default:
    // その他の型
}

type switch の内部では、インターフェースの値(型情報と基底値)へのアクセスが発生します。もしインターフェース変数が複数のゴルーチン間で共有され、type switch が実行されている間に別のゴルーチンがそのインターフェース変数を変更すると、データ競合が発生する可能性があります。

Goコンパイラ (cmd/gc) と AST (Abstract Syntax Tree)

Goコンパイラ(cmd/gc)は、Goのソースコードを機械語に変換する主要なツールチェーンの一部です。コンパイルプロセスでは、ソースコードはまずAST(抽象構文木)にパースされます。ASTはプログラムの構造を木構造で表現したもので、各ノードはプログラムの要素(変数宣言、関数呼び出し、演算子、制御構造など)を表します。

src/cmd/gc/racewalk.c ファイルは、このASTをトラバース(ウォーク)し、データ競合検出に必要なインストゥルメンテーションコードを挿入する役割を担っています。ASTノードには、OASOP(代入演算子)、OTYPESW(type switch)、OAPPEND(append関数呼び出し)、OINDEX(配列/スライスインデックスアクセス)など、様々な種類があります。racewalknode 関数は、これらのノードの種類に応じて異なる処理を行い、必要に応じてメモリアクセス監視のための関数呼び出しを挿入します。

技術的詳細

このコミットの技術的な核心は、src/cmd/gc/racewalk.c ファイルにおけるASTウォークロジックの変更にあります。以前は appendtype switch がRace Detectorのインストゥルメンテーションの対象外であったり、不適切に処理されていたりしたため、それらを正しく検出できるように修正が加えられました。

  1. type switch のインストゥルメンテーションの修正:

    • 以前の racewalknode 関数では、OSWITCH ノード(一般的な switch 文)の処理において、もしそれが OTYPESW(type switch)であれば、インストゥルメンテーションをスキップする return 文がありました。これは「TODO(dvyukov): the expression can contain calls or reads.」というコメントと共に、未実装の課題として残されていました。
    • このコミットでは、この OSWITCH の特殊なスキップロジックが削除されました。
    • 代わりに、OTYPESW ノードが racewalknode の別の case として明示的に追加され、racewalknode(&n->right, init, 0, 0); を通じて type switch の右辺(インターフェース変数)がウォークされるようになりました。これにより、type switch の評価時に発生するインターフェース値へのアクセスがRace Detectorの監視対象となります。
    • また、OAS2DOTTYPE(type assertion を伴う多重代入)も、以前は OASOP などの代入演算子群から除外されていましたが、新たにインストゥルメンテーション対象のノードリストに追加されました。これは、type switchv := i.(type) のような構文が内部的に OAS2DOTTYPE に変換される場合があるため、その代入操作も監視する必要があるためです。
  2. append 関数のインストゥルメンテーションの有効化:

    • 以前の racewalknode 関数には、OAPPEND ノードに対して「// unimplemented」というコメントと共に goto ret; があり、インストゥルメンテーションがスキップされていました。
    • このコミットでは、この OAPPEND のスキップロジックが削除され、OAPPENDOMAKESLICE, OCALL, OCOPY などと同様に、通常のインストゥルメンテーション対象のノードとして扱われるようになりました。これにより、append 関数が内部的に行うメモリの読み書き(特にスライスの再割り当てと要素のコピー)がRace Detectorによって監視されるようになります。
  3. basenod 関数の修正:

    • basenod 関数は、与えられたノードが指すメモリ領域の「基底」となるノード(例: a[i]a)を特定するために使用されます。これは、Race Detectorがメモリ競合を正確に報告するために、アクセスされたメモリの「どこ」が競合しているのかを特定する上で重要です。
    • 以前の basenod では、OINDEX(配列/スライスインデックスアクセス)の場合に無条件に n->left(基底配列/スライス)を返していました。
    • このコミットでは、if(n->op == OINDEX && isfixedarray(n->type)) という条件が追加されました。これは、OINDEX が固定長配列([N]T)へのアクセスである場合にのみ n->left を返すように変更されたことを意味します。スライス([]T)は動的なサイズを持つため、その基底配列は実行時に変更される可能性があり、固定長配列とは異なる扱いが必要です。この変更により、Race Detectorがスライス操作におけるメモリの基底をより正確に追跡できるようになります。
  4. callinstr 関数の修正:

    • callinstr 関数は、特定のノードに対してインストゥルメンテーションが必要かどうかを判断するロジックを含んでいます。
    • b->type->etype == TARRAY という条件が b->op == OINDEX に変更されました。これは、配列型そのものよりも、配列へのインデックスアクセス操作(OINDEX)を直接チェックする方が、インストゥルメンテーションの対象をより正確に絞り込むことができるためと考えられます。

これらの変更により、GoのRace Detectorは appendtype switch を含むより広範なGoの構文におけるデータ競合を検出できるようになり、並行プログラムの信頼性が向上します。

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

src/cmd/gc/racewalk.c における主要な変更箇所を抜粋します。

--- a/src/cmd/gc/racewalk.c
+++ b/src/cmd/gc/racewalk.c
@@ -133,7 +133,6 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
 	case OASOP:
 	case OAS:
 	case OAS2:
-\tcase OAS2DOTTYPE:
 	case OAS2RECV:
 	case OAS2FUNC:
 	case OAS2MAPR:
@@ -186,12 +185,6 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
 \t\tracewalknode(&n->left, init, 0, 0);\n \t\tgoto ret;\n \n-\tcase OSWITCH:\
-\t\tif(n->ntest->op == OTYPESW)\
-\t\t\t// TODO(dvyukov): the expression can contain calls or reads.\
-\t\t\treturn;\
-\t\tgoto ret;\
-\n \tcase ONOT:\
 \tcase OMINUS:\
 \tcase OPLUS:\
@@ -317,6 +310,10 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)\
 \t\tracewalknode(&n->left, init, 0, 0);\n \t\tgoto ret;\n \n+\tcase OTYPESW:\
+\t\tracewalknode(&n->right, init, 0, 0);\n+\t\tgoto ret;\
+\n \t// should not appear in AST by now\
 \tcase OSEND:\
 \tcase ORECV:\
@@ -334,6 +331,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)\
 \tcase OMAKESLICE:\
 \tcase OCALL:\
 \tcase OCOPY:\
+\tcase OAPPEND:\
 \tcase ORUNESTR:\
 \tcase OARRAYBYTESTR:\
 \tcase OARRAYRUNESTR:\
@@ -344,6 +342,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)\
 \tcase OADDSTR:\
 \tcase ODOTTYPE:\
 \tcase ODOTTYPE2:\
+\tcase OAS2DOTTYPE:\
 \tcase OCALLPART: // lowered to PTRLIT\
 \tcase OCLOSURE:  // lowered to PTRLIT\
 \tcase ORANGE:    // lowered to ordinary for loop\
@@ -364,6 +363,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)\
 \tcase OIF:\
 \tcase OCALLMETH:\
 \tcase ORETURN:\
+\tcase OSWITCH:\
 \tcase OSELECT:\
 \tcase OEMPTY:\
 \tcase OBREAK:\
@@ -389,10 +389,6 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)\
 \tcase OLITERAL:\
 \tcase OSLICESTR:  // always preceded by bounds checking, avoid double instrumentation.\
 \t\tgoto ret;\n-\n-\t// unimplemented\
-\tcase OAPPEND:\
-\t\tgoto ret;\
 \t}\
 \n ret:\
@@ -484,7 +481,7 @@ callinstr(Node **np, NodeList **init, int wr, int skip)\
 \t// that has got a pointer inside. Whether it points to\
 \t// the heap or not is impossible to know at compile time\
 \tif((class&PHEAP) || class == PPARAMREF || class == PEXTERN\
-\t\t|| b->type->etype == TARRAY || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) {\
+\t\t|| b->op == OINDEX || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) {\
 \t\thascalls = 0;\
 \t\tforeach(n, hascallspred, &hascalls);\
 \t\tif(hascalls) {\
@@ -518,7 +517,7 @@ basenod(Node *n)\
 \t\t\tn = n->left;\
 \t\t\tcontinue;\
 \t\t}\n-\t\tif(n->op == OINDEX) {\
+\t\tif(n->op == OINDEX && isfixedarray(n->type)) {\
 \t\t\tn = n->left;\
 \t\t\tcontinue;\
 \t\t}\

コアとなるコードの解説

上記の差分は、src/cmd/gc/racewalk.c 内の racewalknodecallinstrbasenod 関数の変更を示しています。

  1. racewalknode 関数の変更:

    • - case OAS2DOTTYPE: の削除:
      • 以前は OASOP, OAS, OAS2 などの代入演算子群の中に OAS2DOTTYPE(型アサーションを伴う多重代入)が含まれていましたが、ここから削除されました。これは、OAS2DOTTYPE の処理が後述の別の場所に移されたためです。
    • OSWITCH の特殊処理の削除:
      • 以前は OSWITCH ノード(一般的な switch 文)が OTYPESW(type switch)である場合に、インストゥルメンテーションをスキップする return; がありました。このコミットでは、この OSWITCH の特殊な処理が完全に削除されました。これにより、type switch がインストゥルメンテーションの対象となる道が開かれました。
    • + case OTYPESW: の追加:
      • 新たに OTYPESW ノードが明示的に racewalknodecase として追加されました。
      • racewalknode(&n->right, init, 0, 0); は、type switch の右辺(通常はインターフェース変数)に対して再帰的に racewalknode を呼び出し、その中のメモリアクセスもインストゥルメンテーションの対象とします。
    • + case OAPPEND: の追加:
      • 以前は OAPPEND ノード(append 関数呼び出し)は「// unimplemented」とコメントされ、goto ret; によってインストゥルメンテーションがスキップされていました。
      • このコミットでは、OAPPENDOMAKESLICE, OCALL, OCOPY などと同様に、通常のインストゥルメンテーション対象のノードリストに追加されました。これにより、append が内部的に行うメモリ操作がRace Detectorによって監視されるようになります。
    • + case OAS2DOTTYPE: の再追加:
      • OAS2DOTTYPEODOTTYPE, ODOTTYPE2 などと共に、別のインストゥルメンテーション対象ノードのグループに再追加されました。これは、type switchv := i.(type) のような構文が内部的に OAS2DOTTYPE に変換される場合があり、その代入操作も監視する必要があるためです。
    • + case OSWITCH: の再追加:
      • OSWITCHOIF, OCALLMETH, ORETURN などと共に、別のインストゥルメンテーション対象ノードのグループに再追加されました。これにより、一般的な switch 文も適切にウォークされ、その中のメモリアクセスが監視されるようになります。
    • - case OAPPEND: の削除:
      • 以前の「// unimplemented」とコメントされていた OAPPEND のスキップロジックが削除されました。
  2. callinstr 関数の変更:

    • - || b->type->etype == TARRAY+ || b->op == OINDEX に変更:
      • callinstr 関数は、特定のノードに対してインストゥルメンテーションが必要かどうかを判断します。
      • 以前は b が配列型(TARRAY)であるかどうかをチェックしていましたが、これを bOINDEX(配列/スライスインデックスアクセス)操作であるかどうかをチェックするように変更しました。これは、配列型そのものよりも、実際に配列へのアクセスが行われる操作を直接監視する方が、より正確なインストゥルメンテーションにつながるためです。
  3. basenod 関数の変更:

    • - if(n->op == OINDEX) {+ if(n->op == OINDEX && isfixedarray(n->type)) { に変更:
      • basenod 関数は、与えられたノードが指すメモリ領域の「基底」となるノードを特定します。
      • 以前は OINDEX(配列/スライスインデックスアクセス)の場合に無条件に n->left(基底配列/スライス)を返していました。
      • この変更により、OINDEX が固定長配列(isfixedarray(n->type))へのアクセスである場合にのみ n->left を返すようになりました。スライスは動的なサイズを持つため、その基底配列は実行時に変更される可能性があり、固定長配列とは異なる扱いが必要です。この修正により、Race Detectorがスライス操作におけるメモリの基底をより正確に追跡できるようになります。

これらの変更は、Goコンパイラが生成するRace Detectorのインストゥルメンテーションコードの精度と網羅性を大幅に向上させ、appendtype switch といったGo言語の重要な構文におけるデータ競合の検出能力を高めます。

関連リンク

参考にした情報源リンク

  • Go言語公式ドキュメント - The Go Race Detector: https://go.dev/doc/articles/race_detector
  • Go言語公式ドキュメント - Slices: usage and internals: https://go.dev/blog/slices
  • Go言語公式ドキュメント - The Go Programming Language Specification - Type switches: https://go.dev/ref/spec#Type_switches
  • Goコンパイラの内部構造に関する一般的な情報源:
    • Goのソースコード(src/cmd/gc/ ディレクトリ)
    • Goのコンパイラに関するブログ記事やプレゼンテーション(例: "Go's Compiler: The Details" by Russ Cox)
    • GoのASTノードに関する情報(Goのソースコード内の src/cmd/compile/internal/syntax/nodes.gosrc/cmd/compile/internal/ir/ir.go など)