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

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

このコミットは、Go言語のツールチェインの一部である cmd/dist におけるARMアーキテクチャ向けのVFP (Vector Floating Point) 検出ロジックの改善に関するものです。特に、Thumb命令セットを使用する新しいARMツールチェインとの互換性を向上させることを目的としています。

コミット

commit c9eb6267dfdbda9e9704477322903688d72ae8dc
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Sat Dec 22 02:39:54 2012 +0800

    cmd/dist: make GOARM detection better compatible with thumb toolchain
    Fixes #4557.
    
    R=dave, rsc
    CC=golang-dev
    https://golang.org/cl/6946078

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

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

元コミット内容

src/cmd/dist/arm.c ファイルが変更されています。このファイルは、Goのビルドシステムの一部であり、ARMアーキテクチャの特定の機能(この場合はVFPのバージョン)を検出するために使用されます。

変更前は、VFPv3およびVFPv1の検出関数 useVFPv3()useVFPv1() の中で、それぞれ対応するVFP命令を直接インラインアセンブリで実行しようとしていました。

// useVFPv3関数内 (変更前)
__asm__ __volatile__ (".word 0xeeb70b00"); // vmov.f64 d0, #112

// useVFPv1関数内 (変更前)
__asm__ __volatile__ (".word 0xeeb00b40"); // vmov.f64 d0, d0

これらの命令は、コンパイラがソフトフロート(浮動小数点演算をソフトウェアでエミュレートする)ツールチェインを使用している場合でも、VFPハードウェアの存在をテストするために直接実行されていました。

変更の背景

このコミットの背景には、ARMアーキテクチャにおけるツールチェインの進化と、それに伴うGoのビルドシステムでの互換性の問題があります。

  1. Thumbツールチェインの普及: ARMプロセッサは、標準のARM命令セット(32ビット固定長)と、よりコード密度が高いThumb命令セット(16ビットまたは32ビット可変長)の両方をサポートしています。新しいツールチェインの中には、デフォルトでThumbモードでコードを生成するように設定されているものがありました。
  2. アセンブリコードの実行モード: Goの cmd/dist は、ARMプロセッサのVFP機能を検出するために、インラインアセンブリで特定のVFP命令を実行していました。しかし、ツールチェインがThumbモードで動作している場合、これらのARM命令を直接実行しようとすると問題が発生する可能性がありました。ARMモードとThumbモードの間で切り替えるメカニズムが必要でした。
  3. bx pc の警告: ARMモードからThumbモード、またはその逆への切り替えには、通常 BX (Branch and Exchange) 命令が使用されます。特に bx pc (Program Counter) は、現在の実行モードを切り替える一般的な方法です。しかし、GNUアセンブラ (as(1)) の一部のバージョンでは、ARMモードで bx pc を使用すると「use of r15 in bx in ARM mode is not really useful」という警告を出すことがありました。これは、pc (r15) を bx のターゲットとして使うことが、ARMモードでは通常モード切り替え以外の目的ではあまり意味がないというアセンブラの判断によるものです。この警告はビルドプロセスを妨げる可能性がありました。
  4. Issue #4557: コミットメッセージに「Fixes #4557」と記載されています。これは、GoのIssueトラッカーで報告された特定のバグや問題に対応するものであることを示唆しています。この問題は、おそらくThumbツールチェインを使用した場合に GOARM の検出が正しく行われない、またはビルド時に警告が発生するといった内容だったと推測されます。

これらの背景から、Goのビルドシステムがより広範なARMツールチェイン環境で安定して動作するように、VFP検出ロジックを修正する必要がありました。

前提知識の解説

このコミットを理解するためには、以下の技術的な概念を理解しておく必要があります。

  1. ARMアーキテクチャ:

    • ARM命令セット: 32ビット固定長の命令セットで、高性能な実行に適しています。
    • Thumb命令セット: ARMv4T以降で導入された16ビット固定長の命令セット(Thumb-2では32ビット命令も追加)。コード密度が高く、メモリ使用量を削減できます。
    • ARMモードとThumbモード: ARMプロセッサは、ARMモードとThumbモードの2つの実行状態を持っています。モード間で切り替えることで、両方の命令セットの利点を活用できます。
    • VFP (Vector Floating Point): ARMプロセッサのオプションのコプロセッサで、浮動小数点演算をハードウェアで高速化します。VFPv1、VFPv3などのバージョンがあります。Goの cmd/dist は、このVFPの有無とバージョンを検出して、適切なGoのランタイムをビルドするために使用します。
    • GOARM 環境変数: Goのビルドシステムで使用される環境変数で、ARMプロセッサのバージョンやVFPの有無など、特定のARM機能のサポートレベルを指定します。これにより、GoのコンパイラとランタイムがターゲットARMプロセッサに最適化されます。
  2. インラインアセンブリ:

    • C言語のコード内に直接アセンブリ言語の命令を記述する機能です。GCC (GNU Compiler Collection) では __asm__ __volatile__ 構文が使用されます。
    • __volatile__: コンパイラによる最適化(命令の並べ替えや削除など)を防ぐためのキーワードです。
  3. ARMアセンブリ命令:

    • MOV R0, PC: PC (Program Counter、プログラムカウンタ) の現在値をレジスタ R0 にコピーする命令です。PC は現在実行中の命令のアドレスを指します。
    • BX Rn (Branch and Exchange): レジスタ Rn の値に分岐し、同時に実行モードを切り替える命令です。Rn の最下位ビットが1であればThumbモードに、0であればARMモードに切り替わります。
    • BX PC: PC の値に分岐し、モードを切り替えます。これは、現在の PC の値に基づいてモードを切り替えるために使用されることがあります。
    • BX LR (Branch and Exchange Link Register): LR (Link Register) の値に分岐し、モードを切り替えます。LR は通常、関数呼び出し時に戻りアドレスを保持するために使用されます。この命令は、関数から戻る際にモードを切り替える目的で使われることがあります。
    • .word 0x...: アセンブラディレクティブで、指定された16進数の値を直接メモリに配置します。これは、特定の命令のバイナリ表現を直接埋め込むために使用されます。このコミットでは、VFP命令のバイナリコードを直接埋め込んでいます。
      • 0xeeb70b00: vmov.f64 d0, #112 (VFPv3命令)
      • 0xeeb00b40: vmov.f64 d0, d0 (VFPv1命令)
      • 0xe12fff1e: bx lr (ARMモードの bx lr 命令のバイナリ表現)
  4. GNUアセンブラ (as(1)):

    • GNU Binutilsの一部であるアセンブラです。アセンブリ言語のソースコードをオブジェクトファイルに変換します。
    • アセンブラは、コードの構文チェックや、特定の命令の使用方法に関する警告を生成することがあります。

技術的詳細

このコミットの技術的な核心は、ARMモードとThumbモード間の安全なモード切り替えをインラインアセンブリで実現し、かつGNUアセンブラの警告を回避することにあります。

Goの cmd/dist は、ARMプロセッサ上でVFPハードウェアが利用可能かどうかを検出するために、VFP命令を実際に実行しようとします。もしVFPハードウェアが存在しない場合、これらの命令はトラップ(例外)を引き起こし、Goのランタイムはその情報を利用してVFPが利用できないと判断します。

問題は、新しいツールチェインがデフォルトでThumbモードでコードを生成するようになったことです。VFP命令はARM命令セットの一部であり、Thumbモードで直接実行することはできません。そのため、VFP命令を実行する前にARMモードに切り替える必要があります。

元のコードでは、単にVFP命令のバイナリ表現を .word ディレクティブで埋め込んでいました。これは、コンパイラがソフトフロートツールチェインを使用している場合でも、ハードウェアのVFPサポートをテストするためのハックでした。しかし、Thumbモードのコンテキストでは、この直接的なアプローチは機能しません。

新しいコードでは、以下の「モード切り替えマジック」が導入されています。

  1. mov r0, pc: 現在のプログラムカウンタ (pc) の値をレジスタ r0 にコピーします。pc は通常、現在の命令の次の命令のアドレスを指しますが、ARMのパイプラインの性質上、正確な値は実装に依存します。重要なのは、この命令がARMモードで実行されていることです。
  2. bx r0: r0 の値に分岐し、同時にモードを切り替えます。r0 には pc の値がコピーされているため、実質的には現在の実行パスを継続しつつ、モードを切り替えることができます。pc の最下位ビットは常に0であるため、この bx r0 はARMモードへの切り替え(またはARMモードの維持)を保証します。
    • この bx r0 は、元のコメントで言及されている bx pc; nop の代替です。bx pc はモード切り替えの一般的な方法ですが、GNUアセンブラが警告を出すため、mov r0, pcbx r0 の組み合わせで回避しています。
  3. VFP命令の実行: ARMモードに切り替わった後、目的のVFP命令 (.word 0xeeb70b00 または .word 0xeeb00b40) が実行されます。これにより、VFPハードウェアの有無が正しく検出されます。
  4. bx lr: VFP命令の実行後、bx lr 命令が追加されています。これは、関数から戻る際に、呼び出し元のモード(おそらくThumbモード)に戻るためのものです。lr (Link Register) には通常、関数呼び出し時の戻りアドレスが格納されており、そのアドレスの最下位ビットがモード情報(Thumbの場合は1、ARMの場合は0)を含んでいます。これにより、VFP検出コードが実行された後、元の実行モードにスムーズに戻ることができます。

この一連の命令シーケンスにより、Goのビルドシステムは、Thumbモードをデフォルトとする新しいARMツールチェイン環境でも、VFPハードウェアの検出を正確かつ警告なしに行えるようになりました。

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

変更は src/cmd/dist/arm.c ファイルの以下の2つの関数に集中しています。

  1. useVFPv3() 関数
  2. useVFPv1() 関数

それぞれの関数で、VFP命令を実行するインラインアセンブリブロックが修正されています。

src/cmd/dist/arm.c の差分:

--- a/src/cmd/dist/arm.c
+++ b/src/cmd/dist/arm.c
@@ -29,8 +29,19 @@ useVFPv3(void)
  {
  	// try to run VFPv3-only "vmov.f64 d0, #112" instruction
  	// we can't use that instruction directly, because we
- 	// might be compiling with a soft-float only toolchain
- 	__asm__ __volatile__ (".word 0xeeb70b00");
+ 	// might be compiling with a soft-float only toolchain.
+ 	//
+ 	// some newer toolchains are configured to use thumb
+ 	// by default, so we need to do some mode changing magic
+ 	// here.
+ 	// We can use "bx pc; nop" here, but GNU as(1) insists
+ 	// on warning us
+ 	// "use of r15 in bx in ARM mode is not really useful"
+ 	// so we workaround that by using "bx r0"
+ 	__asm__ __volatile__ ("mov r0, pc");
+ 	__asm__ __volatile__ ("bx r0");
+ 	__asm__ __volatile__ (".word 0xeeb70b00"); // vmov.f64 d0, #112
+ 	__asm__ __volatile__ (".word 0xe12fff1e"); // bx lr
  }
  
  static void
@@ -39,7 +50,18 @@ useVFPv1(void)
  	// try to run "vmov.f64 d0, d0" instruction
  	// we can't use that instruction directly, because we
  	// might be compiling with a soft-float only toolchain
- 	__asm__ __volatile__ (".word 0xeeb00b40");
+ 	//
+ 	// some newer toolchains are configured to use thumb
+ 	// by default, so we need to do some mode changing magic
+ 	// here.
+ 	// We can use "bx pc; nop" here, but GNU as(1) insists
+ 	// on warning us
+ 	// "use of r15 in bx in ARM mode is not really useful"
+ 	// so we workaround that by using "bx r0"
+ 	__asm__ __volatile__ ("mov r0, pc");
+ 	__asm__ __volatile__ ("bx r0");
+ 	__asm__ __volatile__ (".word 0xeeb00b40"); // vomv.f64 d0, d0
+ 	__asm__ __volatile__ (".word 0xe12fff1e"); // bx lr
  }
  
  #endif

コアとなるコードの解説

useVFPv3()useVFPv1() の両関数において、VFP命令の実行前後に以下のインラインアセンブリ命令が追加されています。

__asm__ __volatile__ ("mov r0, pc");
__asm__ __volatile__ ("bx r0");
// ここに元のVFP命令 (.word 0x...)
__asm__ __volatile__ (".word 0xe12fff1e"); // bx lr
  1. __asm__ __volatile__ ("mov r0, pc");:

    • この命令は、現在のプログラムカウンタ (pc) の値を汎用レジスタ r0 にコピーします。
    • 目的は、bx 命令のターゲットとして pc の値を使用することです。
  2. __asm__ __volatile__ ("bx r0");:

    • この命令は、r0 に格納されているアドレスに分岐し、同時に実行モードを切り替えます。
    • r0 には pc の値がコピーされているため、この命令は実質的に現在の実行フローを継続しつつ、ARMモードへの切り替え(またはARMモードの維持)を行います。pc の最下位ビットは常に0であるため、bx r0 はARMモードへの切り替えを保証します。
    • このシーケンスは、GNUアセンブラが bx pc に対して出す警告を回避するためのワークアラウンドです。
  3. // ここに元のVFP命令 (.word 0x...):

    • useVFPv3() の場合は ".word 0xeeb70b00" (VFPv3の vmov.f64 d0, #112)。
    • useVFPv1() の場合は ".word 0xeeb00b40" (VFPv1の vmov.f64 d0, d0)。
    • これらの命令は、上記のモード切り替えによってARMモードで実行されるため、VFPハードウェアの有無を正しくテストできます。
  4. __asm__ __volatile__ (".word 0xe12fff1e"); // bx lr:

    • 0xe12fff1e は、ARMモードの bx lr 命令のバイナリ表現です。
    • この命令は、VFP命令の実行後、関数から戻る際に、呼び出し元の実行モード(通常はThumbモード)に戻るために使用されます。lr (Link Register) には、関数呼び出し時の戻りアドレスと、そのアドレスが指す命令のモード情報(最下位ビット)が含まれています。これにより、VFP検出コードが実行された後、元のモードにスムーズに復帰できます。

これらの変更により、Goのビルドシステムは、Thumbモードをデフォルトとする新しいARMツールチェイン環境でも、VFPハードウェアの検出を正確かつ警告なしに行えるようになり、GOARM の検出の互換性が向上しました。

関連リンク

参考にした情報源リンク

  • ARMアーキテクチャリファレンスマニュアル (ARMモード、Thumbモード、BX命令などに関する詳細情報)
  • GNUアセンブラ (as) のドキュメント (インラインアセンブリ、ディレクティブ、警告などに関する情報)
  • Go言語のIssueトラッカー (Issue #4557に関する詳細情報、ただし今回の検索では直接見つからなかったため、一般的な情報源として記載)
  • VFP (Vector Floating Point) の仕様に関するドキュメント