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

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

このコミットは、GoランタイムのGobuf構造体にlr (リンクレジスタ)、ctxt (コンテキスト)、ret (戻り値/戻りアドレス) の各フィールドを追加し、それに伴いゴルーチンのコンテキストスイッチと関数呼び出しのメカニズムを改善するものです。特に、gostartcallおよびgostartcallfnという新しい関数が導入され、既存のgogocallおよびgogocallfnがこれらの新しい関数とgogoの組み合わせとして再定義されています。これにより、Goランタイムがゴルーチンの状態を保存・復元し、関数呼び出しをより柔軟かつ効率的に処理できるようになります。

コミット

commit d67e7e3acff13d845f8952b45daf9b794fa4ad51
Author: Russ Cox <rsc@golang.org>
Date:   Wed Jun 12 15:22:26 2013 -0400

    runtime: add lr, ctxt, ret to Gobuf
    
    Add gostartcall and gostartcallfn.
    The old gogocall = gostartcall + gogo.
    The old gogocallfn = gostartcallfn + gogo.
    
    R=dvyukov, minux.ma
    CC=golang-dev
    https://golang.org/cl/10036044

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

https://github.com/golang/go/commit/d67e7e3acff13d845f8952b45daf9b794fa4ad51

元コミット内容

runtime: add lr, ctxt, ret to Gobuf

Add gostartcall and gostartcallfn.
The old gogocall = gostartcall + gogo.
The old gogocallfn = gostartcallfn + gogo.

変更の背景

Goランタイムは、ゴルーチンのスケジューリングとコンテキストスイッチを効率的に行うために、Gobufというデータ構造を用いてゴルーチンの実行状態を保存・復元します。従来のGobufは、スタックポインタ (sp)、プログラムカウンタ (pc)、現在のゴルーチン (g) などの基本的な情報のみを保持していました。

しかし、Goの内部的な関数呼び出しメカニズム、特にcgoコールバックや新しいゴルーチンの起動、スタックの伸縮といった複雑なシナリオにおいて、より詳細なコンテキスト情報をGobufに含める必要が生じました。具体的には、以下の点が課題となっていました。

  1. 戻りアドレスの管理: ARMのような一部のアーキテクチャでは、関数呼び出し時に戻りアドレスをリンクレジスタ (lr) に格納します。Gobufがこの情報を直接保持しない場合、コンテキストスイッチ後に正確な戻り先を特定するのが困難になる可能性がありました。
  2. 関数コンテキストの伝達: defergoステートメントで関数が呼び出される際、その関数がクロージャである場合など、関連するコンテキスト情報(FuncValなど)を効率的に伝達するメカニズムが必要でした。
  3. 汎用的な戻り値/状態の伝達: gogo関数がコンテキストを復元して実行を再開する際に、特定の戻り値や状態を呼び出し元に伝えるための汎用的なフィールドが求められていました。

これらの課題に対処するため、Gobuflr, ctxt, retフィールドを追加し、gostartcallおよびgostartcallfnという新しい抽象化レイヤーを導入することで、ランタイムの柔軟性と堅牢性を向上させることがこのコミットの背景にあります。これにより、Goランタイムはより複雑な実行フローを正確に管理し、将来的な機能拡張にも対応しやすくなります。

前提知識の解説

このコミットを理解するためには、以下のGoランタイムの概念とアセンブリ言語の知識が不可欠です。

  1. Goルーチン (Goroutine): Goにおける軽量な実行スレッド。OSのスレッドよりもはるかに軽量で、数百万個を同時に実行できます。GoランタイムがこれらのゴルーチンをOSスレッドにマッピングし、スケジューリングを行います。
  2. M (Machine) と P (Processor): Goスケジューラにおける重要な概念です。
    • M (Machine): OSのスレッドを表します。GoランタイムはM上でゴルーチンを実行します。
    • P (Processor): 論理的なプロセッサを表し、Mとゴルーチンの間の仲介役となります。Pは実行可能なゴルーチンのキューを保持し、Mにゴルーチンを割り当てます。
  3. Gobuf: ゴルーチンの実行コンテキスト(状態)を保存するためのデータ構造です。sp (スタックポインタ)、pc (プログラムカウンタ)、g (現在のゴルーチンへのポインタ) などの情報が含まれます。このコミットでlr, ctxt, retが追加されます。
  4. runtime.gosave: 現在のゴルーチンの実行状態(レジスタ、スタックポインタ、プログラムカウンタなど)をGobufに保存するアセンブリ関数です。
  5. runtime.gogo: Gobufに保存された状態を復元し、その状態から実行を再開するアセンブリ関数です。これにより、ゴルーチンのコンテキストスイッチが実現されます。
  6. runtime.mcall: 現在のゴルーチンのスタック(ユーザーゴルーチンのスタック)から、現在のM(OSスレッド)に紐付けられた特別なシステムスタック(g0スタック)に切り替えるアセンブリ関数です。ランタイムの重要な処理(スケジューリング、GCなど)はg0スタック上で行われます。
  7. FuncVal: Goの内部的な構造体で、関数値を表します。特にクロージャの場合、関数エントリポイントへのポインタと、クロージャがキャプチャした変数を含む環境情報(コンテキスト)を保持します。
  8. スタックトレース (Stack Trace): プログラムの実行中に、現在実行中の関数から呼び出し元の関数へと遡って、関数の呼び出し履歴を表示するものです。デバッグやエラー解析に不可欠です。runtime.gentracebackがこの機能を提供します。
  9. アセンブリ言語 (Assembly Language): Goランタイムの低レベルな部分は、パフォーマンスとOSとの直接的な対話のためにアセンブリ言語で記述されています。特に、コンテキストスイッチやレジスタ操作はアセンブリで行われます。このコミットでは、asm_386.s, asm_amd64.s, asm_arm.sといったファイルが変更されています。
  10. リンクレジスタ (Link Register, LR): ARMアーキテクチャなどで使用される特殊なレジスタで、関数呼び出し時に戻りアドレスを格納します。関数から戻る際にこのレジスタの値が使用されます。

技術的詳細

このコミットの核心は、Gobuf構造体の拡張と、それを利用したゴルーチンコンテキストスイッチおよび関数呼び出しメカニズムの再構築にあります。

Gobuf構造体の拡張

src/pkg/runtime/runtime.hにおいて、Gobuf構造体に以下の3つのフィールドが追加されました。

struct	Gobuf
{
	// The offsets of sp, pc, and g are known to (hard-coded in) libmach.
	uintptr	sp;
	uintptr	pc;
	G*	g;
	uintptr	ret;  // 新規追加: 戻り値または戻りアドレス
	void*	ctxt; // 新規追加: コンテキスト情報 (FuncValなど)
	uintptr	lr;   // 新規追加: リンクレジスタ (ARMなど)
};
  • lr (Link Register): ARMアーキテクチャなど、リンクレジスタを持つCPUのために追加されました。関数呼び出し時に戻りアドレスを保持し、gogoでコンテキストを復元する際に正確な戻り先を保証します。x86/amd64では通常0に設定されます。
  • ctxt (Context): 汎用的なコンテキストポインタです。主にFuncVal(クロージャの関数値)へのポインタを格納するために使用されます。これにより、defergoステートメントで呼び出される関数の環境情報をGobuf経由で伝達できるようになります。
  • ret (Return Value/Address): gogoが実行を再開する際に、特定の戻り値や状態を伝えるためのフィールドです。例えば、panic.crecovery関数では、gogoに渡す戻り値としてret = 1を設定しています。

これらのフィールドの追加により、Gobufはゴルーチンの状態をより詳細かつ柔軟に表現できるようになり、特にスタックの切り替えや非同期的な関数呼び出しの際に必要な情報が網羅されます。

gostartcallgostartcallfnの導入

このコミットでは、gogocallgogocallfnという既存の関数呼び出しメカニズムを置き換える形で、gostartcallgostartcallfnという新しい関数が導入されました。

  • runtime.gostartcall(Gobuf *gobuf, void (*fn)(void), void *ctxt):

    • sys_arm.csys_x86.cに新しく追加されたC関数です。
    • Gobufpcfn(呼び出す関数のエントリポイント)に設定し、ctxtを引数として渡されたctxtに設定します。
    • x86/amd64では、gobuf->pcをスタックにプッシュし、gobuf->spを調整することで、fnが呼び出されたかのようにスタックを準備します。
    • ARMでは、gobuf->lrに元のgobuf->pcを保存し、gobuf->pcfnに設定します。これは、ARMのリンクレジスタの挙動を模倣しています。
    • この関数は、gogoが実行される前にGobufを「呼び出し準備完了」の状態にする役割を担います。
  • runtime.gostartcallfn(Gobuf *gobuf, FuncVal *fv):

    • stack.cに新しく追加されたC関数です。
    • FuncVal (fv) を受け取り、そのfnフィールドをgostartcallfn引数として、fv自体をctxt引数としてruntime.gostartcallを呼び出します。
    • これは、FuncVal(特にクロージャ)を新しいゴルーチンとして起動したり、reflect.callのようなメカニズムで呼び出したりする際に使用されます。

gogocallgogocallfnの再定義

従来のgogocallgogocallfnは、Gobufの状態を復元し、その後すぐに指定された関数を呼び出すという複合的な処理を行っていました。このコミットでは、これらの関数が削除され、その機能がgostartcall / gostartcallfngogoの組み合わせとして再定義されました。

  • gogocall / gogocallfn の削除: asm_386.s, asm_amd64.s, asm_arm.sから、これらのアセンブリ関数が削除されました。
  • 新しい呼び出しパターン:
    • runtime.gostartcall(...) または runtime.gostartcallfn(...) を呼び出して、Gobufを呼び出し準備状態にする。
    • その後、runtime.gogo(&gobuf) を呼び出して、Gobufに保存された状態にコンテキストスイッチし、準備された関数を実行する。

この変更により、コンテキストスイッチと関数呼び出しの準備が明確に分離され、コードのモジュール化と理解が容易になります。また、gogoがより汎用的なコンテキスト復元メカニズムとして機能し、gostartcallが特定の関数呼び出しの準備を担当するという役割分担が明確になります。

その他の変更点

  • runtime.gosaveの変更: Gobufに新しいフィールドが追加されたため、runtime.gosaveアセンブリ関数もこれらのフィールドを初期化(通常は0に設定)するように変更されました。
  • runtime.goexitの扱い: runtime.goexitはゴルーチンが終了する際に呼び出される特別な関数ですが、このコミットではgp->fnstartフィールドが削除され、runtime.newproc1で新しいゴルーチンを起動する際にruntime.gostartcallfnを使用してruntime.goexitを初期PCとして設定するようになりました。これにより、ゴルーチンの開始と終了のロジックがより一貫性を持つようになります。
  • スタックトレースの改善: traceback_arm.ctraceback_x86.cにおいて、runtime.goexitruntime.mcallなどの特定の関数が引数を持たないことをruntime.haszeroargs関数で判定するように変更されました。また、runtime.gentracebackの引数にlr0が追加され、ARMアーキテクチャでのトレースバックの精度が向上しました。
  • StackTop定数の変更: src/pkg/runtime/stack.hStackTop72から96に増加しました。これは、Gobuf構造体のサイズが増加したことによる調整と考えられます。
  • runtime.memclrの利用: runtime.newproc1で新しいゴルーチンのschedGobuf)を初期化する際にruntime.memclrが使用され、すべてのフィールドがゼロクリアされるようになりました。これは、新しいGobufフィールドの安全な初期化を保証します。

これらの変更は、Goランタイムの内部構造をより堅牢で、将来の拡張に対応しやすいように進化させるための重要なステップです。特に、ゴルーチンのコンテキスト管理と関数呼び出しの抽象化が強化されたことで、ランタイムの複雑な挙動をより正確に制御できるようになりました。

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

このコミットにおけるコアとなるコードの変更は、主に以下のファイルに集中しています。

  1. src/pkg/runtime/runtime.h: Gobuf構造体の定義が変更され、lr, ctxt, retフィールドが追加されました。また、gogocallgogocallfnのプロトタイプが削除され、gostartcallgostartcallfnのプロトタイプが追加されました。

    --- a/src/pkg/runtime/runtime.h
    +++ b/src/pkg/runtime/runtime.h
    @@ -209,10 +209,13 @@ struct	Slice
     };
     struct	Gobuf
     {
    -	// The offsets of these fields are known to (hard-coded in) libmach.
    +	// The offsets of sp, pc, and g are known to (hard-coded in) libmach.
     	uintptr	sp;
     	uintptr	pc;
     	G*	g;
    +	uintptr	ret;
    +	void*	ctxt;
    +	uintptr	lr;
     };
     struct	GCStats
     {
    @@ -238,7 +241,6 @@ struct	G
     	uintptr	gcguard;		// if status==Gsyscall, gcguard = stackguard to use during gc
     	uintptr	stackguard;	// same as stackguard0, but not set to StackPreempt
     	uintptr	stack0;
    -	FuncVal*	fnstart;		// initial function
     	G*	alllink;	// on allg
     	void*	param;		// passed parameter on wakeup
     	int16	status;
    @@ -711,9 +714,9 @@ int32	runtime·charntorune(int32*, uint8*, int32);\n  */\n #define FLUSH(x)	USED(x)\n \n-void	runtime·gogo(Gobuf*, uintptr);\n-void	runtime·gogocall(Gobuf*, void(*)(void), uintptr);\n-void	runtime·gogocallfn(Gobuf*, FuncVal*);\n+void	runtime·gogo(Gobuf*);\n+void	runtime·gostartcall(Gobuf*, void(*)(void), void*);\n+void	runtime·gostartcallfn(Gobuf*, FuncVal*);\
     void	runtime·gosave(Gobuf*);\n     void	runtime·lessstack(void);\
     void	runtime·goargs(void);\
    
  2. src/pkg/runtime/asm_386.s, src/pkg/runtime/asm_amd64.s, src/pkg/runtime/asm_arm.s:

    • runtime.gogocallruntime.gogocallfnのアセンブリ実装が削除されました。
    • runtime.gosaveruntime.gogoのアセンブリ実装が、新しいGobufフィールドを扱うように変更されました。特にgogoでは、ret, ctxt, lrをレジスタにロードし、Gobuf内のこれらのフィールドをクリアする処理が追加されています。
    • runtime.asmcgocall内でgosaveを呼び出すように変更されました。

    例: src/pkg/runtime/asm_amd64.sgogoの変更

    --- a/src/pkg/runtime/asm_amd64.s
    +++ b/src/pkg/runtime/asm_amd64.s
    @@ -129,50 +131,20 @@ TEXT runtime·gosave(SB), 7, $0
     // void gogo(Gobuf*, uintptr)
     // restore state from Gobuf; longjmp
     TEXT runtime·gogo(SB), 7, $0
    -	MOVQ	16(SP), AX		// return 2nd arg
     	MOVQ	8(SP), BX		// gobuf
     	MOVQ	gobuf_g(BX), DX
     	MOVQ	0(DX), CX		// make sure g != nil
     	get_tls(CX)
     	MOVQ	DX, g(CX)
     	MOVQ	gobuf_sp(BX), SP	// restore SP
    +	MOVQ	gobuf_ret(BX), AX
    +	MOVQ	gobuf_ctxt(BX), DX
    +	MOVQ	$0, gobuf_sp(BX)	// clear to help garbage collector
    +	MOVQ	$0, gobuf_ret(BX)
    +	MOVQ	$0, gobuf_ctxt(BX)
     	MOVQ	gobuf_pc(BX), BX
     	JMP	BX
    
  3. src/pkg/runtime/sys_arm.csrc/pkg/runtime/sys_x86.c:

    • runtime.gostartcall関数が新しく追加されました。この関数は、Gobufを特定の関数呼び出しのために準備します。

    例: src/pkg/runtime/sys_x86.cの新規追加

    // Copyright 2013 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.
    
    // +build amd64 386
    
    #include "runtime.h"
    
    // adjust Gobuf as it if executed a call to fn with context ctxt
    // and then did an immediate gosave.
    void
    runtime·gostartcall(Gobuf *gobuf, void (*fn)(void), void *ctxt)
    {
    	uintptr *sp;
    	
    	sp = (uintptr*)gobuf->sp;
    	*--sp = (uintptr)gobuf->pc;
    	gobuf->sp = (uintptr)sp;
    	gobuf->pc = (uintptr)fn;
    	gobuf->ctxt = ctxt;
    }
    
  4. src/pkg/runtime/stack.c:

    • runtime.gostartcallfn関数が新しく追加されました。
    • runtime.oldstackruntime.newstack内で、gogocallgogocallfnの代わりにgostartcallgostartcallfngogoの組み合わせが使用されるように変更されました。
  5. src/pkg/runtime/proc.c:

    • execute関数内でgogocallfnが削除され、gogoのみが呼び出されるようになりました。
    • runtime.newproc1で新しいゴルーチンを初期化する際に、fnstartフィールドの代わりにruntime.gostartcallfnが使用されるようになりました。
    • saveヘルパー関数が追加され、g->schedのフィールドを初期化する際に使用されます。
    • runtime.haszeroargs関数が追加されました。
  6. src/pkg/runtime/panic.c:

    • recovery関数内でgogoに渡す引数が変更され、gp->sched.ret = 1を設定してからruntime.gogo(&gp->sched)を呼び出すようになりました。
  7. src/pkg/runtime/mgc0.c:

    • addstackroots関数内でgp->sched.lrが使用されるようになりました。
    • gp->sched.ctxtが0でない場合に、そのコンテキストがルートとして追加されるようになりました。
    • mgc関数内でgogoに渡す引数が変更されました。
  8. src/pkg/runtime/traceback_arm.c, src/pkg/runtime/traceback_x86.c:

    • runtime.gentracebackの引数にlr0が追加されました。
    • runtime.goexitに関する特別な処理が削除され、runtime.haszeroargs関数が使用されるようになりました。
    • frame.arglenの計算ロジックが変更されました。

これらの変更は、Goランタイムの低レベルな部分に広範囲にわたる影響を与え、ゴルーチンのライフサイクル管理と関数呼び出しのセマンティクスを根本的に変更しています。

コアとなるコードの解説

このコミットの核となる変更は、Gobuf構造体の拡張と、それを利用したゴルーチンコンテキストスイッチおよび関数呼び出しの新しいパラダイムです。

Gobufの拡張と役割

Gobufは、ゴルーチンの実行状態を保存・復元するための「スナップショット」のようなものです。従来のsp (スタックポインタ)、pc (プログラムカウンタ)、g (ゴルーチンポインタ) に加えて、以下のフィールドが追加されました。

  • lr (Link Register): ARMアーキテクチャのようなCPUでは、関数呼び出し時に戻りアドレスをlrに格納します。このフィールドの追加により、Gobufはこれらのアーキテクチャでより正確なコンテキストを保存できるようになりました。gogoGobufから状態を復元する際、lrの値も適切に設定されることで、関数からの戻りが正しく処理されます。x86/amd64ではこのレジスタは存在しないため、通常は0に設定されます。
  • ctxt (Context): これは汎用的なポインタで、主にFuncVal(関数値、特にクロージャ)へのポインタを格納するために使用されます。例えば、goステートメントで新しいゴルーチンを起動する際、そのゴルーチンが実行する関数がクロージャであれば、そのFuncValctxtに保存されます。これにより、gogoでコンテキストが復元された後、その関数が正しく実行されるために必要な環境情報が利用可能になります。mgc0.caddstackroots関数では、gp->sched.ctxtが0でない場合に、そのコンテキストがGCルートとして追加されるようになりました。これは、ctxtがヒープ上のオブジェクトを指す可能性があるため、GCによって回収されないようにするためです。
  • ret (Return Value/Address): gogoがコンテキストを復元して実行を再開する際に、特定の戻り値や状態を伝えるためのフィールドです。例えば、panic.crecovery関数では、パニックからの回復時にgp->sched.ret = 1を設定し、gogoを呼び出すことで、回復が成功したことを示すシグナルとして利用しています。

これらのフィールドの追加により、Gobufは単なるレジスタの保存場所ではなく、ゴルーチンの実行コンテキスト全体をより豊かに表現できるようになりました。

gostartcallgostartcallfnによる関数呼び出しの抽象化

このコミットのもう一つの重要な側面は、gostartcallgostartcallfnという新しい関数の導入です。これらは、gogoが実行される前にGobufを特定の関数呼び出しのために「準備」する役割を担います。

  • runtime.gostartcall(Gobuf *gobuf, void (*fn)(void), void *ctxt):

    • この関数は、gobufpcフィールドを呼び出す関数fnのエントリポイントに設定します。
    • x86/amd64アーキテクチャでは、gobuf->pcの元の値をスタックにプッシュし、gobuf->spを調整することで、fnが通常の関数呼び出しのように実行されるためのスタックフレームをシミュレートします。これは、gogoがスタックを復元した際に、fnがまるでcall命令で呼び出されたかのように振る舞うことを保証します。
    • ARMアーキテクチャでは、gobuf->lrに元のgobuf->pcを保存し、gobuf->pcfnに設定します。これは、ARMの関数呼び出し規約(戻りアドレスをlrに格納する)に合わせたものです。
    • ctxt引数は、Gobufctxtフィールドに直接コピーされ、関数実行に必要な追加のコンテキスト情報を提供します。
  • runtime.gostartcallfn(Gobuf *gobuf, FuncVal *fv):

    • この関数は、FuncVal (fv) を受け取り、そのfnフィールドをgostartcallfn引数として、fv自体をgostartcallctxt引数として渡します。
    • これは、Goのクロージャやメソッド呼び出しなど、FuncValを介して関数が呼び出されるシナリオを簡潔に扱うためのヘルパー関数です。

gogocallgogocallfnの廃止と新しいフロー

従来のgogocallgogocallfnは、Gobufの復元と関数呼び出しを一体として行っていました。このコミットでは、これらの関数が削除され、その機能は以下の2段階のプロセスに分割されました。

  1. gostartcall / gostartcallfn による呼び出し準備: まず、gostartcallまたはgostartcallfnを呼び出して、Gobufを特定の関数を呼び出すための状態に設定します。これには、pcsplrctxtなどの適切な設定が含まれます。
  2. gogo によるコンテキストスイッチと実行: 次に、gogo(&gobuf)を呼び出します。gogoは、gobufに保存された状態(sp, pc, g, lr, ctxt, ret)を現在のCPUレジスタに復元し、pcにジャンプすることで、準備された関数を実行します。

この分離により、Goランタイムのコードはよりモジュール化され、理解しやすくなりました。gogoは純粋なコンテキストスイッチプリミティブとなり、gostartcallは関数呼び出しの準備という特定のタスクを担当するようになりました。これにより、ランタイムの柔軟性が向上し、将来的に異なる種類の関数呼び出しやコンテキストスイッチのシナリオをより容易にサポートできるようになります。

例えば、新しいゴルーチンを起動するruntime.newproc1関数では、以前はnewg->fnstartというフィールドを使っていましたが、このコミットでfnstartが削除され、代わりにruntime.gostartcallfn(&newg->sched, fn)を呼び出すことで、新しいゴルーチンのGobufを初期関数fnの実行のために準備するようになりました。

runtime.gosaveの変更

runtime.gosaveは、現在のゴルーチンの状態をGobufに保存するアセンブリ関数です。Gobufに新しいフィールドが追加されたため、gosaveもこれらのフィールド(lr, ctxt, ret)を適切に初期化(通常は0に設定)するように変更されました。これにより、保存されるGobufの状態が常に完全で一貫性のあるものになります。

スタックトレースの改善

runtime.gentracebackは、スタックトレースを生成する関数です。このコミットでは、gentracebackの引数にlr0(リンクレジスタの値)が追加され、特にARMアーキテクチャでのトレースバックの精度が向上しました。また、runtime.goexitruntime.mcallのような特定のランタイム関数が引数を持たないことを判定するために、runtime.haszeroargsというヘルパー関数が導入されました。これにより、スタックフレームの引数サイズの計算がより正確になります。

これらの変更は、Goランタイムの内部的な整合性を高め、ゴルーチンのライフサイクル管理、コンテキストスイッチ、およびデバッグ情報の生成をより堅牢かつ効率的に行うための基盤を強化するものです。

関連リンク

  • GoのGobufに関する議論: [https://groups.google.com/g/golang-dev/c/0_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_3. runtime.gosave: This function is responsible for saving the current state of a goroutine. It captures essential information such as the program counter (pc), stack pointer (sp), file, line number, and function name, which are necessary for later resuming the goroutine's execution.[2][3]\n\nThese functions are part of the Go runtime, which is a substantial component compiled into every Go binary, handling core functionalities like garbage collection, scheduling, and goroutine execution.[4][5] The runtime's bootstrap process is the actual entry point of a Go program, not the main function.[4][6]\n\nSources:\n[1] segmentfault.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHusUhVSBmPDoOHRvzq-RFAD4V_JUAUBDGjoJ4maxLhDH5kVq5Q6rtKCPgYveGX5Q_yyNCvzsg29PPlK9FGhy93xLg8gXlauo4ZQvkYgdJLBMdoJrzULl71hSR0R1caHHuSm_MSBSc=)\n[2] vercel.app (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHtFn6mFwHp9XruLjueaE7kJEInASyR7c6VuKVAeiGA7xhf-rhoIPBHSlmGY0KXY6tcuk1ZkxTfubNIK1fUu70oWSIYADc_d6633FAPAtFY7Mq-QFabj-MFKj7sRJvFA0siKfJJcy5nV1PgkF9v0LRjRXaKE-UaPev6lGOmnnzEEy-Nm5HDKbJDngUuNux1j3hwmOx8fzEJ)\n[3] vercel.app (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEtrdvKakTOwc4YA88Mjt_uHNd1e25eLy6F1Hg-nF13GDxrAYUdb9OOKF6GNIV1JyL9LkdFt-5n0fRoD59jkezDNT02sclQgLUX29wZoWC_h-9K3Dgb_s1hRM1FaCXXkQH2K0yc_feVAUrsiH4xkv22ZlQrckvywYegWSZKU4KkIZU8DqizuBLOzi9PDlQENd9HfL-bT9nEyJXh2iBosW51SRMVybwzUJOo1-uKXHTJ4R4=)\n[4] youtube.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEiWPo1AmXPgQ2caz3zBSgy9R9FbDJ5l_z6D5xTksBItq1e0OP_Mb5XpYdXt3_SrR1w57x0KH_D2wbswQpzQmyYHetSXYtIGP4UmSSaVC7cEA6W3ivWpjSGG3U-8_0mOOPuNN88Cdc=)\n[5] golab.io (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGXciM8L5X7P-58F6hUT3fklZwQ_7TKd34cPN-JL6-4lPxKNRIN4eGmi3Yt3-MYYSdFDn3OHLjhwJ7ZStsKsrMlowUeAJJJU9PrNYBnmLrppKFRlHrLizpQ7cXRWAQDQ185vu6u-gX6C5sxZLZkpA==)\n[6] youtube.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFuaGrXC1OLPeu_14G6x8tEyhI4b3jW9gm0zL28MtvunDpR9gpTkiXetyMoxQn7kxitpMGfiwmjCSF84MoN_JLRj3kGn442lTkHQvwyJwXbDGfzjZUjFRUBEMOAtXzWoaCtXpVsUXw=)"}} {"google_web_search_response": {"output": "Web search results for "Go runtime lr ctxt ret in Gobuf":\n\nIn the Go runtime, gobuf is a structure defined in src/runtime/runtime2.go that is used to save and restore the execution state of a goroutine, particularly during context switches. It contains several fields that represent different aspects of the goroutine's state:\n\n* lr (Link Register): This field is relevant for architectures that utilize a link register (such as ARM, ARM64, PPC64, and MIPS). It stores the return program counter (PC) when a function is called, serving a similar purpose to pushing the return address onto the stack in other architectures.[1]\n* ctxt: This field is used to store context-specific information. It can hold a heap-allocated function value (funcval) and is managed by assembly code. For deferred calls or newly started goroutines, ctxt stores the function object that was deferred or initiated with go.[2][3]\n* ret: This field typically stores the return address or return value, playing a role in the function's return mechanism.[2]\n\nThe gobuf structure, along with assembly functions like gogo and gosave, is fundamental to the Go runtime's ability to efficiently manage and switch between goroutines.[1][4]\n\nSources:\n[1] google.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQERsxZRfRRventOCit3CPvSHGzCE_7UwJANeqv3Qq2N3Kdqw3qItVSzStTyIzTWjBm58EMD4wdYc3nSxU2KWwe04Op_Q_6ovPxMzHPrqU26MjIM6AqFzD5sJSJgG7TianFjSqFlpqWNx5I6fzwpm8Jj)\n[2] go.dev (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEPhNjqTToKItsLpAqVQCf5N9dtcQFU1S9k2tt711Bh1xQ9-HTQe1djYw8pUWLuYIC3eKmnmuL9CoYSuT01vcj3KdS8EEWJFcjkropelDe-3QG6Bk4hUYrTuDQ5yZfq69Lb)\n[3] nutanix.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHLZRs6ARlUKiF7DQmxqLc1Kctk4iawCcnbXhnCl1p27hnDrPuL68WH6_6jm5-Qsjj7PYsy2V-pB-VS-Y_9zbdMHiVFlAh3DRafszTGwqMrJQ8IvLxaP5LnXjDUmQ_2t5Yf1XtyJLvvF0Js3yrsyaGHNXK3Nv_5VoaxuY4gWetuC63VAo_UtnrHUJofOf5q_DECOKtlSqvPt6TcsR0wxBcTsgGooqdqfxvz5iw0OA==)\n[4] pact.im (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGJJeYNRBmThcBUsGl18KGRqW9VQkwH35eiAuIcp9qDKqI83QBhOHHgl3LiKA4OH60-bo3dnd5pFg9dDQAGrc1WR9LRRmmBdMoMdrgLQSlWeH7Or9IenHu54MUJR37L97t0kH7RP4PdAQWQYNYcJxiaew==)"}} {"google_web_search_response": {"output": "Web search results for "Go runtime runtime.goexit runtime.mcall FuncVal Stktop runtime.gentraceback":\n\nThe Go runtime is a crucial component of the Go programming language, managing goroutines, memory allocation, garbage collection, and other low-level operations. Understanding specific internal functions and structures provides insight into how Go programs execute efficiently. Here are detailed technical explanations of the provided Go runtime terms and their associated commits or issues:\n\n### runtime.goexit\n\nExplanation: runtime.Goexit is a Go runtime function designed to terminate the goroutine that calls it. Unlike os.Exit, which halts the entire program, runtime.Goexit only affects the calling goroutine, ensuring that all deferred functions within that goroutine are executed before it exits. Other goroutines in the program continue their execution. If runtime.Goexit is invoked from the main goroutine, the main goroutine will terminate, but the program will persist as long as other goroutines are active. The program will only crash if all other goroutines subsequently exit, leading to a deadlock.[1][2][3] This function is rarely used in typical application code but finds its primary utility in specific scenarios, such as within the testing package's FailNow or SkipNow functions, where a controlled, immediate goroutine termination with deferred call execution is necessary.[1]\n\nRelevant Commit/Issue: Issue #7711, titled "program can exit 0 after runtime.Goexit from main," discusses the behavior where a program could exit with a zero status code even after runtime.Goexit was called from the main goroutine. This behavior was initially surprising to some but was ultimately marked as "workingasintended" and closed.[4] This indicates that the current operational model—where the program continues if other goroutines are active and only crashes upon their collective exit—is the intended design.\n\n### runtime.mcall\n\nExplanation: runtime.mcall is a low-level, assembly-implemented runtime function critical for managing execution contexts within the Go scheduler. Its primary purpose is to switch the execution flow from a goroutine's user stack to the special g0 (system) stack of the current operating system thread (M). This stack switch is essential for performing operations that must not be preempted, such as core scheduler logic, internal stack management (like growing or shrinking a goroutine's stack), or handling system calls. Code executing on the g0 stack is implicitly non-preemptible, and its memory is not subject to garbage collection. For[5][6] instance, when a goroutine completes its execution, the goexit assembly function calls goexit1, which then utilizes mcall to transition to the g0 stack to perform necessary cleanup tasks and schedule the next goroutine for execution.\n\n**[6]Relevant Commit/Issue**: Issue #56774, "runtime: mcall called on m->g0 stack," describes a fatal error that occurred when mcall was erroneously invoked while already on the g0 stack, leading to a program crash. This issue was reported to be resolved in Go 1.21.3. The[5] resolution of this bug highlights the importance of precise state management and correct invocation patterns for mcall to ensure the stability of the Go runtime.\n\n### FuncVal\n\nExplanation: FuncVal is an internal runtime structure in Go that represents a function value, particularly for closures. It typically comprises a uintptr field named fn, which points to the function's entry point (the executable code). Additionally, it may contain variable-sized data that encapsulates the closure's environment, specifically the captured variables. Thi[7][8]s structure is foundational to Go's treatment of functions as first-class citizens, enabling them to be passed as arguments, returned from functions, and assigned to variables while correctly preserving their associated state for closures. The reflect package in Go interacts with these internal funcval structures to provide dynamic runtime reflection capabilities for functions.\n\n**[7]Relevant Commit/Issue**: While a single, definitive "definition commit" for FuncVal is challenging to identify due to its long-standing role as an internal type, its structure and usage are evident in core runtime files such as src/runtime/runtime2.go. Mod[8]ifications to FuncVal are typically integrated into broader runtime or compiler changes related to function call mechanisms, closure implementations, or reflection, rather than being isolated commits. For example, the runtime/proc.go file demonstrates newproc utilizing a *funcval when initiating a new goroutine.\n\n##[6]# Stktop\n\nExplanation: "Stktop" is not a specific named entity (like a function or a struct) within the Go runtime's public API or commonly referenced internal components. Instead, it conceptually refers to the "stack top"—the current highest memory address (or lowest, depending on the stack growth direction) of a goroutine's execution stack. The Go runtime employs a dynamic stack management strategy for goroutines: stacks start small (e.g., 2KB in Go 1.20) and automatically grow or shrink as required. When [9][10]a goroutine approaches its stack limit, the runtime allocates a new, larger stack segment, copies the contents of the old stack to the new one, and updates relevant pointers. Conve[9]rsely, stack shrinking mechanisms are in place to reclaim memory if a stack becomes underutilized, optimizing memory usage.\n\nRe[9]levant Commit/Issue: Issue #13183, "runtime: reconsider stack shrinking," discusses the inherent complexities and safety considerations associated with dynamic stack shrinking, particularly in a concurrent environment without a global stop-the-world mechanism. This [11]issue underscores the continuous efforts and challenges in refining Go's dynamic stack management, which directly impacts the conceptual "stack top." Another related commit, "runtime: disable stack shrinking while a goroutine is being traced," addresses a specific scenario where stack shrinking could interfere with tracing operations, further illustrating the ongoing work to ensure correctness and stability in stack management.\n\n### [12]runtime.gentraceback\n\nExplanation: runtime.gentraceback is a crucial internal function within the Go runtime responsible for generating stack traces. It is invoked during various events, including unrecovered panics, unexpected runtime errors, or when debugging tools request a stack dump. This function is inherently complex due to its need to support multiple operational modes: printing the traceback directly, populating a program counter (PC) buffer, or invoking a callback function for each stack frame. Furthermore, it must accurately unwind different types of frames, including physical Go frames, inlined Go frames (where compiler optimizations have eliminated distinct stack frames), and CGO frames. The behavior and verbosity of gentraceback can be controlled by the GOTRACEBACK environment variable, which allows users to specify the level of detail, including whether to include internal runtime frames.\n\nRe[13][14]levant Commit/Issue:\n* Rewrite as Iterator API: Issue #54466, "runtime: rewrite gentraceback as an iterator API," represents a significant ongoing effort to refactor runtime.gentraceback. The p[13]rimary objective is to replace its highly interwoven and complex logic with a more modular, caller-driven iterator-style interface. This refactoring aims to simplify the codebase, improve the layering of different stack unwinding modes (physical, inlined, CGO), and facilitate more efficient scanning of goroutine stacks, particularly for advanced features like open-coded defers.\n* [13]SIGSEGV Fix: Issue #50936, "runtime: SIGSEGV in gentraceback during SIGPROF handling of cgo callback," details a critical bug where gentraceback could cause a segmentation fault when processing profiling signals (SIGPROF) during CGO callbacks. This [15]bug was introduced by a change that extended stack tracing capabilities to CGO tracebacks and was subsequently fixed by reverting the problematic commit (CL 358900) and implementing a regression test. This [15]incident highlights the intricate nature of gentraceback and the continuous challenges involved in maintaining its correctness and robustness across diverse execution contexts, especially when interacting with CGO.\n\nSources:\n[1] go.dev (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEY5-TtPf-ZbBMUQ-rADpRwDaU0Gx-VwrvssTgL70sdI59aAWgZIe1a9Hpr19B54Fi2qtE1j1qCRZJXik_f4t3oUlbjzl9bbKL82YeTmPZHXisOHFCt)\n[2] robreid.io (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG84gGNWvU_L_aFgz_dKCzF6RBHFIE6DKE3xd4Tg1h_wG5Uog4HCGra0EN8HTZXfg8z4C5u1eOnnTZLixSgz9avqKwQB_b6valyC4xC6QSH45Q_d9fR2b4sMmT-61g=)\n[3] medium.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFcjQEgd2j4QwmjNl6OnZ4mJQIvIoXC4wauoNeoyr6m-VTcTg25HlwvyVftpxCi0Rl1Apk8s4kNuA0ZIAVErcjjJDjLoZMkZo4ewFNMKig425_3jvmZsJp05QFSdCaS73dhB6Eruq8XkEEMRlYmwQnkjBIWQwELg8ktFZZREb5Lxhr0xiK_IQhU6W5dF2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2_2.
    
    

コアとなるコードの解説

このコミットの主要な変更は、Goランタイムにおけるゴルーチンのコンテキスト管理と関数呼び出しのメカニズムをより柔軟かつ効率的にするためのものです。

Gobuf構造体の拡張

Gobufは、Goランタイムがゴルーチンの実行状態を保存・復元するために使用する重要なデータ構造です。このコミットでは、既存のsp (スタックポインタ)、pc (プログラムカウンタ)、g (現在のゴルーチンへのポインタ) に加えて、以下の3つのフィールドが追加されました。

  • lr (Link Register): ARMアーキテクチャのような一部のCPUでは、関数呼び出し時に戻りアドレスをリンクレジスタに格納します。このフィールドの追加により、Gobufはこれらのアーキテクチャでより正確なコンテキストを保存できるようになりました。gogoGobufから状態を復元する際、lrの値も適切に設定されることで、関数からの戻りが正しく処理されます。x86/amd64ではこのレジスタは存在しないため、通常は0に設定されます。
  • ctxt (Context): これは汎用的なポインタで、主にFuncVal(関数値、特にクロージャ)へのポインタを格納するために使用されます。例えば、goステートメントで新しいゴルーチンを起動する際、そのゴルーチンが実行する関数がクロージャであれば、そのFuncValctxtに保存されます。これにより、gogoでコンテキストが復元された後、その関数が正しく実行されるために必要な環境情報が利用可能になります。mgc0.caddstackroots関数では、gp->sched.ctxtが0でない場合に、そのコンテキストがGCルートとして追加されるようになりました。これは、ctxtがヒープ上のオブジェクトを指す可能性があるため、GCによって回収されないようにするためです。
  • ret (Return Value/Address): gogoがコンテキストを復元して実行を再開する際に、特定の戻り値や状態を伝えるためのフィールドです。例えば、panic.crecovery関数では、パニックからの回復時にgp->sched.ret = 1を設定し、gogoを呼び出すことで、回復が成功したことを示すシグナルとして利用しています。

これらのフィールドの追加により、Gobufは単なるレジスタの保存場所ではなく、ゴルーチンの実行コンテキスト全体をより豊かに表現できるようになりました。

gostartcallgostartcallfnによる関数呼び出しの抽象化

このコミットのもう一つの重要な側面は、gostartcallgostartcallfnという新しい関数の導入です。これらは、gogoが実行される前にGobufを特定の関数呼び出しのために「準備」する役割を担います。

  • runtime.gostartcall(Gobuf *gobuf, void (*fn)(void), void *ctxt):

    • この関数は、gobufpcフィールドを呼び出す関数fnのエントリポイントに設定します。
    • x86/amd64アーキテクチャでは、gobuf->pcの元の値をスタックにプッシュし、gobuf->spを調整することで、fnが通常の関数呼び出しのように実行されるためのスタックフレームをシミュレートします。これは、gogoがスタックを復元した際に、fnがまるでcall命令で呼び出されたかのように振る舞うことを保証します。
    • ARMアーキテクチャでは、gobuf->lrに元のgobuf->pcを保存し、gobuf->pcfnに設定します。これは、ARMの関数呼び出し規約(戻りアドレスをlrに格納する)に合わせたものです。
    • ctxt引数は、Gobufctxtフィールドに直接コピーされ、関数実行に必要な追加のコンテキスト情報を提供します。
  • runtime.gostartcallfn(Gobuf *gobuf, FuncVal *fv):

    • この関数は、FuncVal (fv) を受け取り、そのfnフィールドをgostartcallfn引数として、fv自体をgostartcallctxt引数として渡します。
    • これは、Goのクロージャやメソッド呼び出しなど、FuncValを介して関数が呼び出されるシナリオを簡潔に扱うためのヘルパー関数です。

gogocallgogocallfnの廃止と新しいフロー

従来のgogocallgogocallfnは、Gobufの復元と関数呼び出しを一体として行っていました。このコミットでは、これらの関数が削除され、その機能は以下の2段階のプロセスに分割されました。

  1. gostartcall / gostartcallfn による呼び出し準備: まず、gostartcallまたはgostartcallfnを呼び出して、Gobufを特定の関数を呼び出すための状態に設定します。これには、pcsplrctxtなどの適切な設定が含まれます。
  2. gogo によるコンテキストスイッチと実行: 次に、gogo(&gobuf)を呼び出します。gogoは、gobufに保存された状態(sp, pc, g, lr, ctxt, ret)を現在のCPUレジスタに復元し、pcにジャンプすることで、準備された関数を実行します。

この分離により、Goランタイムのコードはよりモジュール化され、理解しやすくなりました。gogoは純粋なコンテキストスイッチプリミティブとなり、gostartcallは関数呼び出しの準備という特定のタスクを担当するようになりました。これにより、ランタイムの柔軟性が向上し、将来的に異なる種類の関数呼び出しやコンテキストスイッチのシナリオをより容易にサポートできるようになります。

例えば、新しいゴルーチンを起動するruntime.newproc1関数では、以前はnewg->fnstartというフィールドを使っていましたが、このコミットでfnstartが削除され、代わりにruntime.gostartcallfn(&newg->sched, fn)を呼び出すことで、新しいゴルーチンのGobufを初期関数fnの実行のために準備するようになりました。

runtime.gosaveの変更

runtime.gosaveは、現在のゴルーチンの状態をGobufに保存するアセンブリ関数です。Gobufに新しいフィールドが追加されたため、gosaveもこれらのフィールド(lr, ctxt, ret)を適切に初期化(通常は0に設定)するように変更されました。これにより、保存されるGobufの状態が常に完全で一貫性のあるものになります。

スタックトレースの改善

runtime.gentracebackは、スタックトレースを生成する関数です。このコミットでは、gentracebackの引数にlr0(リンクレジスタの値)が追加され、特にARMアーキテクチャでのトレースバックの精度が向上しました。また、runtime.goexitruntime.mcallのような特定のランタイム関数が引数を持たないことを判定するために、runtime.haszeroargsというヘルパー関数が導入されました。これにより、スタックフレームの引数サイズの計算がより正確になります。

これらの変更は、Goランタイムの内部的な整合性を高め、ゴルーチンのライフサイクル管理、コンテキストスイッチ、およびデバッグ情報の生成をより堅牢かつ効率的に行うための基盤を強化するものです。

参考にした情報源リンク