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

[インデックス 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 93380044CL 105260044 は、Gerrit 上の特定の変更セットを指します。

Windows ビルダー

Goプロジェクトは、様々なオペレーティングシステムやアーキテクチャ向けに自動化されたビルドおよびテストシステム(通称「ビルダー」)を運用しています。これにより、コード変更が異なる環境で正しく動作するかどうかを継続的に検証しています。このコミットの背景にある「Windows ビルダーが壊れている」という問題は、GoのコードベースがWindows環境でコンパイルまたは実行する際に、go:nosplit の実装が何らかの互換性問題やバグを引き起こしていたことを意味します。

技術的詳細

このコミットの技術的詳細は、Goコンパイラが go:nosplit プラグマをどのように認識し、処理し、そして最終的に生成されるコードにどのように影響を与えていたか、そしてその機能がどのように削除されたかに集約されます。

  1. プラグマの認識と解析:

    • src/cmd/gc/lex.c はコンパイラの字句解析器であり、ソースコードをトークンに分割します。元の実装では、go:nosplit という文字列を特別なプラグマとして認識し、内部的な nosplit フラグをセットしていました。
    • src/cmd/gc/go.y はコンパイラの構文解析器(Yacc/Bisonによって生成される)の定義ファイルです。ここでは、関数宣言を解析する際に、字句解析器によってセットされた nosplit フラグを、抽象構文木(AST)のノード(Node 構造体)に伝播させていました。
  2. 内部表現とフラグの管理:

    • src/cmd/gc/go.h はコンパイラの内部データ構造の定義を含んでいます。Node 構造体には、関数が nosplit であるかどうかを示す nosplit フィールドが追加されていました。また、グローバルな nosplit 変数も存在し、プラグマの認識状態を保持していました。
  3. コード生成への影響:

    • 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 フラグに基づいて TEXTFLAGNOSPLIT を設定するロジックが削除されました。これは、最終的に生成されるアセンブリコードに NOSPLIT という指示が含まれなくなることを意味します。結果として、これらの関数は通常の関数と同様にスタック分割の対象となり得ます。
  • src/cmd/gc/fmt.c の変更: コメントの修正は、nosplit 機能が削除されたことによる単なるクリーンアップです。

これらの変更は一貫しており、go:nosplit プラグマのライフサイクル(認識、内部表現、コード生成への影響)全体をコンパイラから取り除いています。これにより、Windows ビルダーで発生していた問題が解消されることが期待されます。

関連リンク

参考にした情報源リンク

(注: 上記の参考リンクは、当時の情報や現在のGoのドキュメントに基づいて推測されるものです。コミット当時の正確な情報源を特定することは困難な場合があります。)