[インデックス 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
に含める必要が生じました。具体的には、以下の点が課題となっていました。
- 戻りアドレスの管理: ARMのような一部のアーキテクチャでは、関数呼び出し時に戻りアドレスをリンクレジスタ (
lr
) に格納します。Gobuf
がこの情報を直接保持しない場合、コンテキストスイッチ後に正確な戻り先を特定するのが困難になる可能性がありました。 - 関数コンテキストの伝達:
defer
やgo
ステートメントで関数が呼び出される際、その関数がクロージャである場合など、関連するコンテキスト情報(FuncVal
など)を効率的に伝達するメカニズムが必要でした。 - 汎用的な戻り値/状態の伝達:
gogo
関数がコンテキストを復元して実行を再開する際に、特定の戻り値や状態を呼び出し元に伝えるための汎用的なフィールドが求められていました。
これらの課題に対処するため、Gobuf
にlr
, ctxt
, ret
フィールドを追加し、gostartcall
およびgostartcallfn
という新しい抽象化レイヤーを導入することで、ランタイムの柔軟性と堅牢性を向上させることがこのコミットの背景にあります。これにより、Goランタイムはより複雑な実行フローを正確に管理し、将来的な機能拡張にも対応しやすくなります。
前提知識の解説
このコミットを理解するためには、以下のGoランタイムの概念とアセンブリ言語の知識が不可欠です。
- Goルーチン (Goroutine): Goにおける軽量な実行スレッド。OSのスレッドよりもはるかに軽量で、数百万個を同時に実行できます。GoランタイムがこれらのゴルーチンをOSスレッドにマッピングし、スケジューリングを行います。
- M (Machine) と P (Processor): Goスケジューラにおける重要な概念です。
- M (Machine): OSのスレッドを表します。GoランタイムはM上でゴルーチンを実行します。
- P (Processor): 論理的なプロセッサを表し、Mとゴルーチンの間の仲介役となります。Pは実行可能なゴルーチンのキューを保持し、Mにゴルーチンを割り当てます。
- Gobuf: ゴルーチンの実行コンテキスト(状態)を保存するためのデータ構造です。
sp
(スタックポインタ)、pc
(プログラムカウンタ)、g
(現在のゴルーチンへのポインタ) などの情報が含まれます。このコミットでlr
,ctxt
,ret
が追加されます。 runtime.gosave
: 現在のゴルーチンの実行状態(レジスタ、スタックポインタ、プログラムカウンタなど)をGobuf
に保存するアセンブリ関数です。runtime.gogo
:Gobuf
に保存された状態を復元し、その状態から実行を再開するアセンブリ関数です。これにより、ゴルーチンのコンテキストスイッチが実現されます。runtime.mcall
: 現在のゴルーチンのスタック(ユーザーゴルーチンのスタック)から、現在のM(OSスレッド)に紐付けられた特別なシステムスタック(g0
スタック)に切り替えるアセンブリ関数です。ランタイムの重要な処理(スケジューリング、GCなど)はg0
スタック上で行われます。FuncVal
: Goの内部的な構造体で、関数値を表します。特にクロージャの場合、関数エントリポイントへのポインタと、クロージャがキャプチャした変数を含む環境情報(コンテキスト)を保持します。- スタックトレース (Stack Trace): プログラムの実行中に、現在実行中の関数から呼び出し元の関数へと遡って、関数の呼び出し履歴を表示するものです。デバッグやエラー解析に不可欠です。
runtime.gentraceback
がこの機能を提供します。 - アセンブリ言語 (Assembly Language): Goランタイムの低レベルな部分は、パフォーマンスとOSとの直接的な対話のためにアセンブリ言語で記述されています。特に、コンテキストスイッチやレジスタ操作はアセンブリで行われます。このコミットでは、
asm_386.s
,asm_amd64.s
,asm_arm.s
といったファイルが変更されています。 - リンクレジスタ (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
(クロージャの関数値)へのポインタを格納するために使用されます。これにより、defer
やgo
ステートメントで呼び出される関数の環境情報をGobuf
経由で伝達できるようになります。ret
(Return Value/Address):gogo
が実行を再開する際に、特定の戻り値や状態を伝えるためのフィールドです。例えば、panic.c
のrecovery
関数では、gogo
に渡す戻り値としてret = 1
を設定しています。
これらのフィールドの追加により、Gobuf
はゴルーチンの状態をより詳細かつ柔軟に表現できるようになり、特にスタックの切り替えや非同期的な関数呼び出しの際に必要な情報が網羅されます。
gostartcall
とgostartcallfn
の導入
このコミットでは、gogocall
とgogocallfn
という既存の関数呼び出しメカニズムを置き換える形で、gostartcall
とgostartcallfn
という新しい関数が導入されました。
-
runtime.gostartcall(Gobuf *gobuf, void (*fn)(void), void *ctxt)
:sys_arm.c
とsys_x86.c
に新しく追加されたC関数です。Gobuf
のpc
をfn
(呼び出す関数のエントリポイント)に設定し、ctxt
を引数として渡されたctxt
に設定します。- x86/amd64では、
gobuf->pc
をスタックにプッシュし、gobuf->sp
を調整することで、fn
が呼び出されたかのようにスタックを準備します。 - ARMでは、
gobuf->lr
に元のgobuf->pc
を保存し、gobuf->pc
をfn
に設定します。これは、ARMのリンクレジスタの挙動を模倣しています。 - この関数は、
gogo
が実行される前にGobuf
を「呼び出し準備完了」の状態にする役割を担います。
-
runtime.gostartcallfn(Gobuf *gobuf, FuncVal *fv)
:stack.c
に新しく追加されたC関数です。FuncVal
(fv
) を受け取り、そのfn
フィールドをgostartcall
のfn
引数として、fv
自体をctxt
引数としてruntime.gostartcall
を呼び出します。- これは、
FuncVal
(特にクロージャ)を新しいゴルーチンとして起動したり、reflect.call
のようなメカニズムで呼び出したりする際に使用されます。
gogocall
とgogocallfn
の再定義
従来のgogocall
とgogocallfn
は、Gobuf
の状態を復元し、その後すぐに指定された関数を呼び出すという複合的な処理を行っていました。このコミットでは、これらの関数が削除され、その機能がgostartcall
/ gostartcallfn
とgogo
の組み合わせとして再定義されました。
- 旧
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.c
とtraceback_x86.c
において、runtime.goexit
やruntime.mcall
などの特定の関数が引数を持たないことをruntime.haszeroargs
関数で判定するように変更されました。また、runtime.gentraceback
の引数にlr0
が追加され、ARMアーキテクチャでのトレースバックの精度が向上しました。 StackTop
定数の変更:src/pkg/runtime/stack.h
でStackTop
が72
から96
に増加しました。これは、Gobuf
構造体のサイズが増加したことによる調整と考えられます。runtime.memclr
の利用:runtime.newproc1
で新しいゴルーチンのsched
(Gobuf
)を初期化する際にruntime.memclr
が使用され、すべてのフィールドがゼロクリアされるようになりました。これは、新しいGobuf
フィールドの安全な初期化を保証します。
これらの変更は、Goランタイムの内部構造をより堅牢で、将来の拡張に対応しやすいように進化させるための重要なステップです。特に、ゴルーチンのコンテキスト管理と関数呼び出しの抽象化が強化されたことで、ランタイムの複雑な挙動をより正確に制御できるようになりました。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に以下のファイルに集中しています。
-
src/pkg/runtime/runtime.h
:Gobuf
構造体の定義が変更され、lr
,ctxt
,ret
フィールドが追加されました。また、gogocall
とgogocallfn
のプロトタイプが削除され、gostartcall
とgostartcallfn
のプロトタイプが追加されました。--- 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);\
-
src/pkg/runtime/asm_386.s
,src/pkg/runtime/asm_amd64.s
,src/pkg/runtime/asm_arm.s
:runtime.gogocall
とruntime.gogocallfn
のアセンブリ実装が削除されました。runtime.gosave
とruntime.gogo
のアセンブリ実装が、新しいGobuf
フィールドを扱うように変更されました。特にgogo
では、ret
,ctxt
,lr
をレジスタにロードし、Gobuf
内のこれらのフィールドをクリアする処理が追加されています。runtime.asmcgocall
内でgosave
を呼び出すように変更されました。
例:
src/pkg/runtime/asm_amd64.s
のgogo
の変更--- 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
-
src/pkg/runtime/sys_arm.c
とsrc/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; }
-
src/pkg/runtime/stack.c
:runtime.gostartcallfn
関数が新しく追加されました。runtime.oldstack
とruntime.newstack
内で、gogocall
やgogocallfn
の代わりにgostartcall
やgostartcallfn
とgogo
の組み合わせが使用されるように変更されました。
-
src/pkg/runtime/proc.c
:execute
関数内でgogocallfn
が削除され、gogo
のみが呼び出されるようになりました。runtime.newproc1
で新しいゴルーチンを初期化する際に、fnstart
フィールドの代わりにruntime.gostartcallfn
が使用されるようになりました。save
ヘルパー関数が追加され、g->sched
のフィールドを初期化する際に使用されます。runtime.haszeroargs
関数が追加されました。
-
src/pkg/runtime/panic.c
:recovery
関数内でgogo
に渡す引数が変更され、gp->sched.ret = 1
を設定してからruntime.gogo(&gp->sched)
を呼び出すようになりました。
-
src/pkg/runtime/mgc0.c
:addstackroots
関数内でgp->sched.lr
が使用されるようになりました。gp->sched.ctxt
が0でない場合に、そのコンテキストがルートとして追加されるようになりました。mgc
関数内でgogo
に渡す引数が変更されました。
-
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
はこれらのアーキテクチャでより正確なコンテキストを保存できるようになりました。gogo
がGobuf
から状態を復元する際、lr
の値も適切に設定されることで、関数からの戻りが正しく処理されます。x86/amd64ではこのレジスタは存在しないため、通常は0に設定されます。ctxt
(Context): これは汎用的なポインタで、主にFuncVal
(関数値、特にクロージャ)へのポインタを格納するために使用されます。例えば、go
ステートメントで新しいゴルーチンを起動する際、そのゴルーチンが実行する関数がクロージャであれば、そのFuncVal
がctxt
に保存されます。これにより、gogo
でコンテキストが復元された後、その関数が正しく実行されるために必要な環境情報が利用可能になります。mgc0.c
のaddstackroots
関数では、gp->sched.ctxt
が0でない場合に、そのコンテキストがGCルートとして追加されるようになりました。これは、ctxt
がヒープ上のオブジェクトを指す可能性があるため、GCによって回収されないようにするためです。ret
(Return Value/Address):gogo
がコンテキストを復元して実行を再開する際に、特定の戻り値や状態を伝えるためのフィールドです。例えば、panic.c
のrecovery
関数では、パニックからの回復時にgp->sched.ret = 1
を設定し、gogo
を呼び出すことで、回復が成功したことを示すシグナルとして利用しています。
これらのフィールドの追加により、Gobuf
は単なるレジスタの保存場所ではなく、ゴルーチンの実行コンテキスト全体をより豊かに表現できるようになりました。
gostartcall
とgostartcallfn
による関数呼び出しの抽象化
このコミットのもう一つの重要な側面は、gostartcall
とgostartcallfn
という新しい関数の導入です。これらは、gogo
が実行される前にGobuf
を特定の関数呼び出しのために「準備」する役割を担います。
-
runtime.gostartcall(Gobuf *gobuf, void (*fn)(void), void *ctxt)
:- この関数は、
gobuf
のpc
フィールドを呼び出す関数fn
のエントリポイントに設定します。 - x86/amd64アーキテクチャでは、
gobuf->pc
の元の値をスタックにプッシュし、gobuf->sp
を調整することで、fn
が通常の関数呼び出しのように実行されるためのスタックフレームをシミュレートします。これは、gogo
がスタックを復元した際に、fn
がまるでcall
命令で呼び出されたかのように振る舞うことを保証します。 - ARMアーキテクチャでは、
gobuf->lr
に元のgobuf->pc
を保存し、gobuf->pc
をfn
に設定します。これは、ARMの関数呼び出し規約(戻りアドレスをlr
に格納する)に合わせたものです。 ctxt
引数は、Gobuf
のctxt
フィールドに直接コピーされ、関数実行に必要な追加のコンテキスト情報を提供します。
- この関数は、
-
runtime.gostartcallfn(Gobuf *gobuf, FuncVal *fv)
:- この関数は、
FuncVal
(fv
) を受け取り、そのfn
フィールドをgostartcall
のfn
引数として、fv
自体をgostartcall
のctxt
引数として渡します。 - これは、Goのクロージャやメソッド呼び出しなど、
FuncVal
を介して関数が呼び出されるシナリオを簡潔に扱うためのヘルパー関数です。
- この関数は、
gogocall
とgogocallfn
の廃止と新しいフロー
従来のgogocall
とgogocallfn
は、Gobuf
の復元と関数呼び出しを一体として行っていました。このコミットでは、これらの関数が削除され、その機能は以下の2段階のプロセスに分割されました。
gostartcall
/gostartcallfn
による呼び出し準備: まず、gostartcall
またはgostartcallfn
を呼び出して、Gobuf
を特定の関数を呼び出すための状態に設定します。これには、pc
、sp
、lr
、ctxt
などの適切な設定が含まれます。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.goexit
やruntime.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 themain
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 insrc/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 withgo
.[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\nThegobuf
structure, along with assembly functions likegogo
andgosave
, 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. Unlikeos.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. Ifruntime.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 thetesting
package'sFailNow
orSkipNow
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 afterruntime.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 specialg0
(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 theg0
stack is implicitly non-preemptible, and its memory is not subject to garbage collection. For[5][6] instance, when a goroutine completes its execution, thegoexit
assembly function callsgoexit1
, which then utilizesmcall
to transition to theg0
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 whenmcall
was erroneously invoked while already on theg0
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 formcall
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 auintptr
field namedfn
, 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. Thereflect
package in Go interacts with these internalfuncval
structures to provide dynamic runtime reflection capabilities for functions.\n\n**[7]Relevant Commit/Issue**: While a single, definitive "definition commit" forFuncVal
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 assrc/runtime/runtime2.go
. Mod[8]ifications toFuncVal
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, theruntime/proc.go
file demonstratesnewproc
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 ofgentraceback
can be controlled by theGOTRACEBACK
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 refactorruntime.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 wheregentraceback
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 ofgentraceback
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
はこれらのアーキテクチャでより正確なコンテキストを保存できるようになりました。gogo
がGobuf
から状態を復元する際、lr
の値も適切に設定されることで、関数からの戻りが正しく処理されます。x86/amd64ではこのレジスタは存在しないため、通常は0に設定されます。ctxt
(Context): これは汎用的なポインタで、主にFuncVal
(関数値、特にクロージャ)へのポインタを格納するために使用されます。例えば、go
ステートメントで新しいゴルーチンを起動する際、そのゴルーチンが実行する関数がクロージャであれば、そのFuncVal
がctxt
に保存されます。これにより、gogo
でコンテキストが復元された後、その関数が正しく実行されるために必要な環境情報が利用可能になります。mgc0.c
のaddstackroots
関数では、gp->sched.ctxt
が0でない場合に、そのコンテキストがGCルートとして追加されるようになりました。これは、ctxt
がヒープ上のオブジェクトを指す可能性があるため、GCによって回収されないようにするためです。ret
(Return Value/Address):gogo
がコンテキストを復元して実行を再開する際に、特定の戻り値や状態を伝えるためのフィールドです。例えば、panic.c
のrecovery
関数では、パニックからの回復時にgp->sched.ret = 1
を設定し、gogo
を呼び出すことで、回復が成功したことを示すシグナルとして利用しています。
これらのフィールドの追加により、Gobuf
は単なるレジスタの保存場所ではなく、ゴルーチンの実行コンテキスト全体をより豊かに表現できるようになりました。
gostartcall
とgostartcallfn
による関数呼び出しの抽象化
このコミットのもう一つの重要な側面は、gostartcall
とgostartcallfn
という新しい関数の導入です。これらは、gogo
が実行される前にGobuf
を特定の関数呼び出しのために「準備」する役割を担います。
-
runtime.gostartcall(Gobuf *gobuf, void (*fn)(void), void *ctxt)
:- この関数は、
gobuf
のpc
フィールドを呼び出す関数fn
のエントリポイントに設定します。 - x86/amd64アーキテクチャでは、
gobuf->pc
の元の値をスタックにプッシュし、gobuf->sp
を調整することで、fn
が通常の関数呼び出しのように実行されるためのスタックフレームをシミュレートします。これは、gogo
がスタックを復元した際に、fn
がまるでcall
命令で呼び出されたかのように振る舞うことを保証します。 - ARMアーキテクチャでは、
gobuf->lr
に元のgobuf->pc
を保存し、gobuf->pc
をfn
に設定します。これは、ARMの関数呼び出し規約(戻りアドレスをlr
に格納する)に合わせたものです。 ctxt
引数は、Gobuf
のctxt
フィールドに直接コピーされ、関数実行に必要な追加のコンテキスト情報を提供します。
- この関数は、
-
runtime.gostartcallfn(Gobuf *gobuf, FuncVal *fv)
:- この関数は、
FuncVal
(fv
) を受け取り、そのfn
フィールドをgostartcall
のfn
引数として、fv
自体をgostartcall
のctxt
引数として渡します。 - これは、Goのクロージャやメソッド呼び出しなど、
FuncVal
を介して関数が呼び出されるシナリオを簡潔に扱うためのヘルパー関数です。
- この関数は、
gogocall
とgogocallfn
の廃止と新しいフロー
従来のgogocall
とgogocallfn
は、Gobuf
の復元と関数呼び出しを一体として行っていました。このコミットでは、これらの関数が削除され、その機能は以下の2段階のプロセスに分割されました。
gostartcall
/gostartcallfn
による呼び出し準備: まず、gostartcall
またはgostartcallfn
を呼び出して、Gobuf
を特定の関数を呼び出すための状態に設定します。これには、pc
、sp
、lr
、ctxt
などの適切な設定が含まれます。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.goexit
やruntime.mcall
のような特定のランタイム関数が引数を持たないことを判定するために、runtime.haszeroargs
というヘルパー関数が導入されました。これにより、スタックフレームの引数サイズの計算がより正確になります。
これらの変更は、Goランタイムの内部的な整合性を高め、ゴルーチンのライフサイクル管理、コンテキストスイッチ、およびデバッグ情報の生成をより堅牢かつ効率的に行うための基盤を強化するものです。
参考にした情報源リンク
- Go runtime Gobuf purpose: https://go.dev/src/runtime/runtime2.go
- Go runtime gostartcall and gostartcallfn: https://go.dev/src/runtime/proc.go
- Go runtime gosave gogo: https://segmentfault.com/a/1190000040030000
- Go runtime lr ctxt ret in Gobuf: https://go.dev/src/runtime/runtime.h
- Go runtime runtime.goexit: https://go.dev/doc/go1.13#goexit
- Go runtime runtime.mcall: https://go.dev/src/runtime/asm_amd64.s
- Go runtime FuncVal: https://go.dev/src/runtime/runtime2.go
- Go runtime Stktop: https://go.dev/src/runtime/stack.go
- Go runtime runtime.gentraceback: https://go.dev/src/runtime/traceback.go
- Go CL 10036044: https://golang.org/cl/10036044
- Go Issue 7711: https://github.com/golang/go/issues/7711
- Go Issue 56774: https://github.com/golang/go/issues/56774
- Go Issue 13183: https://github.com/golang/go/issues/13183
- Go Issue 54466: https://github.com/golang/go/issues/54466
- Go Issue 50936: https://github.com/golang/go/issues/50936