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

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

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

コミット

  • コミットハッシュ: bd6404a4cc90aec9c9599ae97cd244dc09088588
  • 作者: Russ Cox rsc@golang.org
  • 日付: 2012年3月15日 木曜日 17:40:17 -0400
  • コミットメッセージ:
    runtime: fix arm build
    
    TBR=golang-dev
    CC=golang-dev
    https://golang.org/cl/5832047
    

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

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

元コミット内容

runtime: fix arm build

TBR=golang-dev
CC=golang-dev
https://golang.org/cl/5832047

変更の背景

このコミットは、Go言語のランタイムがARMアーキテクチャ上で正しくビルドされない問題を修正するために行われました。Go言語はクロスプラットフォーム対応を目指しており、ARMのような異なるCPUアーキテクチャ上でも安定して動作する必要があります。ビルドエラーは、特定のアーキテクチャ向けのアセンブリコードがそのアーキテクチャの命令セットやアセンブラの挙動と合致していない場合に発生します。このケースでは、MOVL命令の使用がARMビルドの問題を引き起こしていたと考えられます。

前提知識の解説

ARMアセンブリ言語

ARMアセンブリ言語は、ARMアーキテクチャのプロセッサが直接実行できる機械語命令を人間が読める形式で記述したものです。低レベルのプログラミングや、OSのカーネル、組み込みシステム、高性能なライブラリなどで使用されます。レジスタ、メモリ操作、分岐命令など、CPUの基本的な操作を直接制御します。

MOVWMOVL命令の違い (ARMアセンブリ)

ARMアセンブリには、値をレジスタに移動させるためのいくつかの命令があります。このコミットで焦点となっているのは、MOVWMOVLです。

  • MOVW (Move Wide):

    • これは具体的なARM命令であり、16ビットの即値(immediate value)を下位16ビットにロードし、上位16ビットをゼロクリアして32ビットレジスタに格納します。
    • 通常、MOVT (Move Top) 命令と組み合わせて使用され、完全な32ビットの即値をレジスタにロードします。MOVWで下位16ビットをロードした後、MOVTで上位16ビットをロードし、下位ビットには影響を与えません。

    例:

    MOVW R0, #0x1234   ; R0 は 0x00001234 になる
    MOVT R0, #0x5678   ; R0 は 0x56781234 になる (上位16ビットが更新され、下位16ビットは変更なし)
    
  • MOVL (Move Literal / Pseudo-instruction):

    • これは通常、ARMアセンブラが提供する擬似命令 (pseudo-instruction) です。つまり、単一の直接的な機械語命令ではなく、プログラマの便宜のために提供されるものです。アセンブラはMOVLを1つ以上の実際の機械語命令に変換します。
    • その主な役割は、32ビットまたは64ビットの即値、あるいはアドレス(PC相対アドレスや外部アドレスなど)をレジスタにロードすることです。これは、即値が単一のMOV命令に直接エンコードするには大きすぎる場合に特に役立ちます。
    • アセンブラは、32ビット値の場合、MOVWMOVTのペアなど、最も効率的な命令シーケンスを自動的に決定して展開します。

    例:

    MOVL R0, #0x12345678 ; アセンブラはこれを以下のようなシーケンスに変換する:
                         ; MOVW R0, #0x5678
                         ; MOVT R0, #0x1234
    

重要な違い: MOVWは具体的な命令であり、レジスタの下位16ビットをロードし、上位16ビットをゼロクリアします。一方、MOVLは擬似命令であり、アセンブラが適切な命令シーケンス(多くの場合MOVWMOVTの組み合わせ)に展開して、指定された値をレジスタ全体にロードします。

技術的詳細

このコミットは、GoランタイムのARMアセンブリコードにおけるruntime·stackguard関数内の命令の誤用を修正しています。

runtime·stackguard関数は、Goのランタイムにおいてスタックの境界チェックに関連する重要な役割を担っています。スタックオーバーフローを防ぐために、現在のスタックポインタ(R13レジスタに格納されていることが多い)と、スタックガード(g_stackguard(g)、現在のgoroutineのスタックガード値)を比較する必要があります。

元のコードでは、これらの値をレジスタにロードするためにMOVL命令を使用していました。しかし、MOVLは擬似命令であり、アセンブラがどのように展開するかはコンテキストに依存します。特に、MOVLが単一のMOV命令に展開される場合、それは32ビット値を完全にロードするのではなく、特定のビットパターンをロードする可能性があります。

この問題は、MOVLR13(スタックポインタ)やg_stackguard(g)のような32ビットの完全なアドレスまたは値をロードする際に、意図しない挙動を引き起こした可能性が考えられます。特に、MOVWが下位16ビットをロードし、上位16ビットをゼロクリアするという特性を考えると、もしMOVLが内部的にMOVWに展開され、かつMOVTがそれに続かない場合、レジスタの上位ビットが意図せずゼロになってしまい、スタックポインタやスタックガードの値が正しく比較されなくなる可能性があります。

この修正では、MOVLMOVWに置き換えることで、この問題を解決しています。MOVWは16ビットの即値をロードする命令ですが、このコンテキストでは、R13g_stackguard(g)が指すアドレスや値が、MOVWで処理できる範囲内であったか、あるいはアセンブラがMOVWをより適切に処理するようになったか、またはこの特定の場所では上位ビットがゼロクリアされても問題ない(例えば、比較対象が下位16ビットのみで十分な場合や、後続の命令で上位ビットが適切に設定される場合)といった理由が考えられます。

しかし、一般的には32ビットレジスタに完全な32ビット値をロードする際にはMOVWMOVTのペアを使用するのが一般的です。このコミットがMOVWのみに変更していることから、この特定のruntime·stackguard関数内のMOVLの使用が、ARMビルドにおいて予期せぬレジスタ値の破損を引き起こし、MOVWにすることでその問題が回避されたと推測されます。これは、アセンブラの特定のバージョンや、GoのランタイムがARM上でどのようにスタックを管理しているかという詳細に依存する可能性があります。

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

diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s
index 9c36ba0d69..423fda7a0c 100644
--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -315,8 +315,8 @@ casfail:
  	RET
 
  TEXT runtime·stackguard(SB),7,$0
-	MOVL	R13, R1
-	MOVL	g_stackguard(g), R2
-	MOVL	R1, sp+0(FP)
-	MOVL	R2, limit+4(FP)
+	MOVW	R13, R1
+	MOVW	g_stackguard(g), R2
+	MOVW	R1, sp+0(FP)
+	MOVW	R2, limit+4(FP)
  	RET

コアとなるコードの解説

変更されたのは、src/pkg/runtime/asm_arm.s ファイル内の runtime·stackguard 関数です。

元のコード:

 TEXT runtime·stackguard(SB),7,$0
	MOVL	R13, R1
	MOVL	g_stackguard(g), R2
	MOVL	R1, sp+0(FP)
	MOVL	R2, limit+4(FP)
 	RET

修正後のコード:

 TEXT runtime·stackguard(SB),7,$0
	MOVW	R13, R1
	MOVW	g_stackguard(g), R2
	MOVW	R1, sp+0(FP)
	MOVW	R2, limit+4(FP)
 	RET

この変更は、MOVL命令をすべてMOVW命令に置き換えています。

  1. MOVL R13, R1 から MOVW R13, R1:

    • R13は通常、スタックポインタ(SP)レジスタとして使用されます。この命令は、スタックポインタの値をR1レジスタに移動しようとしています。
    • MOVLからMOVWへの変更は、R13の値をR1にロードする際の挙動を修正します。これにより、R1にスタックポインタの正しい値が確実にロードされるようになります。
  2. MOVL g_stackguard(g), R2 から MOVW g_stackguard(g), R2:

    • g_stackguard(g)は、現在のgoroutine (g) のスタックガード値(スタックの限界を示す値)を指します。この命令は、その値をR2レジスタに移動しようとしています。
    • 同様に、MOVLからMOVWへの変更により、R2にスタックガードの正しい値がロードされるようになります。
  3. MOVL R1, sp+0(FP)MOVL R2, limit+4(FP) から MOVW R1, sp+0(FP)MOVW R2, limit+4(FP):

    • これらの命令は、R1R2にロードされた値を、フレームポインタ(FP)からのオフセットで示されるスタック上の特定のメモリ位置に格納しています。sp+0(FP)limit+4(FP)は、おそらくスタックフレーム内のローカル変数や引数の位置を示しています。
    • ここでもMOVLからMOVWへの変更が行われていますが、これは前の2つのロード命令の修正と一貫性を保つため、またはストア命令の挙動がロード命令の挙動に依存するためと考えられます。

全体として、この修正は、ARMアセンブリにおけるMOVL擬似命令の特定のコンテキストでの挙動が、Goランタイムのスタックガードチェックに必要な正確な値のロードを妨げていた問題を解決しています。MOVWに切り替えることで、レジスタにロードされる値の正確性が保証され、ARMビルドの問題が解消されたと考えられます。

関連リンク

参考にした情報源リンク