[インデックス 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/cpuinfo
やAT_HWCAP
(Auxiliary Vector) を通じて、CPUがサポートするハードウェア機能(VFPv3の有無など)をユーザー空間のプログラムに公開します。プログラムはこれらの情報を利用して、実行環境の機能を検出できます。
このコミットは、GOARM
とVFPv3の間の相互作用、特にGOARM=7
がVFPv3の最適化を有効にするという点に焦点を当てています。
技術的詳細
このコミットは、Go言語のARM向けリンカ (5l
) とランタイム (src/pkg/runtime/signal_linux_arm.c
) に変更を加えています。
-
デフォルトの
GOARM
値のリセット:src/cmd/5l/obj.c
において、GOARM
環境変数が設定されていない場合のデフォルト値が7
から6
に変更されました。これにより、明示的にGOARM=7
を指定しない限り、VFPv3の最適化がデフォルトで有効になることを防ぎ、より広範なARMv6互換デバイスでの実行を可能にします。
-
VFPv3命令の使用条件の厳格化:
src/cmd/5l/asm.c
のchipzero
およびchipfloat
関数(浮動小数点定数の最適化に関連する関数)において、VFPv3のvmov (imm)
命令(即値移動命令)を使用するためのゲートとしてgoarm < 7
のチェックが追加されました。これは、GOARM=7
が設定されている場合にのみ、これらのVFPv3最適化が適用されることを保証します。
-
VFPv3サポートの実行時チェック:
src/pkg/runtime/signal_linux_arm.c
に、HWCAP_VFPv3
という新しいハードウェア機能フラグが追加されました。これはLinuxのAuxiliary Vector (AT_HWCAP
) から取得されるCPUの機能情報の一部です。runtime·checkgoarm
関数内に、GOARM > 6
(つまりGOARM=7
の場合) かつruntime·hwcap
にHWCAP_VFPv3
フラグが含まれていない場合に、実行時エラーでプログラムをアボートさせるロジックが追加されました。これにより、VFPv3最適化が有効なバイナリが、VFPv3をサポートしないCPUで実行された際に、明確なエラーメッセージを出力して終了するようになります。
これらの変更により、GoのARM向けバイナリは、ビルド時に指定されたGOARM
値と実行環境のハードウェア機能をより厳密に照合し、不一致がある場合には早期に問題を検出してユーザーに通知するようになりました。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更は以下のファイルに集中しています。
-
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;
-
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;
-
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;
-
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
の変更
chipzero
と chipfloat
は、Goのリンカが浮動小数点定数を処理する際に呼び出される関数です。ARMアーキテクチャには、特定の浮動小数点定数を効率的にレジスタにロードするための vmov (imm)
命令があります。この命令はVFPv3で導入されたものです。
変更前は、これらの関数は単に浮動小数点値がゼロであるか、特定の形式であるかをチェックしていました。変更後は、goarm < 7
という条件が追加されました。これは、GOARM
が7
未満(つまり5
または6
)の場合には、VFPv3の vmov (imm)
命令を使用する最適化を行わないことを意味します。これにより、GOARM=5
やGOARM=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)