[インデックス 19638] ファイルの概要
このコミットは、GoランタイムのARMアーキテクチャ向けアセンブリコードにおけるコメントの更新を目的としています。具体的には、m
(machine) レジスタに関する記述が削除されており、これはGoランタイムの内部的な変更により、特定のコンテキストで m
レジスタの明示的な保存・復元が不要になったことを反映しています。
変更されたファイルは以下の2つです。
src/pkg/runtime/asm_arm.s
: Goランタイムの主要なARMアセンブリコードが含まれています。goroutineの切り替え (gogo
) やシグナルハンドリングに関連するコメントが修正されています。src/pkg/runtime/cgo/gcc_arm.S
: Cgo (GoとCの相互運用) のためのARMアセンブリコードが含まれています。setg_gcc
関数に関連するコメントが修正されています。
コミット
commit 54951023cb0a1743f7f3cb233ff424593bf1a131
Author: David Crawshaw <david.crawshaw@zentus.com>
Date: Mon Jun 30 19:10:41 2014 -0400
runtime: update arm comments now register m is gone
LGTM=minux
R=golang-codereviews, minux
CC=golang-codereviews
https://golang.org/cl/109220046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/54951023cb0a1743f7f3cb233ff424593bf1a131
元コミット内容
runtime: update arm comments now register m is gone
変更の背景
このコミットの背景には、GoランタイムのARMアーキテクチャにおける内部的な変更があります。以前は、特定のGoランタイムの操作、特にCgo呼び出しやシグナルハンドリングの際に、現在のgoroutine (g
) と共に m
(machine、OSスレッドを表すGoランタイムの構造体) の状態も明示的に保存・復元する必要がありました。これは、GoとCの実行コンテキスト間の遷移や、予期せぬシグナル(例: SIGSEGV)が発生した場合に、Goランタイムの状態を正しく維持するためです。
しかし、このコミットが行われる前に、GoランタイムのスケジューラやARMアセンブリコードの設計が変更され、これらの特定のコンテキストにおいて m
レジスタ(または m
構造体へのポインタを保持するレジスタ)の明示的な管理が不要になったと考えられます。このコミットは、その基盤となる変更を反映し、コードのコメントを最新の状態に保つためのクリーンアップ作業です。これにより、コードの可読性が向上し、開発者がGoランタイムの現在の動作をより正確に理解できるようになります。
前提知識の解説
このコミットを理解するためには、以下のGoランタイムの概念とARMアセンブリの基礎知識が必要です。
- Goランタイム (Go Runtime): Goプログラムの実行を管理するシステムです。これには、スケジューラ(goroutineの実行を管理)、ガベージコレクタ、メモリ管理、Cgoインターフェースなどが含まれます。Goプログラムは、OSによって直接実行されるのではなく、Goランタイム上で動作します。
- Goroutine (
g
): Goランタイムが管理する軽量な実行単位です。OSスレッドよりもはるかに軽量で、数百万のgoroutineを同時に実行できます。Goランタイムは、複数のgoroutineを少数のOSスレッド (m
) にマッピングして実行します。 - Machine (
m
): Goランタイムにおいて、OSスレッドを表す構造体です。m
はGoコードを実行する実際のOSスレッドであり、スケジューラによってp
(Processor) とg
(Goroutine) が割り当てられます。 - Processor (
p
): 論理的なプロセッサを表すGoランタイムの構造体です。p
は実行可能なgoroutineのキューを保持し、m
に割り当てられてgoroutineを実行します。m
とp
は1対1で関連付けられ、p
がなければm
はGoコードを実行できません。 - Cgo: GoプログラムからC言語の関数を呼び出したり、C言語からGoの関数を呼び出したりするためのGoの機能です。Cgoを使用すると、GoとCの実行コンテキスト間での頻繁な切り替えが発生し、その際にGoランタイムの状態(特に現在のgoroutine)を正しく保存・復元する必要があります。
- ARMアセンブリ: ARMプロセッサ向けの低レベルなプログラミング言語です。Goランタイムのパフォーマンスが重要な部分や、OSとの直接的なインタラクションが必要な部分(例: goroutineの切り替え、シグナルハンドリング)は、アセンブリ言語で記述されています。
- レジスタ: CPU内部にある高速な記憶領域です。ARMプロセッサには汎用レジスタ(R0-R12)、スタックポインタ(SP)、リンクレジスタ(LR)、プログラムカウンタ(PC)などがあります。
- R9, R10: GoのARMアセンブリでは、慣例的にR10が現在のgoroutine (
g
) へのポインタを、R9が現在のmachine (m
) へのポインタを保持するために使用されることがあります。 - SP (Stack Pointer): 現在のスタックフレームの最上位アドレスを指します。
- LR (Link Register): 関数呼び出しからの戻りアドレスを保持します。
- FP (Frame Pointer): 現在のスタックフレームのベースアドレスを指します。
- R9, R10: GoのARMアセンブリでは、慣例的にR10が現在のgoroutine (
- TLS (Thread Local Storage): スレッドごとに独立したデータを保存するためのメカニズムです。Cgoやシグナルハンドリングのコンテキストで、Goランタイムがレジスタを直接操作できない場合(例: 外部Cコードがレジスタを使用している場合)に、
g
やm
のポインタをTLSに保存することがあります。 - SIGSEGV (Segmentation Fault): プログラムが不正なメモリ領域にアクセスしようとしたときにOSが生成するシグナル(セグメンテーション違反)です。Goランタイムは、このようなシグナルを捕捉し、適切に処理することで、プログラムのクラッシュを防ぎ、スタックトレースなどのデバッグ情報を提供します。
- ABI (Application Binary Interface): 異なるコンパイラや言語で生成されたコードが相互に作用するための規約です。レジスタの使用方法、関数の引数の渡し方、戻り値の受け取り方などを定義します。GoのARMアセンブリは、特定のABI(例: 5c ABI)に準拠して記述されることがあります。
技術的詳細
このコミットは、GoランタイムのARMアセンブリコード内のコメントを更新する純粋なドキュメンテーションの変更です。しかし、その背後にはGoランタイムの重要な設計変更が示唆されています。
以前のGoランタイムでは、Cgo呼び出しやシグナルハンドリングのような特定のシナリオにおいて、現在のgoroutine (g
) だけでなく、現在のOSスレッド (m
) の状態も明示的に管理する必要がありました。これは、GoとCのコードが同じOSスレッド上で実行される際に、レジスタの状態が予期せず変更されることを防ぎ、Goランタイムの整合性を保つためです。特に、シグナルハンドラが外部Cコードの実行中にトリガーされた場合、Goランタイムが g
や m
を保持するレジスタを安全に利用できる保証がなかったため、TLS (Thread Local Storage) のようなメカニズムに頼る必要がありました。
このコミットは、「m
レジスタがなくなったため」という理由でコメントを更新しています。これは、GoランタイムのARMアセンブリコードにおいて、m
へのポインタを保持するレジスタ(通常はR9)が、特定のコンテキストでGoランタイムの状態を保存・復元する際に考慮すべき重要な要素ではなくなったことを意味します。
考えられる変更の理由は以下の通りです。
- スケジューラの進化: Goスケジューラの設計が進化し、
m
の状態管理がより抽象化されたか、あるいは特定のレジスタに依存しない形になった可能性があります。例えば、m
の状態がメモリ上の構造体で一元的に管理され、レジスタに直接保持する必要がなくなった、またはその必要性が減少した、などが考えられます。 - Cgoインターフェースの改善: Cgoの呼び出し規約や内部実装が改善され、GoとCのコンテキスト切り替え時に
m
レジスタの状態を特別に扱う必要がなくなった可能性があります。 - シグナルハンドリングの堅牢化: シグナルハンドリングのロジックがより堅牢になり、
m
レジスタの明示的な保存・復元なしでも安全に動作するようになった可能性があります。例えば、シグナルハンドラが呼び出される前に、必要なGoランタイムの状態が自動的に保存されるようなメカニズムが導入されたのかもしれません。
このコミット自体はコードの動作を変更するものではなく、既存の動作をより正確に反映するためのドキュメンテーションの更新です。しかし、このコメントの変更は、GoランタイムのARMアーキテクチャにおける低レベルな実装が、よりシンプルで効率的な方向に進化していることを示唆しています。
コアとなるコードの変更箇所
diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s
index 7564e96b1e..4f029c850a 100644
--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -125,7 +125,7 @@ TEXT runtime·gogo(SB), NOSPLIT, $-4-4
MOVW gobuf_g(R1), g
MOVW 0(g), R2 // make sure g != nil
MOVB runtime·iscgo(SB), R2
- CMP $0, R2 // if in Cgo, we have to save g and m
+ CMP $0, R2 // if in Cgo, we have to save g
BL.NE runtime·save_g(SB) // this call will clobber R0
MOVW gobuf_sp(R1), SP // restore SP
MOVW gobuf_lr(R1), LR
@@ -688,10 +688,10 @@ _eqnext:\
MOVB R7, v+16(FP)\n \tRET\n \n-// We have to resort to TLS variable to save g(R10) and\n-// m(R9). One reason is that external code might trigger\n+// We have to resort to TLS variable to save g(R10).\n+// One reason is that external code might trigger\n // SIGSEGV, and our runtime.sigtramp don't even know we\n-// are in external code, and will continue to use R10/R9,\n+// are in external code, and will continue to use R10,\n // this might as well result in another SIGSEGV.\n // Note: all three functions will clobber R0, and the last\n // two can be called from 5c ABI code.\n@@ -724,7 +724,7 @@ TEXT runtime·load_g(SB),NOSPLIT,$0\n MOVW 0(R0), g\n RET\n \n-// void setg_gcc(M*, G*); set m and g called from gcc.\n+// void setg_gcc(G*); set g called from gcc.\n TEXT setg_gcc<>(SB),NOSPLIT,$0\n MOVW R0, g\n B\t\truntime·save_g(SB)\ndiff --git a/src/pkg/runtime/cgo/gcc_arm.S b/src/pkg/runtime/cgo/gcc_arm.S
index 336f8ca62c..2e4b3528ba 100644
--- a/src/pkg/runtime/cgo/gcc_arm.S
+++ b/src/pkg/runtime/cgo/gcc_arm.S
@@ -31,7 +31,7 @@ EXT(crosscall_arm1):\
mov r4, r0\n \tmov r5, r1\n \tmov r0, r2\n-\tblx r5 // setmg(g) \n+\tblx r5 // setg(g) \n \tblx r4 // fn() \n \tpop {r4, r5, r6, r7, r8, r9, r10, r11, ip, pc}\n \n```
## コアとなるコードの解説
このコミットにおけるコードの変更はすべてコメントの修正であり、実行されるアセンブリ命令自体は変更されていません。しかし、これらのコメントの変更は、Goランタイムの内部的な動作に関する重要な情報を提供します。
1. **`src/pkg/runtime/asm_arm.s` の変更点:**
* **`TEXT runtime·gogo(SB)` 内のコメント:**
```diff
- CMP $0, R2 // if in Cgo, we have to save g and m
+ CMP $0, R2 // if in Cgo, we have to save g
```
`runtime·gogo` はgoroutineのコンテキスト切り替えを行う関数です。Cgoのコンテキストにいる場合、以前は `g` (goroutine) と `m` (machine) の両方を保存する必要があるとコメントされていました。この変更により、`m` の保存に関する言及が削除され、Cgo呼び出し時に `g` のみを保存すればよいことが示唆されています。これは、`m` の状態がこの時点でレジスタに依存しない形で管理されているか、あるいは `g` の保存だけで十分な状態が復元できるようになったことを意味します。
* **TLS変数に関するコメント:**
```diff
-// We have to resort to TLS variable to save g(R10) and
-// m(R9). One reason is that external code might trigger
+// We have to resort to TLS variable to save g(R10).
+// One reason is that external code might trigger
// SIGSEGV, and our runtime.sigtramp don't even know we
-// are in external code, and will continue to use R10/R9,\n // this might as well result in another SIGSEGV.\n // Note: all three functions will clobber R0, and the last\n // two can be called from 5c ABI code.\n
+// are in external code, and will continue to use R10,
```
このコメントは、シグナルハンドリング(特にSIGSEGV)の際に、外部Cコードがレジスタを破壊する可能性があるため、`g` (R10) と `m` (R9) をTLS変数に保存する必要があることを説明していました。変更後、`m(R9)` の言及が削除され、`g(R10)` のみをTLSに保存すればよいことが示されています。これは、`m` レジスタがシグナルハンドリングのコンテキストで特別な扱いを必要としなくなったことを明確に示しています。
* **`setg_gcc` 関数のコメント:**
```diff
-// void setg_gcc(M*, G*); set m and g called from gcc.\n TEXT setg_gcc<>(SB),NOSPLIT,$0
+// void setg_gcc(G*); set g called from gcc.\n TEXT setg_gcc<>(SB),NOSPLIT,$0
```
`setg_gcc` は、CコードからGoランタイムに制御が戻る際に、現在の `g` と `m` を設定するために使用される関数でした。変更前は `M*` と `G*` の両方を引数として受け取るとコメントされていましたが、変更後には `G*` のみを受け取るとコメントされています。これは、CコードからGoに遷移する際に `m` を明示的に設定する必要がなくなったことを示唆しています。
2. **`src/pkg/runtime/cgo/gcc_arm.S` の変更点:**
* **`crosscall_arm1` 内のコメント:**
```diff
- tblx r5 // setmg(g)
+ tblx r5 // setg(g)
```
`crosscall_arm1` はCgoのクロス呼び出しに関連するアセンブリコードです。以前は `setmg(g)` というコメントがあり、`g` と `m` の両方を設定する関数が呼び出されることを示唆していました。変更後、コメントは `setg(g)` となり、`g` のみを設定する関数が呼び出されることを示しています。これは `setg_gcc` のコメント変更と一貫しており、`m` の明示的な設定が不要になったことを裏付けています。
これらのコメントの変更は、GoランタイムのARMアーキテクチャにおける低レベルな実装が、`m` レジスタの直接的な管理から解放され、よりシンプルで効率的な設計へと移行したことを示しています。これは、Goランタイムの進化と成熟の一環であり、パフォーマンスの向上やコードの保守性の改善に寄与する可能性があります。
## 関連リンク
* Go issue tracker (CL 109220046): [https://golang.org/cl/109220046](https://golang.org/cl/109220046) (このコミットのChangeListページ)
## 参考にした情報源リンク
* Goのソースコード (特に `src/pkg/runtime/asm_arm.s` と `src/pkg/runtime/cgo/gcc_arm.S` の周辺コード)
* Goのドキュメンテーション (Goランタイム、Cgo、スケジューラに関する情報)
* ARMアーキテクチャのリファレンスマニュアル (レジスタの使用法、アセンブリ命令に関する情報)
* GoのIssueトラッカーやメーリングリスト (関連する議論や設計変更の背景)
* Goのスケジューラに関するブログ記事や解説 (Goの `g`, `m`, `p` モデルの理解のため)