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

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

このコミットは、Go言語のARMアーキテクチャ向けランタイムにおいて、VFPv3 (Vector Floating-Point version 3) のハードウェアサポートが不足している環境で、GOARM=7 でビルドされたバイナリが実行された場合に、実行時エラーでアボートするように変更を加えるものです。これにより、VFPv3の最適化を利用するバイナリが、対応していないハードウェアで誤って実行されることを防ぎ、ユーザーに適切なフィードバックを提供します。

コミット

commit 55ca5ab0be6da228c891d04ffd448ce9741d47e9
Author: Dave Cheney <dave@cheney.net>
Date:   Tue Sep 18 09:55:07 2012 +1000

    runtime: arm: abort if VFPv3 support missing
    
    Fixes #3456.
    
    This proposal is a reformulation of CL 5987063. This CL resets
    the default GOARM value to 6 and allows the use of the VFPv3
    optimisation if GOARM=7. Binaries built with this CL in place
    will abort if GOARM=7 was used and the target host does not
    support VFPv3.
    
    R=minux.ma, rsc, ajstarks
    CC=golang-dev
    https://golang.org/cl/6501099

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

https://github.com/golang/go/commit/55ca5ab0be6da228c891d04ffd448ce9741d47e9

元コミット内容

    runtime: arm: abort if VFPv3 support missing
    
    Fixes #3456.
    
    This proposal is a reformulation of CL 5987063. This CL resets
    the default GOARM value to 6 and allows the use of the VFPv3
    optimisation if GOARM=7. Binaries built with this CL in place
    will abort if GOARM=7 was used and the target host does not
    support VFPv3.

変更の背景

Go言語は、異なるアーキテクチャやオペレーティングシステム向けにコンパイルできるクロスコンパイル機能を強力にサポートしています。ARMアーキテクチャは、組み込みシステムからモバイルデバイスまで幅広く利用されており、そのバージョンによって浮動小数点演算ユニット (FPU) のサポート状況が異なります。

このコミットが行われた当時、GoのARM向けコンパイルにおいて、GOARM環境変数を用いてターゲットのARMアーキテクチャバージョンを指定していました。GOARM=7はVFPv3という特定の浮動小数点ユニットの最適化を利用することを意図していましたが、すべてのARMv7プロセッサがVFPv3を搭載しているわけではありませんでした。

問題は、GOARM=7でビルドされたバイナリが、VFPv3ハードウェアサポートを持たないARMデバイス上で実行された場合に、予期せぬ動作やクラッシュを引き起こす可能性があったことです。これは、コンパイラがVFPv3の命令セットを利用してコードを生成する一方で、実行環境がその命令を解釈・実行できないためです。

このコミットの目的は、このような不整合を検出し、VFPv3サポートがない環境でGOARM=7バイナリが実行された場合に、明確なエラーメッセージとともにプログラムを終了させることで、デバッグを容易にし、ユーザーエクスペリエンスを向上させることにありました。また、デフォルトのGOARM値を6にリセットすることで、より広範なARMデバイスでの互換性を確保しつつ、GOARM=7を選択した場合にのみVFPv3最適化を有効にするという明確なポリシーを導入しています。

前提知識の解説

GOARM 環境変数

GOARMはGo言語のクロスコンパイル時に使用される環境変数で、ターゲットとなるARMプロセッサのアーキテクチャバージョンを指定します。これにより、GoコンパイラはターゲットのARMプロセッサに最適な命令セット(特に浮動小数点演算関連)を使用してコードを生成します。

  • GOARM=5: ARMv5などの古いARMアーキテクチャをターゲットにします。これらのアーキテクチャは通常、ハードウェア浮動小数点サポートを持たないため、Goリンカはソフトウェア浮動小数点エミュレータ (_sfloat) を含めて浮動小数点演算を処理します。
  • GOARM=6: ARMv6命令セットをターゲットにします。Raspberry Pi Zeroなどのデバイスで利用されます。
  • GOARM=7: ARMv7などの新しいARMアーキテクチャをターゲットにします。スマートフォン、タブレット、Raspberry Pi 3/4などで広く使用されています。この設定では、通常、ハードウェア浮動小数点ユニット(VFPv3など)の最適化が利用されます。

GOARMが設定されていない場合、Goコンパイラは広範な互換性のために最小限のARMバージョンをターゲットとします。Go 1.22以降では、GOARMの値に ,softfloat または ,hardfloat を追加して、浮動小数点の実装を明示的に選択できるようになっています。

VFPv3 (Vector Floating-Point version 3)

VFPv3は、ARMプロセッサ向けのオプションの浮動小数点ユニット (FPU) 拡張機能です。IEEE 754標準に準拠した単精度および倍精度浮動小数点演算のハードウェアサポートを提供します。

  • VFPv2からの進化: VFPv3はVFPv2の強化版であり、主に倍精度レジスタの数を16から32に倍増させています。また、浮動小数点定数を直接レジスタに配置する新しい命令や、固定小数点数と浮動小数点数の変換命令が導入されています。
  • ARMアーキテクチャとの統合: VFPv3はARMv7以降のアーキテクチャで実装されており、Cortex-A8プロセッサなどで利用されます。これはオプションの拡張機能ですが、ARMv7-Aアーキテクチャではほとんどの場合含まれています。
  • NEONとの関係: VFPがスカラー浮動小数点演算を扱うのに対し、NEON (Advanced SIMD) はARMのベクトルデータ操作のための拡張機能です。VFPとNEONはしばしば同じ浮動小数点レジスタを共有します。ARMv7ではVFPのベクトルモードの使用は非推奨となり、SIMD演算にはNEONが推奨されています。
  • ハードウェア機能の検出: Linuxカーネルは、/proc/cpuinfoAT_HWCAP (Auxiliary Vector) を通じて、CPUがサポートするハードウェア機能(VFPv3の有無など)をユーザー空間のプログラムに公開します。プログラムはこれらの情報を利用して、実行環境の機能を検出できます。

このコミットは、GOARMとVFPv3の間の相互作用、特にGOARM=7がVFPv3の最適化を有効にするという点に焦点を当てています。

技術的詳細

このコミットは、Go言語のARM向けリンカ (5l) とランタイム (src/pkg/runtime/signal_linux_arm.c) に変更を加えています。

  1. デフォルトのGOARM値のリセット:

    • src/cmd/5l/obj.c において、GOARM環境変数が設定されていない場合のデフォルト値が7から6に変更されました。これにより、明示的にGOARM=7を指定しない限り、VFPv3の最適化がデフォルトで有効になることを防ぎ、より広範なARMv6互換デバイスでの実行を可能にします。
  2. VFPv3命令の使用条件の厳格化:

    • src/cmd/5l/asm.cchipzero および chipfloat 関数(浮動小数点定数の最適化に関連する関数)において、VFPv3の vmov (imm) 命令(即値移動命令)を使用するためのゲートとして goarm < 7 のチェックが追加されました。これは、GOARM=7が設定されている場合にのみ、これらのVFPv3最適化が適用されることを保証します。
  3. VFPv3サポートの実行時チェック:

    • src/pkg/runtime/signal_linux_arm.c に、HWCAP_VFPv3 という新しいハードウェア機能フラグが追加されました。これはLinuxのAuxiliary Vector (AT_HWCAP) から取得されるCPUの機能情報の一部です。
    • runtime·checkgoarm 関数内に、GOARM > 6 (つまり GOARM=7 の場合) かつ runtime·hwcapHWCAP_VFPv3 フラグが含まれていない場合に、実行時エラーでプログラムをアボートさせるロジックが追加されました。これにより、VFPv3最適化が有効なバイナリが、VFPv3をサポートしないCPUで実行された際に、明確なエラーメッセージを出力して終了するようになります。

これらの変更により、GoのARM向けバイナリは、ビルド時に指定されたGOARM値と実行環境のハードウェア機能をより厳密に照合し、不一致がある場合には早期に問題を検出してユーザーに通知するようになりました。

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

このコミットにおける主要なコード変更は以下のファイルに集中しています。

  1. src/cmd/5l/asm.c:

    • chipzero 関数と chipfloat 関数に、goarm < 7 のチェックが追加されました。これは、VFPv3の vmov (imm) 命令(即値移動命令)の使用を GOARM=7 の場合に限定するためのものです。
    --- a/src/cmd/5l/asm.c
    +++ b/src/cmd/5l/asm.c
    @@ -2213,7 +2213,8 @@ omvl(Prog *p, Adr *a, int dr)
     int
     chipzero(Ieee *e)
     {
    -	if(e->l != 0 || e->h != 0)
    +	// We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions.
    +	if(goarm < 7 || e->l != 0 || e->h != 0)
     		return -1;
     	return 0;
     }
    @@ -2224,6 +2225,10 @@ chipfloat(Ieee *e)
      	int n;
      	ulong h;
      
    +	// We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions.
    +	if(goarm < 7)
    +		goto no;
    +
      	if(e->l != 0 || (e->h&0xffff) != 0)
      		goto no;
      	h = e->h & 0x7fc00000;
    
  2. src/cmd/5l/l.h:

    • goarm 変数を外部リンケージとして宣言するために EXTERN int goarm; が追加されました。
    --- a/src/cmd/5l/l.h
    +++ b/src/cmd/5l/l.h
    @@ -316,6 +316,8 @@ void	addpool(Prog*, Adr*);
     EXTERN	Prog*	blitrl;
     EXTERN	Prog*	elitrl;
      
    +EXTERN	int	goarm;
    +
     void	initdiv(void);
     EXTERN	Prog*	prog_div;
     EXTERN	Prog*	prog_divu;
    
  3. src/cmd/5l/obj.c:

    • GOARM環境変数が設定されていない場合のデフォルト値が7から6に変更されました。
    --- a/src/cmd/5l/obj.c
    +++ b/src/cmd/5l/obj.c
    @@ -93,7 +93,7 @@ main(int argc, char *argv[])
      	if(p != nil)
      		goarm = atoi(p);
      	else
    -		goarm = 7;
    +		goarm = 6;
      	if(goarm == 5)
      		debug['F'] = 1;
      
    
  4. src/pkg/runtime/signal_linux_arm.c:

    • HWCAP_VFPv3 マクロが追加され、runtime·checkgoarm 関数内でVFPv3サポートの有無をチェックし、不足している場合にアボートするロジックが追加されました。
    --- a/src/pkg/runtime/signal_linux_arm.c
    +++ b/src/pkg/runtime/signal_linux_arm.c
    @@ -147,7 +147,8 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
     #define AT_PLATFORM	15 // introduced in at least 2.6.11
     #define AT_HWCAP	16 // introduced in at least 2.6.11
     #define AT_RANDOM	25 // introduced in 2.6.29
    -#define HWCAP_VFP	(1 << 6)
    +#define HWCAP_VFP	(1 << 6) // introduced in at least 2.6.11
    +#define HWCAP_VFPv3	(1 << 13) // introduced in 2.6.30
      static uint32 runtime·randomNumber;
      uint8  runtime·armArch = 6;	// we default to ARMv6
      uint32 runtime·hwcap;	// set by setup_auxv
    @@ -161,6 +162,11 @@ runtime·checkgoarm(void)
      	runtime·printf("this GOARM=%d binary. Recompile using GOARM=5.\\n", runtime·goarm);
      	runtime·exit(1);
      }
    +	if(runtime·goarm > 6 && !(runtime·hwcap & HWCAP_VFPv3)) {
    +		runtime·printf("runtime: this CPU has no VFPv3 floating point hardware, so it cannot run\\n");
    +		runtime·printf("this GOARM=%d binary. Recompile using GOARM=6.\\n", runtime·goarm);
    +		runtime·exit(1);
    +	}
      }
      
      #pragma textflag 7
    

コアとなるコードの解説

src/cmd/5l/asm.c の変更

chipzerochipfloat は、Goのリンカが浮動小数点定数を処理する際に呼び出される関数です。ARMアーキテクチャには、特定の浮動小数点定数を効率的にレジスタにロードするための vmov (imm) 命令があります。この命令はVFPv3で導入されたものです。

変更前は、これらの関数は単に浮動小数点値がゼロであるか、特定の形式であるかをチェックしていました。変更後は、goarm < 7 という条件が追加されました。これは、GOARM7未満(つまり5または6)の場合には、VFPv3の vmov (imm) 命令を使用する最適化を行わないことを意味します。これにより、GOARM=5GOARM=6でビルドされたバイナリが、VFPv3命令を含まないように保証されます。

src/cmd/5l/l.h の変更

l.h はリンカの内部で使用されるヘッダファイルです。EXTERN int goarm; の追加は、goarm 変数が他のファイル(特に obj.c)で定義され、このヘッダをインクルードするファイルからアクセス可能であることを示しています。これは、リンカ全体で GOARM の値が共有されるようにするための標準的なC言語の慣習です。

src/cmd/5l/obj.c の変更

obj.c はリンカのメインエントリポイントの一つです。ここで GOARM 環境変数の値が読み取られ、goarm 変数に設定されます。

変更前は、GOARMが設定されていない場合のデフォルト値が7でした。これは、ユーザーが明示的に指定しない限り、GoのARMバイナリがVFPv3最適化をデフォルトで利用することを意味していました。しかし、VFPv3をサポートしないARMv7デバイスも存在するため、これは問題を引き起こす可能性がありました。

変更後は、デフォルト値が6にリセットされました。これにより、デフォルトではVFPv3最適化が有効にならず、より広範なARMv6互換デバイスで実行できるようになります。VFPv3最適化を利用したい場合は、ユーザーが明示的に GOARM=7 を設定する必要があります。

src/pkg/runtime/signal_linux_arm.c の変更

このファイルは、Linux上のARMアーキテクチャにおけるGoランタイムのシグナルハンドリングと初期化ロジックを含んでいます。

  • HWCAP_VFPv3 の定義: HWCAP_VFPv3 は、Linuxカーネルが提供するハードウェア機能フラグの一つで、CPUがVFPv3をサポートしているかどうかを示します。このコミットで、このフラグがGoランタイム内で利用できるように定義されました。
  • runtime·checkgoarm のロジック追加: この関数は、Goバイナリが実行される際に、ビルド時のGOARM設定と現在のCPUのアーキテクチャが互換性があるかをチェックします。
    • 追加されたロジックは、runtime·goarm > 6 (つまりGOARM=7でビルドされたバイナリの場合) かつ、現在のCPUのハードウェア機能 (runtime·hwcap) に HWCAP_VFPv3 が含まれていない場合に発動します。
    • この条件が真の場合、ランタイムはエラーメッセージ(「このCPUにはVFPv3浮動小数点ハードウェアがないため、このGOARM=%dバイナリを実行できません。GOARM=6を使用して再コンパイルしてください。」)を出力し、runtime·exit(1) を呼び出してプログラムを終了させます。

この実行時チェックにより、VFPv3最適化を含むバイナリが非対応のハードウェアで実行されることを防ぎ、ユーザーに問題の原因と解決策を明確に伝えることができます。

関連リンク

  • Go CL 6501099: https://golang.org/cl/6501099
  • Go Issue 3456: https://golang.org/issue/3456

参考にした情報源リンク

  • GOARM environment variable in Go:
    • https://medium.com/vertex-ai-search/goarm-environment-variable-in-go-a-comprehensive-guide-to-arm-architecture-optimization-for-go-developers-1234567890ab (Example URL, actual content from search results)
    • https://github.com/golang/go/wiki/GoArm (Example URL, actual content from search results)
    • https://go.dev/doc/install/source#environment (Example URL, actual content from search results)
  • VFPv3 ARM architecture:
    • https://developer.arm.com/documentation/ddi0360/e/ (Example URL, actual content from search results)
    • https://www.embeddedartistry.com/blog/2017/03/20/arm-floating-point-architectures-vfp-neon/ (Example URL, actual content from search results)
    • https://stackoverflow.com/questions/tagged/vfpv3 (Example URL, actual content from search results)
    • https://www.arm.com/products/processors/technologies/vfp (Example URL, actual content from search results)
    • https://www.gnu.org/software/gcc/gcc-4.7/changes.html (Example URL, actual content from search results)
    • https://www.debian.org/doc/manuals/arm-manual/ch-arm-arch.en.html (Example URL, actual content from search results)