[インデックス 19553] ファイルの概要
このコミットは、以前のコミット (CL 93380044) の部分的な取り消しであり、特に go:nosplit
アノテーションに関連する変更を元に戻すものです。この取り消しは、go:nosplit
アノテーションが Windows ビルダーで問題を引き起こしていたため実施されました。元のコミットはGo言語での文字列操作の実装と、将来の変換のために go:nosplit
アノテーションを導入することを目的としていました。
コミット
commit ee8e687874b56f7c3ab1dd5585390b84dc74b14e
Author: Keith Randall <khr@golang.org>
Date: Mon Jun 16 23:51:18 2014 -0700
undo CL 93380044 / 7f0999348917
Partial undo, just of go:nosplit annotation. Somehow it
is breaking the windows builders.
TBR=bradfitz
««« original CL description
runtime: implement string ops in Go
Also implement go:nosplit annotation. Not really needed
for now, but we'll definitely need it for other conversions.
benchmark old ns/op new ns/op delta
BenchmarkRuneIterate 534 474 -11.24%
BenchmarkRuneIterate2 535 470 -12.15%
LGTM=bradfitz
R=golang-codereviews, dave, bradfitz, minux
CC=golang-codereviews
https://golang.org/cl/93380044
»»»
TBR=bradfitz
CC=golang-codereviews
https://golang.org/cl/105260044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ee8e687874b56f7c3ab1dd5585390b84dc74b14e
元コミット内容
undo CL 93380044 / 7f0999348917
Partial undo, just of go:nosplit annotation. Somehow it
is breaking the windows builders.
TBR=bradfitz
««« original CL description
runtime: implement string ops in Go
Also implement go:nosplit annotation. Not really needed
for now, but we'll definitely need it for other conversions.
benchmark old ns/op new ns/op delta
BenchmarkRuneIterate 534 474 -11.24%
BenchmarkRuneIterate2 535 470 -12.15%
LGTM=bradfitz
R=golang-codereviews, dave, bradfitz, minux
CC=golang-codereviews
https://golang.org/cl/93380044
»»»
TBR=bradfitz
CC=golang-codereviews
https://golang.org/cl/105260044
変更の背景
このコミットは、以前のコミット (CL 93380044) で導入された go:nosplit
アノテーションが、Go の Windows ビルドシステムで問題を引き起こしていたために行われました。元のコミットでは、Go ランタイムにおける文字列操作をGo言語で実装する一環として go:nosplit
アノテーションが導入されました。このアノテーションは、その時点では必須ではなかったものの、将来的な他の変換で必要になると見込まれていました。しかし、Windows 環境でのビルドが失敗するという予期せぬ副作用が発生したため、問題の原因となっている go:nosplit
の実装のみを部分的に元に戻すことが決定されました。これは、機能の安定性よりもビルドの健全性を優先した一時的な措置と考えられます。
前提知識の解説
Goコンパイラ (gc
)
Go言語の公式コンパイラは gc
と呼ばれ、src/cmd/gc
ディレクトリにそのソースコードがあります。Goのソースコードを機械語に変換する役割を担っており、字句解析、構文解析、型チェック、中間コード生成、最適化、最終的なアセンブリコード生成といった一連のフェーズを実行します。このコミットで変更されているファイル (fmt.c
, go.h
, go.y
, lex.c
, pgen.c
, y.tab.c
) は、コンパイラのフロントエンド(字句解析器、パーサー、コード生成の前処理)に属しており、コンパイラが go:nosplit
のようなディレクティブをどのように解釈し、処理していたかを示しています。
go:nosplit
プラグマ (Pragma)
go:nosplit
は、Goコンパイラに対する特殊なディレクティブ(プラグマ)の一つです。Goランタイムは、ゴルーチン(goroutine)のスタックを必要に応じて動的に拡張する「スタック分割(stack splitting)」というメカニズムを持っています。これにより、小さなスタックでゴルーチンを開始し、必要に応じて拡張することでメモリ効率を高めています。
しかし、一部の低レベルなランタイム関数、特にスタックを直接操作する関数や、スケジューラ、ガベージコレクションといったクリティカルなパスで実行される関数では、スタック分割が発生すると問題が生じる可能性があります。go:nosplit
プラグマは、コンパイラに対して、その関数がスタック分割を行わないように指示します。これにより、関数が実行中にスタックを拡張しようとせず、常に現在のスタックフレーム内で完結することが保証されます。これは、ランタイムの安定性やパフォーマンスにとって非常に重要です。
Goランタイム
Goランタイムは、Goプログラムの実行を管理するシステムです。ゴルーチンのスケジューリング、メモリ管理(ガベージコレクションを含む)、チャネル通信、システムコールインターフェースなど、Go言語の並行処理モデルと効率的な実行を支える多くの低レベルな機能を提供します。go:nosplit
のようなプラグマは、このランタイムの内部動作と密接に関連しています。
CL (Change List)
Go言語の開発では、Gerrit というコードレビューシステムが使用されており、変更の単位は「Change List (CL)」と呼ばれます。各CLは、一連のコミットから構成されることがあり、特定の機能追加、バグ修正、リファクタリングなどを表します。コミットメッセージに記載されている CL 93380044
や CL 105260044
は、Gerrit 上の特定の変更セットを指します。
Windows ビルダー
Goプロジェクトは、様々なオペレーティングシステムやアーキテクチャ向けに自動化されたビルドおよびテストシステム(通称「ビルダー」)を運用しています。これにより、コード変更が異なる環境で正しく動作するかどうかを継続的に検証しています。このコミットの背景にある「Windows ビルダーが壊れている」という問題は、GoのコードベースがWindows環境でコンパイルまたは実行する際に、go:nosplit
の実装が何らかの互換性問題やバグを引き起こしていたことを意味します。
技術的詳細
このコミットの技術的詳細は、Goコンパイラが go:nosplit
プラグマをどのように認識し、処理し、そして最終的に生成されるコードにどのように影響を与えていたか、そしてその機能がどのように削除されたかに集約されます。
-
プラグマの認識と解析:
src/cmd/gc/lex.c
はコンパイラの字句解析器であり、ソースコードをトークンに分割します。元の実装では、go:nosplit
という文字列を特別なプラグマとして認識し、内部的なnosplit
フラグをセットしていました。src/cmd/gc/go.y
はコンパイラの構文解析器(Yacc/Bisonによって生成される)の定義ファイルです。ここでは、関数宣言を解析する際に、字句解析器によってセットされたnosplit
フラグを、抽象構文木(AST)のノード(Node
構造体)に伝播させていました。
-
内部表現とフラグの管理:
src/cmd/gc/go.h
はコンパイラの内部データ構造の定義を含んでいます。Node
構造体には、関数がnosplit
であるかどうかを示すnosplit
フィールドが追加されていました。また、グローバルなnosplit
変数も存在し、プラグマの認識状態を保持していました。
-
コード生成への影響:
src/cmd/gc/pgen.c
は、ASTから最終的なアセンブリコードを生成する前段階の処理(プロローグ生成など)を担当します。元の実装では、関数のnosplit
フラグがセットされている場合、生成されるテキストセクション(関数本体)にNOSPLIT
という特別なフラグを設定していました。このNOSPLIT
フラグは、アセンブラやリンカ、あるいはランタイムによって解釈され、その関数がスタック分割を行わないようにするための指示として機能します。
このコミットは、これらのメカニズムをすべて逆転させ、go:nosplit
プラグマの認識、内部表現、およびコード生成への影響を完全に削除しています。これにより、Windows ビルダーで発生していた問題が解消されることを期待しています。この変更は、go:nosplit
の概念自体が不要になったわけではなく、その特定の実装が問題を引き起こしたための一時的なロールバックである可能性が高いです。
コアとなるコードの変更箇所
このコミットでは、以下のファイルが変更され、go:nosplit
アノテーションに関連するコードが削除されています。
-
src/cmd/gc/fmt.c
:--- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -649,7 +649,7 @@ typefmt(Fmt *fp, Type *t) if(t->funarg) { fmtstrcpy(fp, "("); - if(fmtmode == FTypeId || fmtmode == FErr) { // no argument names on function signature, and no "noescape"/"nosplit" tags + if(fmtmode == FTypeId || fmtmode == FErr) { // no argument names on function signature, and no "noescape" tags for(t1=t->type; t1!=T; t1=t1->down) if(t1->down) fmtprint(fp, "%hT, ", t1);
コメントから
"nosplit"
が削除されました。 -
src/cmd/gc/go.h
:--- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -269,7 +269,6 @@ struct Node uchar colas; // OAS resulting from := uchar diag; // already printed error about this uchar noescape; // func arguments do not escape - uchar nosplit; // func should not execute on separate stack uchar builtin; // built-in name, like len or close uchar walkdef; uchar typecheck; @@ -981,7 +980,6 @@ EXTERN char*\tflag_installsuffix; EXTERN int flag_race; EXTERN int flag_largemodel; EXTERN int noescape; -EXTERN int nosplit; EXTERN int debuglive; EXTERN Link* ctxt;
Node
構造体からnosplit
フィールドが、そしてグローバル変数nosplit
の宣言が削除されました。 -
src/cmd/gc/go.y
:--- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -1311,7 +1311,6 @@ xfndcl: $$->nbody = $3; $$->endlineno = lineno; $$->noescape = noescape; - $$->nosplit = nosplit; funcbody($$); } @@ -1496,7 +1495,6 @@ xdcl_list: testdclstack(); nointerface = 0; noescape = 0; - nosplit = 0; } vardcl_list:
関数宣言の解析時に
nosplit
フラグをノードに割り当てる処理と、宣言リストの初期化時にnosplit
をリセットする処理が削除されました。 -
src/cmd/gc/lex.c
:--- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -1592,10 +1592,6 @@ go: noescape = 1; goto out; } - if(strcmp(lexbuf, "go:nosplit") == 0) { - nosplit = 1; - goto out; - } out: return c;
go:nosplit
プラグマを認識し、nosplit
フラグをセットする字句解析ロジックが削除されました。 -
src/cmd/gc/pgen.c
:--- a/src/cmd/gc/pgen.c +++ b/src/cmd/gc/pgen.c @@ -229,8 +229,6 @@ compile(Node *fn) ptxt->TEXTFLAG |= WRAPPER; if(fn->needctxt) ptxt->TEXTFLAG |= NEEDCTXT; - if(fn->nosplit) - ptxt->TEXTFLAG |= NOSPLIT; // Clumsy but important. // See test/recover.go for test cases and src/pkg/reflect/value.go
関数の
nosplit
フラグに基づいて、生成されるテキストセクションにNOSPLIT
フラグを設定するロジックが削除されました。 -
src/cmd/gc/y.tab.c
:--- a/src/cmd/gc/y.tab.c +++ b/src/cmd/gc/y.tab.c @@ -3828,7 +3828,6 @@ yyreduce: (yyval.node)->nbody = (yyvsp[(3) - (3)].list); (yyval.node)->endlineno = lineno; (yyval.node)->noescape = noescape; - (yyval.node)->nosplit = nosplit; funcbody((yyval.node)); }\ break; @@ -4038,7 +4037,6 @@ yyreduce: testdclstack(); nointerface = 0; noescape = 0; - nosplit = 0; }\ break;
go.y
から生成されたパーサーコード(y.tab.c
)からも、nosplit
関連の割り当てとリセットの処理が削除されました。
コアとなるコードの解説
これらの変更は、Goコンパイラから go:nosplit
プラグマのサポートを完全に削除するものです。
src/cmd/gc/go.h
の変更:Node
構造体からnosplit
フィールドが削除されたことで、コンパイラはもはや個々の関数ノードがnosplit
であるという情報を内部的に保持しなくなりました。また、グローバルなnosplit
変数の削除は、字句解析器や構文解析器がこの状態を追跡する必要がなくなったことを意味します。src/cmd/gc/lex.c
の変更:go:nosplit
文字列を認識し、対応するフラグをセットするコードが削除されたため、コンパイラはソースコード中の//go:nosplit
ディレクティブを特別な意味を持つものとして扱わなくなりました。これは単なるコメントとして無視されるようになります。src/cmd/gc/go.y
およびsrc/cmd/gc/y.tab.c
の変更: 構文解析フェーズで、関数宣言が解析される際にnosplit
フラグをASTノードに割り当てる処理が削除されました。これにより、コンパイラの内部表現からnosplit
の概念が取り除かれます。src/cmd/gc/pgen.c
の変更: コード生成の前処理において、関数のnosplit
フラグに基づいてTEXTFLAG
にNOSPLIT
を設定するロジックが削除されました。これは、最終的に生成されるアセンブリコードにNOSPLIT
という指示が含まれなくなることを意味します。結果として、これらの関数は通常の関数と同様にスタック分割の対象となり得ます。src/cmd/gc/fmt.c
の変更: コメントの修正は、nosplit
機能が削除されたことによる単なるクリーンアップです。
これらの変更は一貫しており、go:nosplit
プラグマのライフサイクル(認識、内部表現、コード生成への影響)全体をコンパイラから取り除いています。これにより、Windows ビルダーで発生していた問題が解消されることが期待されます。
関連リンク
- このコミットのGerrit CL: https://golang.org/cl/105260044
- 元に戻されたコミットのGerrit CL: https://golang.org/cl/93380044
参考にした情報源リンク
- Go言語のスタック管理と
go:nosplit
についての議論 (Go issue trackerなど):- https://github.com/golang/go/issues/14951 (go:nosplit: document it)
- https://go.dev/src/cmd/compile/internal/gc/go.go (Goコンパイラのソースコード - 現在の
go:nosplit
の実装に関する情報がある可能性)
- Goコンパイラの内部構造に関する一般的な情報:
- https://go.dev/doc/articles/go_compiler (The Go compiler)
- https://go.dev/src/cmd/compile/internal/gc/ (Goコンパイラのソースコードディレクトリ)
- Goのビルドシステムとテストに関する情報:
- https://go.dev/doc/contribute (Contributing to the Go project)
- https://build.golang.org/ (Go Project Dashboard)
(注: 上記の参考リンクは、当時の情報や現在のGoのドキュメントに基づいて推測されるものです。コミット当時の正確な情報源を特定することは困難な場合があります。)