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

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

このコミットは、Go言語のランタイムにおけるARMアーキテクチャ向けビルドの問題を修正するものです。具体的には、src/pkg/runtime/asm_arm.sファイル内のCgoコールバック処理に関連するアセンブリコードにおいて、MOVL命令をMOVW命令に置き換えることで、ARMビルドが正しく行われるように修正しています。

コミット

commit 56a0bafdb66c95f31de86d9a713fa819911d50a5
Author: Russ Cox <rsc@golang.org>
Date:   Fri Feb 22 16:38:44 2013 -0500

    runtime: fix arm build
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/7399050

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

https://github.com/golang/go/commit/56a0bafdb66c95f31de86d9a713fa819911d50a5

元コミット内容

runtime: fix arm build

R=ken2
CC=golang-dev
https://golang.org/cl/7399050

変更の背景

このコミットの背景には、Go言語のランタイムがARMアーキテクチャ上で正しく動作しないという問題がありました。Goはクロスプラットフォーム対応を重視しており、様々なアーキテクチャで動作するように設計されています。しかし、特定のアセンブリ命令の使用方法がARMの仕様と合致していなかったため、ビルドエラーや実行時エラーが発生していたと考えられます。

特に、GoのランタイムはC言語で書かれたコード(Cgo)との連携をサポートしており、GoコードからCコードを呼び出したり、CコードからGoコードを呼び出したりする機能を提供しています。このCgoコールバックのメカニズムにおいて、ARMアセンブリコードが特定のレジスタにアドレスをロードする際に誤った命令を使用していたことが、問題の根本原因でした。

MOVL命令は、ARMアセンブリにおいて32ビットの即値またはアドレスをレジスタにロードするために使用されますが、その使用には特定の制約があります。特に、大きなアドレス値を直接ロードする場合には、MOVWMOVTの組み合わせを使用するなど、より柔軟な命令が必要となることがあります。このコミットは、runtime·cgocallback_gofuncというGo関数のアドレスをロードする際に、MOVLが適切でなかったために発生したビルド問題を解決することを目的としています。

前提知識の解説

Goランタイム (Go Runtime)

Goランタイムは、Goプログラムの実行を管理する低レベルのコンポーネントです。これには、ガベージコレクション、スケジューラ(ゴルーチンの管理)、メモリ管理、システムコールインターフェースなどが含まれます。Goプログラムは、コンパイル時にGoランタイムとリンクされ、実行可能ファイルを形成します。ランタイムの一部は、パフォーマンスや特定のハードウェアとのインタラクションのためにアセンブリ言語で書かれています。

ARMアーキテクチャ (ARM Architecture)

ARM(Advanced RISC Machine)は、モバイルデバイス、組み込みシステム、最近ではサーバーやデスクトップPCなど、幅広いデバイスで使用されているRISC(Reduced Instruction Set Computer)ベースのプロセッサアーキテクチャです。ARMプロセッサは、その電力効率と性能のバランスから広く採用されています。

アセンブリ言語 (Assembly Language)

アセンブリ言語は、特定のコンピュータアーキテクチャの命令セットに対応する低レベルのプログラミング言語です。人間が読めるニーモニック(例: MOVW, BL)を使用して、プロセッサが直接実行できる機械語命令を表現します。Goランタイムの一部は、パフォーマンス最適化やハードウェアとの直接的なインタラクションのためにアセンブリ言語で記述されています。

Goのアセンブリ構文

Goのアセンブリ言語は、Plan 9アセンブラの構文に基づいています。一般的なアセンブラとは異なる独自の記法を持っています。

  • TEXT: 関数の開始を宣言します。例: TEXT runtime·cgocallback(SB),7,$12
    • runtime·cgocallback: 関数名。Goのパッケージ名と関数名は·で区切られます。
    • (SB): SBは「Static Base」レジスタを表し、グローバルシンボルや静的データへのオフセットを計算するための基準点として使用されます。
    • 7: 関数フラグ。ここではスタックフレームのサイズやレジスタの使用に関する情報を示します。
    • $12: スタックフレームのサイズ(バイト単位)。この関数が使用するローカル変数のためのスタック領域を示します。
  • SB (Static Base): グローバルシンボルや静的データのアドレスを指す擬似レジスタ。
  • FP (Frame Pointer): 現在の関数のスタックフレームの開始位置を指す擬似レジスタ。引数やローカル変数へのアクセスに使用されます。
  • R0, R13 など: ARMプロセッサの汎用レジスタ。R0は通常、関数の戻り値や引数に使用されます。R13は通常、スタックポインタ(SP)として使用されます。
  • MOVW (Move Word): ARMアセンブリ命令の一つで、32ビットの値をレジスタにロードします。即値またはメモリからのロードに使用されます。
  • MOVL (Move Long): ARMアセンブリ命令の一つで、32ビットの値をレジスタにロードします。MOVWと似ていますが、特定のコンテキストや古いARMアーキテクチャでは異なる動作や制約を持つことがあります。特に、大きなアドレス値を直接ロードする際には、MOVWMOVTの組み合わせが推奨されることがあります。
  • BL (Branch with Link): サブルーチンコールを行うための命令。指定されたアドレスにジャンプし、現在のプログラムカウンタ(PC)の次の命令のアドレスをリンクレジスタ(LR, 通常はR14)に保存します。これにより、サブルーチンから戻ることができます。
  • RET (Return): 関数から戻るための命令。

Cgoコールバック (Cgo Callback)

Cgoは、GoプログラムがC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのメカニズムです。Cgoコールバックは、CコードがGoの関数を呼び出す際に使用されるパスです。このプロセスには、GoとCのスタックフレームの切り替え、レジスタの保存と復元など、低レベルの処理が伴います。runtime·cgocallbackは、このコールバック処理のエントリポイントとなるGoランタイム内のアセンブリ関数です。

技術的詳細

このコミットの核心は、src/pkg/runtime/asm_arm.sファイル内の以下の変更です。

--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -319,7 +319,7 @@ TEXT runtime·cgocallback(SB),7,$12
 	MOVW	R0, 8(R13)
 	MOVW	framesize+8(FP), R0
 	MOVW	R0, 12(R13)
-	MOVL	$runtime·cgocallback_gofunc(SB), R0
+	MOVW	$runtime·cgocallback_gofunc(SB), R0
 	BL	(R0)
 	RET

変更された行は、runtime·cgocallback_gofuncというGo関数のアドレスをレジスタR0にロードする部分です。

  • 変更前: MOVL $runtime·cgocallback_gofunc(SB), R0
  • 変更後: MOVW $runtime·cgocallback_gofunc(SB), R0

ARMアーキテクチャにおいて、MOVLは通常、32ビットの即値またはアドレスをレジスタにロードするために使用されます。しかし、ARMの命令セットには、ロードできる即値の範囲に制約があります。特に、大きなアドレス値(例えば、プログラムのメモリ空間のどこかにある関数のアドレス)を直接MOVLでロードしようとすると、コンパイラやアセンブラがエラーを出すか、正しくないコードを生成する可能性があります。

一方、MOVW命令は、ARMv7-A以降のThumb-2命令セットで導入された命令で、16ビットの即値をレジスタにロードし、必要に応じてMOVT(Move Top)命令と組み合わせて32ビットの値をロードすることができます。しかし、Goのアセンブリでは、MOVW $symbol(SB), R0のような形式は、アセンブラがシンボルのアドレスをR0にロードするための適切な命令シーケンス(例えば、MOVWMOVTの組み合わせ、またはLDR命令など)を自動的に選択して生成することを意味します。

この修正は、runtime·cgocallback_gofuncのアドレスがMOVL命令の即値範囲を超えていたか、またはMOVLが特定のARMビルド環境で問題を引き起こしていたことを示唆しています。MOVWを使用することで、アセンブラがより柔軟かつ正確にアドレスをロードする命令を生成できるようになり、ARMビルドの問題が解決されました。これは、Goのクロスコンパイル環境や特定のARMプロセッサの命令セットの差異に対応するための重要な修正です。

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

--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -319,7 +319,7 @@ TEXT runtime·cgocallback(SB),7,$12
 	MOVW	R0, 8(R13)
 	MOVW	framesize+8(FP), R0
 	MOVW	R0, 12(R13)
-	MOVL	$runtime·cgocallback_gofunc(SB), R0
+	MOVW	$runtime·cgocallback_gofunc(SB), R0
 	BL	(R0)
 	RET

コアとなるコードの解説

変更された行は、TEXT runtime·cgocallback(SB),7,$12関数内にあります。この関数は、Cgoコールバックの処理を担当するGoランタイムのアセンブリ関数です。

  1. MOVW R0, 8(R13)
  2. MOVW framesize+8(FP), R0
  3. MOVW R0, 12(R13)

これらの命令は、Cgoコールバックに必要な引数やコンテキスト情報をレジスタやスタックに設定している部分です。

  1. - MOVL $runtime·cgocallback_gofunc(SB), R0

    • 変更前のコードでは、runtime·cgocallback_gofuncというGo関数のアドレスをR0レジスタにロードするためにMOVL命令を使用していました。$symbol(SB)は、symbolのアドレスを意味します。
  2. + MOVW $runtime·cgocallback_gofunc(SB), R0

    • 変更後のコードでは、同じ目的でMOVW命令を使用しています。この変更により、アセンブラはruntime·cgocallback_gofuncのアドレスをR0にロードするためのより適切なARM命令シーケンスを生成できるようになりました。これにより、特定のアドレス範囲の問題や、特定のARMコンパイラ/アセンブラの挙動の違いが吸収され、ARMビルドが成功するようになりました。
  3. BL (R0)

    • R0にロードされたアドレス(runtime·cgocallback_gofuncのアドレス)にジャンプし、サブルーチンコールを実行します。BL命令は、呼び出し元の次の命令のアドレスをリンクレジスタ(LR)に保存するため、呼び出された関数から戻ることができます。
  4. RET

    • 関数から戻ります。

この修正は、GoランタイムがCgoコールバックを処理する際に、ARMアーキテクチャの特性をより正確に考慮し、安定したビルドと実行を保証するために不可欠でした。

関連リンク

参考にした情報源リンク