[インデックス 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つのファイルが変更されています。
src/cmd/5l/obj.c
: Go言語のARMリンカ(5l
)のソースコードです。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
へのダミー参照が削除されたことは、ソフトフロートエミュレーションが不要な環境では、この関数がリンカによって適切に削除されるようになったことを意味します。これにより、ランタイムバイナリから不要なコードが取り除かれ、全体のサイズ削減に寄与します。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/1a59e6239ccf6b354bdf2a3763ad9c8e4aded7f9
- Go CL (Change List): https://golang.org/cl/5975060
参考にした情報源リンク
- Go ARM softfloat emulation: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGKHQKHL4OwX4Acgcg7PoJgYJ57Zmyyq9I4256UPfI3CtxLWI8IrIpc5LPNlfFaIxq4P6JQO93GsbHAyVNcO3S1poJtAFza32uapV3VCnGFEjcmvu4=
- ARM VFP NEON: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFYMmMVDV-6bYc0hbNBP4JpKTXk76V4X1qnLeD_cmldiDJL0q7l5UJmsevnvumdTR6GLDTS6tSVSteNDUPA9FWDyJHml7TLQVrAjD427qK85UyJ_Xb5fBESTwwkJ-snDNy6_qhdrLBs89DwCvaIx4ut2HDo-Ts40lQHRGpxmfLAm5kxGHklKuzDr7df8gq7ngnuIyTLcho5m9Ofzamn-iSjaXZbbUtwRtTNGXMU3i5gh4mi54cFjnAM
- Go 5l linker: https://stackoverflow.com/questions/10986640/what-is-the-difference-between-6l-8l-and-5l-in-go