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

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

このコミットは、Go言語のランタイムにおけるARMアーキテクチャ向けのアセンブリコードの修正に関するものです。具体的には、src/pkg/runtime/asm_arm.s ファイル内のruntime·morestack_noctxt関数におけるジャンプ命令がJMPからBに変更されています。

コミット

commit f884e15aabcdf547eb8c8a10e02e6bddc801e7e8
Author: Russ Cox <rsc@golang.org>
Date:   Tue Mar 4 14:03:39 2014 -0500

    runtime: fix arm build (B not JMP)
    
    TBR=dvyukov
    CC=golang-codereviews
    https://golang.org/cl/71060046

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

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

元コミット内容

diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s
index aa171d7be9..3aed51f490 100644
--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -215,7 +215,7 @@ TEXT runtime·morestack(SB),NOSPLIT,$-4-0
 
 TEXT runtime·morestack_noctxt(SB),NOSPLIT,$-4-0
 	MOVW	$0, R7
-	JMP runtime·morestack(SB)
+	B runtime·morestack(SB)
 
 // Called from panic.  Mimics morestack,
 // reuses stack growth code to create a frame

変更の背景

このコミットの背景には、GoランタイムのARMアーキテクチャ向けビルドにおける問題がありました。Goランタイムは、スタックの拡張(morestack)やゴルーチンのスケジューリングなど、低レベルの処理にアセンブリコードを使用しています。特に、スタックが不足した場合に呼び出されるmorestack関数は、Goプログラムの安定性と効率性に不可欠です。

runtime·morestack_noctxt関数は、コンテキスト情報なしでruntime·morestackを呼び出すためのラッパーのような役割を果たしています。元のコードでは、この関数内でJMP命令を使用してruntime·morestackへジャンプしていました。しかし、ARMアーキテクチャにおけるJMP命令の挙動、またはGoのアセンブラがJMPをどのように解釈・変換するかに問題があった可能性があります。

具体的には、Goのアセンブラは、異なるアーキテクチャ間で共通のニーモニック(例: JMP)を提供しつつ、それを各アーキテクチャのネイティブな命令に変換します。ARMの場合、JMPは通常、PC(プログラムカウンタ)相対アドレス指定を用いた分岐命令に変換されます。しかし、特定の状況下でこの変換が正しく行われず、ビルドエラーや実行時エラーを引き起こしていたと考えられます。

このコミットは、JMP命令がARMビルドで問題を引き起こしていたため、より直接的でARMアーキテクチャに適した分岐命令であるB(Branch)に置き換えることで、この問題を解決することを目的としています。

前提知識の解説

Goランタイムとスタック管理

Go言語は、独自のランタイムを持っており、ガベージコレクション、スケジューリング、スタック管理などを担当しています。Goのゴルーチンは、必要に応じてスタックサイズを動的に拡張する「可変スタック」を採用しています。

  • runtime·morestack: Goランタイムにおいて、現在のゴルーチンのスタックが不足した場合に呼び出される重要な関数です。この関数は、新しい(より大きな)スタックフレームを割り当て、既存のスタックの内容を新しいスタックにコピーし、実行を再開する役割を担います。
  • runtime·morestack_noctxt: runtime·morestackを呼び出すための補助的な関数です。noctxtという名前が示す通り、コンテキスト情報(例: レジスタの状態など)を保存せずにmorestackを呼び出す必要がある場合に利用されます。これは、特定の低レベルな状況や、コンテキストの保存が不要または不適切である場合に用いられます。

ARMアセンブリ命令

ARMアーキテクチャは、RISC(Reduced Instruction Set Computer)ベースのプロセッサであり、そのアセンブリ言語は特定の命令セットを持っています。

  • JMP (Jump): 一般的なアセンブリ言語のニーモニックで、無条件ジャンプを意味します。Goのアセンブラでは、このJMPニーモニックは、ターゲットアーキテクチャ(この場合はARM)の適切な分岐命令に変換されます。通常、これはPC相対アドレス指定を用いた分岐命令(例: BBL)にマッピングされますが、その具体的な実装はアセンブラのバージョンやターゲットアーキテクチャのサブセットによって異なる場合があります。
  • B (Branch): ARMアセンブリにおける無条件分岐命令です。指定されたアドレスにプログラムカウンタ(PC)を直接設定し、実行フローをそのアドレスに移動させます。B命令は、関数呼び出しではなく、単なるジャンプ(gotoのようなもの)に使用されます。これは、PC相対オフセットを使用してターゲットアドレスを指定します。

Goのアセンブラ (Plan 9 Assembler)

Go言語は、独自のツールチェインの一部として、Plan 9アセンブラをベースにしたアセンブラを使用しています。このアセンブラは、一般的なx86やARMのアセンブラとは異なる独自の構文とセマンティクスを持っています。

  • 擬似命令: Goのアセンブラは、JMPのような擬似命令を提供することがあります。これは、複数のネイティブ命令に展開されたり、特定のアーキテクチャの慣習に合わせて最適化されたりする場合があります。
  • レジスタの命名規則: Goのアセンブラでは、レジスタの命名規則も標準的なARMアセンブリとは異なる場合があります(例: R0, R1など)。
  • シンボル解決: runtime·morestack(SB)のように、GoのランタイムシンボルはSB(Static Base)レジスタからのオフセットとして参照されます。これは、Goのリンカが最終的なアドレスを解決する方法に関連しています。

技術的詳細

このコミットの技術的な核心は、ARMアーキテクチャにおける分岐命令の選択とそのGoアセンブラでの解釈にあります。

元のコードでは、runtime·morestack_noctxt関数内でJMP runtime·morestack(SB)という命令が使用されていました。Goのアセンブラは、このJMPをARMのネイティブ命令に変換します。しかし、この変換がARMビルドにおいて問題を引き起こしていました。

考えられる問題点としては、以下の点が挙げられます。

  1. JMPの変換の複雑性: GoのアセンブラがJMPをARM命令に変換する際に、特定のコンテキスト(例: runtime·morestack_noctxtからruntime·morestackへのジャンプ)で、生成される命令が正しくない、または効率的でない可能性がありました。例えば、PC相対オフセットの計算が誤っていたり、命令のエンコーディングが特定のARMプロセッサのサブセットで問題を引き起こしたりする可能性です。
  2. PC相対分岐の範囲: ARMのB命令は、通常、比較的広い範囲のPC相対分岐をサポートしますが、JMPがより汎用的なニーモニックであるため、アセンブラがより複雑な(そして場合によっては問題のある)分岐シーケンスを生成していた可能性も考えられます。
  3. アセンブラのバグまたは制限: 最も直接的な原因として、Goのアセンブラ自体に、ARMアーキテクチャ向けにJMPを処理する際のバグや、特定のケースでの制限があった可能性が高いです。JMPは、Goのアセンブラが提供する高レベルな抽象化であり、その背後で複数のネイティブ命令に展開されることがあります。この展開プロセスがARMの特定のバージョンやコンパイラの最適化と競合していたのかもしれません。

JMP runtime·morestack(SB)B runtime·morestack(SB)に置き換えることで、この問題は解決されました。B命令はARMアーキテクチャのネイティブな無条件分岐命令であり、より直接的で予測可能な挙動をします。これにより、アセンブラがよりシンプルかつ正確な命令を生成できるようになり、ARMビルドの問題が解消されたと考えられます。

この変更は、Goランタイムの低レベルなアセンブリコードの正確性が、特定のアーキテクチャ(ARM)でのビルドの成功にどれほど重要であるかを示しています。また、Goのアセンブラが提供する抽象化(JMP)が、常にすべてのターゲットアーキテクチャで完璧に機能するわけではないという教訓も示唆しています。

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

変更はsrc/pkg/runtime/asm_arm.sファイルの一箇所のみです。

--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -215,7 +215,7 @@ TEXT runtime·morestack(SB),NOSPLIT,$-4-0
 
 TEXT runtime·morestack_noctxt(SB),NOSPLIT,$-4-0
 	MOVW	$0, R7
-	JMP runtime·morestack(SB)
+	B runtime·morestack(SB)

具体的には、runtime·morestack_noctxt関数の定義内で、MOVW $0, R7命令の直後に続くジャンプ命令が、JMP runtime·morestack(SB)からB runtime·morestack(SB)に変更されました。

コアとなるコードの解説

変更された行は、runtime·morestack_noctxt関数の一部です。

TEXT runtime·morestack_noctxt(SB),NOSPLIT,$-4-0
	MOVW	$0, R7
	B runtime·morestack(SB) // 変更された行
  • TEXT runtime·morestack_noctxt(SB),NOSPLIT,$-4-0: runtime·morestack_noctxtという名前の関数を定義しています。
    • SB: Static Base。Goのアセンブラにおけるグローバルシンボルを参照するためのベースレジスタのようなものです。
    • NOSPLIT: この関数がスタックの分割(stack split)を行わないことを示します。つまり、この関数自体がスタックの拡張をトリガーしないことを意味します。
    • $-4-0: スタックフレームのサイズと引数のサイズを示します。
  • MOVW $0, R7: レジスタR7に値0を移動(書き込み)します。ARMアーキテクチャでは、R7はシステムコール番号を保持するために使用されることがありますが、このコンテキストでの正確な目的は、Goランタイムの内部実装に依存します。しかし、この命令自体は今回のコミットの主要な変更点ではありません。
  • B runtime·morestack(SB): これが変更された命令です。
    • B: ARMの無条件分岐命令です。プログラムの実行フローを、指定されたラベル(この場合はruntime·morestack(SB))に直接ジャンプさせます。
    • runtime·morestack(SB): ジャンプ先のラベルです。これにより、runtime·morestack_noctxtの実行はruntime·morestack関数に引き継がれます。

この変更により、runtime·morestack_noctxtは、より直接的でARMアーキテクチャにネイティブなB命令を使用してruntime·morestackにジャンプするようになりました。これにより、GoのアセンブラがJMPをARM命令に変換する際に発生していた潜在的な問題が回避され、ARMビルドが正しく行われるようになりました。

関連リンク

  • Go言語の公式リポジトリ: https://github.com/golang/go
  • Go言語のスタック管理に関するドキュメント(Goのバージョンによって内容は異なる可能性があります):
  • ARMアーキテクチャのリファレンスマニュアル(ARMの公式ウェブサイトなどで入手可能)

参考にした情報源リンク

  • Go言語のソースコード(特にsrc/pkg/runtime/asm_arm.sの履歴)
  • ARMアーキテクチャのリファレンス(B命令と分岐命令に関する情報)
  • Goのアセンブラ(Plan 9 Assembler)に関するドキュメントや解説記事
  • Go言語のコミット履歴と関連するコードレビュー(https://golang.org/cl/71060046
  • Go言語のメーリングリストやIssueトラッカー(関連する議論やバグレポートがある場合)
  • 一般的なアセンブリ言語とコンパイラの原理に関する知識
  • Go言語のランタイムに関する技術ブログや論文