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

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

このコミットは、Go言語のARMアーキテクチャ向けリンカ(5l)とランタイムにおいて、使用されないソフトフロートエミュレーション関連のコードを削除することで、バイナリサイズを削減することを目的としています。特に、VFP/NEON(浮動小数点ユニット/SIMD拡張)を搭載したARMマシンにおいて、約30KBのコードサイズ削減を実現しています。

コミット

commit 1a59e6239ccf6b354bdf2a3763ad9c8e4aded7f9
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Wed Apr 11 04:11:27 2012 +0800

    5l, runtime: remove softfloat emulation code when not used
    This leads to ~30kB improvement on code size for ARM machines with VFP/NEON.
    Example: go test -c math
           GOARM=5  GOARM=6
      Old: 1884200  1839144
      New: 1884165  1805245
      -:        35    33899
    
    R=rsc, bradfitz, dave, kai.backman
    CC=golang-dev
    https://golang.org/cl/5975060

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

https://github.com/golang/go/commit/1a59e6239ccf6b354bdf2a3763ad9c8e4aded7f9

元コミット内容

5l, runtime: remove softfloat emulation code when not used
This leads to ~30kB improvement on code size for ARM machines with VFP/NEON.
Example: go test -c math
       GOARM=5  GOARM=6
  Old: 1884200  1839144
  New: 1884165  1805245
  -:        35    33899

R=rsc, bradfitz, dave, kai.backman
CC=golang-dev
https://golang.org/cl/5975060

変更の背景

このコミットの主な背景は、Go言語でコンパイルされたARMアーキテクチャ向けバイナリのサイズを最適化することです。特に、ハードウェア浮動小数点ユニット(FPU)であるVFP(Vector Floating Point)やSIMD拡張であるNEONを搭載したARMプロセッサが普及するにつれて、ソフトウェアによる浮動小数点エミュレーション(ソフトフロートエミュレーション)の必要性が低下しました。

FPUを持たない古いARMv5などのアーキテクチャでは、浮動小数点演算をソフトウェアでエミュレートする必要があり、そのためのコードがバイナリに含まれていました。しかし、VFP/NEONを持つ新しいARMプロセッサでは、これらのエミュレーションコードは不要であり、バイナリサイズを不必要に増加させていました。

このコミットは、不要なソフトフロートエミュレーションコードをリンカとランタイムから削除することで、特にVFP/NEONを搭載したARMマシンにおけるGoバイナリのフットプリントを削減し、リソースが限られた環境でのデプロイメントや実行効率を向上させることを目指しています。コミットメッセージに示されているように、GOARM=6(VFP/NEONをサポートするARMv6以降)の環境で顕著なサイズ削減効果(約30KB)が見られます。

前提知識の解説

ARMアーキテクチャ

ARM(Advanced RISC Machine)は、主にモバイルデバイス、組み込みシステム、IoTデバイスなどで広く利用されているRISC(Reduced Instruction Set Computer)ベースのプロセッサアーキテクチャです。低消費電力と高性能を両立させる設計が特徴で、スマートフォン、タブレット、スマートテレビ、自動車のECUなど、多岐にわたる製品に採用されています。

VFP (Vector Floating Point) と NEON (Advanced SIMD)

VFPとNEONは、ARMプロセッサに搭載されるオプションの拡張機能で、浮動小数点演算と並列データ処理をハードウェアレベルで高速化します。

  • VFP (Vector Floating Point): ARMプロセッサの浮動小数点演算ユニット(FPU)です。IEEE 754標準に準拠した単精度および倍精度の浮動小数点演算をハードウェアで実行します。これにより、ソフトウェアエミュレーションに比べて大幅な性能向上が期待できます。VFPは主にスカラー浮動小数点演算(一度に一つのデータに対する演算)に特化しています。
  • NEON (Advanced SIMD): ARM Cortex-AおよびCortex-Rシリーズプロセッサ向けの高度なSIMD(Single Instruction, Multiple Data)アーキテクチャ拡張です。NEONは、単一の命令で複数のデータ要素に対して同じ演算を同時に実行できるため、マルチメディア処理(オーディオ/ビデオエンコーディング・デコーディング)、2D/3Dグラフィックス、ゲーム、信号処理、コンピュータビジョン、ディープラーニングなど、データ並列性の高いタスクにおいて非常に高いパフォーマンスを発揮します。NEONは整数および浮動小数点データ型をサポートし、VFPとレジスタファイルを共有します。

VFPとNEONの存在により、ソフトウェアによる浮動小数点エミュレーションが不要になるか、その使用頻度が大幅に減少します。

ソフトフロートエミュレーション

ソフトフロートエミュレーション(Software Floating-Point Emulation)とは、プロセッサがハードウェア浮動小数点ユニット(FPU)を持たない場合に、浮動小数点演算をソフトウェア(CPUの整数演算命令の組み合わせ)で実行する技術です。FPUがない環境でも浮動小数点演算を可能にしますが、ハードウェアFPUに比べて処理速度が著しく遅く、バイナリサイズも大きくなる傾向があります。

Go言語では、GOARM環境変数を通じてソフトフロートの挙動を制御します。例えば、GOARM=5(ARMv5アーキテクチャ向け)でコンパイルする場合、Goリンカは浮動小数点命令の前に_sfloat関数への呼び出しを挿入し、ソフトウェアエミュレータを介して浮動小数点演算を実行します。

Go言語のリンカ (5l)

Go言語の初期のツールチェインでは、各アーキテクチャ向けに専用のリンカが用意されていました。

  • 5l: ARMアーキテクチャ向け
  • 6l: x86-64 (amd64) アーキテクチャ向け
  • 8l: x86 (386) アーキテクチャ向け

これらのリンカは、Goのコンパイラによって生成されたオブジェクトファイルを結合し、実行可能なバイナリを生成する役割を担っていました。Go 1.3以降では、リンカのアーキテクチャが大幅に改善され、これらのアーキテクチャ固有のリンカは単一の統合されたリンカに置き換えられました。このコミットは、Go 1.3以前の5lリンカのコードベースに対する変更です。

技術的詳細

このコミットは、Go言語のARMアーキテクチャ向けツールチェインにおいて、ソフトフロートエミュレーションコードが不要な場合にそれを削除する変更です。具体的には、以下の2つのファイルが変更されています。

  1. src/cmd/5l/obj.c: Go言語のARMリンカ(5l)のソースコードです。
  2. src/pkg/runtime/asm_arm.s: Go言語のARMランタイムのアセンブリコードです。

変更の核心は、VFP/NEONを搭載したARMマシンでは、runtime.softfloatのようなソフトウェアエミュレーション関連の関数が不要になるため、リンカがこれらの関数をバイナリに含めないようにすることです。これにより、バイナリサイズが削減されます。

コミットメッセージの例が示すように、go test -c mathコマンドでmathパッケージをコンパイルした際のバイナリサイズが、GOARM=6(VFP/NEONをサポートするARMv6以降)の環境で大幅に削減されています。

  • GOARM=5(FPUなしのARMv5向け)では、ソフトフロートエミュレーションが必要なため、サイズ削減はわずか(35バイト)です。
  • GOARM=6(VFP/NEONありのARMv6向け)では、ソフトフロートエミュレーションが不要になるため、33899バイト(約33KB)という大きなサイズ削減が実現されています。

これは、リンカが不要なコードを適切にデッドコードとして認識し、最終的なバイナリから取り除くことができるようになったことを意味します。

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

src/cmd/5l/obj.c

--- a/src/cmd/5l/obj.c
+++ b/src/cmd/5l/obj.c
@@ -63,13 +63,6 @@ Header headers[] = {
  *\t-Hlinux -Tx -Rx\t\t\tis linux elf
  */
 
-static char*
-linkername[] =
-{
-	"runtime.softfloat",
-	"math.sqrtGoC",
-};
-
 void
 usage(void)
 {
@@ -80,9 +73,8 @@ main(int argc, char *argv[])
 {
-	int c, i;
+	int c;
 	char *p, *name, *val;
 
 	Binit(&bso, 1, OWRITE);
@@ -250,9 +243,8 @@ main(int argc, char *argv[])
 	loadlib();
 
 	// mark some functions that are only referenced after linker code editing
-	// TODO(kaib): this doesn't work, the prog can't be found in runtime
-	// for(i=0; i<nelem(linkername); i++)
-	// 	mark(lookup(linkername[i], 0));
+	if(debug['F'])
+		mark(rlookup("_sfloat", 0));
 	deadcode();
 	if(textp == nil) {
 		diag("no code");

src/pkg/runtime/asm_arm.s

--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -58,17 +58,6 @@ TEXT _rt0_arm(SB),7,$-4
 	MOVW	$1234, R0
 	MOVW	$1000, R1
 	MOVW	R0, (R1)	// fail hard
-	B	runtime·_dep_dummy(SB)	// Never reached
-
-// TODO(kaib): remove these once i actually understand how the linker removes symbols
-// pull in dummy dependencies
-TEXT runtime·_dep_dummy(SB),7,$0
-	BL	_div(SB)
-	BL	_divu(SB)
-	BL	_mod(SB)
-	BL	_modu(SB)
-	BL	_modu(SB)
-	BL	_sfloat(SB)
 
 TEXT runtime·breakpoint(SB),7,$0
 	// no breakpoint yet; let program exit

コアとなるコードの解説

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

このファイルはGo言語のARMリンカ(5l)の主要な部分です。

  • linkername 配列の削除: 以前はlinkernameという静的文字列配列があり、"runtime.softfloat""math.sqrtGoC"といった関数名がハードコードされていました。これらの関数は、リンカがデッドコード削除を行う際に、たとえ直接参照されていなくてもバイナリに含めるべき「特別な」関数として扱われていた可能性があります。この配列が削除されたことで、これらの関数が常に含まれるという制約がなくなりました。

  • main 関数内の mark 呼び出しの変更: 以前のコードでは、linkername配列内のすべての関数をループでmark関数に渡し、リンカがこれらの関数をバイナリに含めるように指示していました。しかし、このコミットでは、このループが削除され、代わりに以下の条件付きのmark呼び出しに置き換えられました。

    if(debug['F'])
        mark(rlookup("_sfloat", 0));
    

    debug['F']は、リンカのデバッグフラグの一つで、おそらくソフトフロートエミュレーションが必要な場合に設定されるフラグです。この変更により、_sfloat(ソフトフロートエミュレーションのコア関数)は、デバッグフラグ'F'が設定されている場合にのみmarkされるようになりました。つまり、VFP/NEONを搭載したARMマシンなど、ソフトフロートエミュレーションが不要な環境では、debug['F']が設定されないため、_sfloat関数はmarkされず、リンカのデッドコード削除の対象となり、最終的なバイナリから除外される可能性が高まります。これにより、不要なコードがバイナリに含まれることを防ぎ、サイズ削減に貢献します。

src/pkg/runtime/asm_arm.s の変更

このファイルはGo言語のARMランタイムのアセンブリコードです。

  • runtime·_dep_dummy テキストブロックの削除: 以前のコードにはruntime·_dep_dummyというラベルを持つアセンブリコードブロックが存在しました。このブロックは、_div, _divu, _mod, _modu, _sfloatといった関数へのダミーのBL(Branch with Link)命令を含んでいました。コメントにもあるように、「リンカがシンボルを削除する方法を実際に理解したら削除する」と書かれており、これはリンカが特定のシンボル(この場合は浮動小数点演算や除算関連のランタイム関数)をデッドコードとして誤って削除しないように、意図的にダミーの依存関係を作成していたものと考えられます。

    このコミットでruntime·_dep_dummyブロックが完全に削除されたことは、リンカのデッドコード削除ロジックが改善され、これらのダミーの依存関係が不要になったことを示唆しています。特に、_sfloatへのダミー参照が削除されたことは、ソフトフロートエミュレーションが不要な環境では、この関数がリンカによって適切に削除されるようになったことを意味します。これにより、ランタイムバイナリから不要なコードが取り除かれ、全体のサイズ削減に寄与します。

関連リンク

参考にした情報源リンク