[インデックス 13570] ファイルの概要
このコミットは、Go言語のARMアーキテクチャ向けツールチェイン(アセンブラ 5a
およびリンカ 5l
)とランタイムに、浮動小数点絶対値命令である ABSD
(Double Precision Absolute Value) および ABSF
(Single Precision Absolute Value) のサポートを追加するものです。これにより、ARMv5以降のVFP (Vector Floating Point) ユニットを持つARMプロセッサ上で、浮動小数点数の絶対値計算をハードウェア命令で効率的に実行できるようになります。また、ハードウェアサポートがない場合のために、ソフトウェア浮動小数点エミュレーションにも対応が追加されています。
コミット
commit dee5adcf74d249adf1d6ba83eb906dbb60e2ee4f
Author: Michał Derkacz <ziutek@lnet.pl>
Date: Fri Aug 3 16:15:11 2012 -0400
5a, 5l, math: Add support for ABSD, ABSF floating point instructions.
R=golang-dev, dave, rsc, minux.ma
CC=golang-dev
https://golang.org/cl/6225051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dee5adcf74d249adf1d6ba83eb906dbb60e2ee4f
元コミット内容
src/cmd/5a/lex.c | 4 ++--
src/cmd/5l/5.out.h | 2 ++
src/cmd/5l/asm.c | 4 +++-
src/cmd/5l/softfloat.c | 2 ++
src/cmd/5l/span.c | 2 ++
src/pkg/runtime/softfloat_arm.c | 17 +++++++++++++++++
6 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c
index 19e5982bfc..cc02879b76 100644
--- a/src/cmd/5a/lex.c
+++ b/src/cmd/5a/lex.c
@@ -329,8 +329,6 @@ struct
"STREXD", LTYPE9, ASTREXD,
/*
- "ABSF", LTYPEI, AABSF,
- "ABSD", LTYPEI, AABSD,
"NEGF", LTYPEI, ANEGF,
"NEGD", LTYPEI, ANEGD,
"SQTF", LTYPEI, ASQTF,
@@ -343,6 +341,8 @@ struct
"NRMD", LTYPEI, ANRMD,
*/
+ "ABSF", LTYPEI, AABSF,
+ "ABSD", LTYPEI, AABSD,
"SQRTF", LTYPEI, ASQRTF,
"SQRTD", LTYPEI, ASQRTD,
"CMPF", LTYPEL, ACMPF,
diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h
index ff13c64a6e..1dea18c49e 100644
--- a/src/cmd/5l/5.out.h
+++ b/src/cmd/5l/5.out.h
@@ -126,6 +126,8 @@ enum as
ADIVD,
ASQRTF,
ASQRTD,
+ AABSF,
+ AABSD,
ASRL,
ASRA,
diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c
index 911b6892cf..9a74c2a35d 100644
--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -1493,7 +1493,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
tr = p->reg;
if(r == NREG) {
r = rt;
- if(p->as == AMOVF || p->as == AMOVD || p->as == ASQRTF || p->as == ASQRTD)
+ if(p->as == AMOVF || p->as == AMOVD || p->as == ASQRTF || p->as == ASQRTD || p->as == AABSF || p->as == AABSD)
r = 0;
}
o1 |= rf | (r<<16) | (rt<<12);
@@ -1948,6 +1948,8 @@ oprrr(int a, int sc)
case ADIVF: return o | (0xe<<24) | (0x8<<20) | (0xa<<8) | (0<<4);
case ASQRTD: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xb<<8) | (0xc<<4);
case ASQRTF: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xa<<8) | (0xc<<4);
+ case AABSD: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xb<<8) | (0xc<<4);
+ case AABSF: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xa<<8) | (0xc<<4);
case ACMPD: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xb<<8) | (0xc<<4);
case ACMPF: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xa<<8) | (0xc<<4);
diff --git a/src/cmd/5l/softfloat.c b/src/cmd/5l/softfloat.c
index 4011071780..8be5b7585c 100644
--- a/src/cmd/5l/softfloat.c
+++ b/src/cmd/5l/softfloat.c
@@ -55,6 +55,8 @@ softfloat(void)
case ADIVD:
case ASQRTF:
case ASQRTD:
+ case AABSF:
+ case AABSD:
goto soft;
default:
diff --git a/src/cmd/5l/span.c b/src/cmd/5l/span.c
index 93991ba49c..3787740e28 100644
--- a/src/cmd/5l/span.c
+++ b/src/cmd/5l/span.c
@@ -819,6 +819,8 @@ buildop(void)
oprange[ASQRTD] = oprange[r];
oprange[AMOVFD] = oprange[r];
oprange[AMOVDF] = oprange[r];
+ oprange[AABSF] = oprange[r];
+ oprange[AABSD] = oprange[r];
break;
case ACMPF:
diff --git a/src/pkg/runtime/softfloat_arm.c b/src/pkg/runtime/softfloat_arm.c
index bd73cb15b5..9a54406302 100644
--- a/src/pkg/runtime/softfloat_arm.c
+++ b/src/pkg/runtime/softfloat_arm.c
@@ -420,6 +420,23 @@ stage3: // regd, regm are 4bit variables
regd, regm, m->freghi[regd], m->freglo[regd]);
break;
+ case 0xeeb00bc0: // D[regd] = abs D[regm]
+ m->freglo[regd] = m->freglo[regm];
+ m->freghi[regd] = m->freghi[regm] & ((1<<31)-1);
+
+ if(trace)
+ runtime·printf("*** D[%d] = abs D[%d] %x-%x\n",
+ regd, regm, m->freghi[regd], m->freglo[regd]);
+ break;
+
+ case 0xeeb00ac0: // F[regd] = abs F[regm]
+ m->freglo[regd] = m->freglo[regm] & ((1<<31)-1);
+
+ if(trace)
+ runtime·printf("*** F[%d] = abs F[%d] %x\n",
+ regd, regm, m->freglo[regd]);
+ break;
+
case 0xeeb40bc0: // D[regd] :: D[regm] (CMPD)
case 0xeeb40ac0: // F[regd] :: F[regm] (CMPF)
runtime·fcmp64c(getd(regd), getd(regm), &cmp, &nan);
変更の背景
このコミットの背景には、Go言語がサポートするARMアーキテクチャにおいて、浮動小数点演算の効率性を向上させるという目的があります。特に、浮動小数点数の絶対値を計算する操作は頻繁に行われるため、これをハードウェアのネイティブ命令で直接実行できることは、パフォーマンスの観点から非常に重要です。
Go言語の初期のARMサポートでは、一部の浮動小数点命令が直接サポートされていなかったり、ソフトウェアエミュレーションに頼っていたりするケースがありました。ABSD
(Double Precision Absolute Value) と ABSF
(Single Precision Absolute Value) は、それぞれ倍精度浮動小数点数と単精度浮動小数点数の絶対値を計算する命令であり、ARMのVFP (Vector Floating Point) 拡張命令セットに含まれています。これらの命令をGoのツールチェインが認識し、適切にコード生成できるようにすることで、GoプログラムがARM上でより高速に動作するようになります。
また、src/cmd/5a/lex.c
の変更履歴を見ると、以前は ABSF
と ABSD
がコメントアウトされていたことが示唆されます。これは、これらの命令のサポートが一時的に無効化されていたか、あるいは実装が不完全であった可能性を示しています。このコミットは、それらの命令を完全に統合し、GoのARMツールチェインにおける浮動小数点演算のサポートを強化することを目的としています。
前提知識の解説
Go言語のツールチェイン (5a, 5l)
Go言語は、独自のクロスコンパイル可能なツールチェインを持っています。このコミットで言及されている 5a
と 5l
は、それぞれARMアーキテクチャ向けのGoアセンブラとGoリンカを指します。
5a
(Go Assembler for ARM): Go言語のアセンブラは、Goの特殊なアセンブリ言語(Plan 9アセンブリに似ているが、Go固有の拡張がある)を機械語に変換します。5a
は、ARMプロセッサが理解できる命令セットに変換する役割を担います。5l
(Go Linker for ARM): Goリンカは、アセンブラによって生成されたオブジェクトファイルや、Goコンパイラによって生成されたオブジェクトファイルを結合し、実行可能なバイナリを生成します。この過程で、シンボルの解決、メモリレイアウトの決定、外部ライブラリとのリンクなどが行われます。
これらのツールは、Goプログラムを特定のアーキテクチャ(この場合はARM)で実行可能にするために不可欠な要素です。
浮動小数点数とIEEE 754
浮動小数点数は、実数をコンピュータで表現するための形式です。ほとんどの現代のシステムでは、IEEE 754標準が採用されています。この標準では、浮動小数点数を符号部、指数部、仮数部(または分数部)に分けて表現します。
- 符号部 (Sign Bit): 1ビットで、数値が正 (+) か負 (-) かを示します。0は正、1は負です。
- 指数部 (Exponent): 数値の大きさを表します。
- 仮数部 (Mantissa/Fraction): 数値の精度を表します。
絶対値を計算するということは、数値の符号を無視してその大きさだけを考慮することです。IEEE 754形式では、これは単に符号ビットを0に設定することによって実現できます。
- 単精度 (Single Precision): 32ビットで表現され、
float32
型に対応します。 - 倍精度 (Double Precision): 64ビットで表現され、
float64
型に対応します。
ARMのVFP (Vector Floating Point) ユニット
VFPは、ARMアーキテクチャの浮動小数点演算ユニット (FPU) の拡張命令セットです。VFPは、単精度および倍精度の浮動小数点演算をハードウェアで高速に実行するための命令を提供します。これにより、ソフトウェアエミュレーションに比べて大幅なパフォーマンス向上が期待できます。
ソフトウェア浮動小数点エミュレーション (Softfloat)
一部のARMプロセッサや、特定のコンフィギュレーションでは、ハードウェアのVFPユニットが利用できない場合があります。このような場合、浮動小数点演算はソフトウェアによってエミュレートされます。これを「ソフトウェア浮動小数点」または「ソフトフロート」と呼びます。ソフトフロートはハードウェア実装に比べてはるかに低速ですが、ハードウェアサポートがない環境でも浮動小数点演算を可能にします。Goのランタイムには、このような状況に対応するためのソフトフロート実装が含まれています。
技術的詳細
このコミットは、GoのARMツールチェインとランタイムにおける浮動小数点絶対値命令 ABSD
および ABSF
のサポートを統合するために、複数のコンポーネントにわたる変更を加えています。
-
アセンブラ (
src/cmd/5a/lex.c
) の更新:5a
はGoのアセンブリ言語を解析するツールです。このファイルでは、アセンブラが認識する命令のリストにABSF
とABSD
を追加しています。以前はコメントアウトされていた行が有効化され、これらの命令がアセンブリコード内で使用された際に正しく解析されるようになります。LTYPEI
は、これらの命令が単一のオペランド(入力レジスタと出力レジスタが同じ場合が多い)を取ることを示唆しています。
-
リンカの命令定義 (
src/cmd/5l/5.out.h
) の更新:- このヘッダファイルは、リンカが内部的に使用する命令コードの列挙型 (
enum as
) を定義しています。AABSF
とAABSD
がこの列挙型に追加され、リンカがこれらの命令を識別できるようになります。
- このヘッダファイルは、リンカが内部的に使用する命令コードの列挙型 (
-
リンカのアセンブリコード生成 (
src/cmd/5l/asm.c
) の更新:asm.c
は、Goのアセンブリ命令をARMの機械語命令に変換するリンカの主要部分です。oprrr
関数は、特定の浮動小数点命令の機械語エンコーディングを生成します。このコミットでは、AABSD
とAABSF
の具体的なARM命令エンコーディングが追加されています。AABSD: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xb<<8) | (0xc<<4);
AABSF: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xa<<8) | (0xc<<4);
これらの16進数値は、ARMのVFP命令の特定のビットフィールドに対応しており、命令の種類、レジスタ指定、およびその他のオプションをエンコードしています。例えば、0xe
はVFP命令のプレフィックス、0xb
はデータ処理命令のタイプ、0xa
や0xb
は単精度/倍精度を示すフィールドなどです。
- また、レジスタ処理に関する
if
文が更新され、AMOVF
,AMOVD
,ASQRTF
,ASQRTD
に加えてAABSF
とAABSD
も含まれるようになりました。これは、これらの命令が特定のレジスタ操作パターンに従うことを示しています。
-
リンカのソフトウェア浮動小数点処理 (
src/cmd/5l/softfloat.c
) の更新:- このファイルは、ハードウェア浮動小数点ユニットがない場合に、リンカが特定の浮動小数点命令をソフトウェアエミュレーションにフォールバックさせるためのロジックを含んでいます。
softfloat
関数内のswitch
文にAABSF
とAABSD
が追加され、これらの命令も必要に応じてソフトウェアで処理されるように指示しています。goto soft;
は、共通のソフトウェアエミュレーションパスにジャンプすることを示します。
- このファイルは、ハードウェア浮動小数点ユニットがない場合に、リンカが特定の浮動小数点命令をソフトウェアエミュレーションにフォールバックさせるためのロジックを含んでいます。
-
リンカのスパン解析 (
src/cmd/5l/span.c
) の更新:span.c
は、リンカが命令のサイズや配置を決定するために使用するスパン解析ロジックを含んでいます。buildop
関数内でoprange
配列にAABSF
とAABSD
が追加されています。oprange
は、各命令のサイズや特性を定義するテーブルであり、リンカが正確なコードレイアウトを生成するために必要です。
-
ARMランタイムのソフトウェア浮動小数点実装 (
src/pkg/runtime/softfloat_arm.c
) の更新:- このファイルは、ARMアーキテクチャにおけるGoランタイムのソフトウェア浮動小数点エミュレーションの核心部分です。
- 最も重要な変更は、
0xeeb00bc0
(ABSD) と0xeeb00ac0
(ABSF) という特定の命令パターンを検出した場合の処理が追加されたことです。 ABSD
(倍精度):m->freglo[regd] = m->freglo[regm];
m->freghi[regd] = m->freghi[regm] & ((1<<31)-1);
倍精度浮動小数点数は2つの32ビットレジスタ (freglo
とfreghi
) で表現されます。freghi
は上位32ビット(符号ビットを含む)を保持します。((1<<31)-1)
は、最上位ビット(符号ビット)を除くすべてのビットが1である32ビットマスクです。このマスクとfreghi[regm]
をビットAND演算することで、符号ビットを強制的に0に設定し、数値の絶対値を取得します。freglo
は下位32ビットなので変更する必要はありません。
ABSF
(単精度):m->freglo[regd] = m->freglo[regm] & ((1<<31)-1);
単精度浮動小数点数は1つの32ビットレジスタ (freglo
) で表現されます。同様に、((1<<31)-1)
マスクを使用して符号ビットをクリアし、絶対値を取得します。
- これらの変更により、ハードウェアで直接実行できない場合でも、Goのランタイムがこれらの絶対値命令を正しくエミュレートできるようになります。
これらの変更は、GoのARMツールチェインが ABSD
と ABSF
命令をアセンブリレベルで認識し、リンカがそれらを適切な機械語に変換し、さらにハードウェアサポートがない場合にはランタイムがソフトウェアでエミュレートするという、包括的なサポートを提供することを示しています。
コアとなるコードの変更箇所
このコミットの最もコアとなる変更は、src/pkg/runtime/softfloat_arm.c
における ABSD
と ABSF
命令のソフトウェアエミュレーションの実装です。
diff --git a/src/pkg/runtime/softfloat_arm.c b/src/pkg/runtime/softfloat_arm.c
index bd73cb15b5..9a54406302 100644
--- a/src/pkg/runtime/softfloat_arm.c
+++ b/src/pkg/runtime/softfloat_arm.c
@@ -420,6 +420,23 @@ stage3: // regd, regm are 4bit variables
regd, regm, m->freghi[regd], m->freglo[regd]);
break;
+ case 0xeeb00bc0: // D[regd] = abs D[regm]
+ m->freglo[regd] = m->freglo[regm];
+ m->freghi[regd] = m->freghi[regm] & ((1<<31)-1);
+
+ if(trace)
+ runtime·printf("*** D[%d] = abs D[%d] %x-%x\\n",
+ regd, regm, m->freghi[regd], m->freglo[regd]);
+ break;
+
+ case 0xeeb00ac0: // F[regd] = abs F[regm]
+ m->freglo[regd] = m->freglo[regm] & ((1<<31)-1);
+
+ if(trace)
+ runtime·printf("*** F[%d] = abs F[%d] %x\\n",
+ regd, regm, m->freglo[regd]);
+ break;
+
case 0xeeb40bc0: // D[regd] :: D[regm] (CMPD)
case 0xeeb40ac0: // F[regd] :: F[regm] (CMPF)
runtime·fcmp64c(getd(regd), getd(regm), &cmp, &nan);
コアとなるコードの解説
上記のコードは、ARMアーキテクチャにおけるソフトウェア浮動小数点演算のディスパッチループの一部です。stage3
ラベルの箇所で、特定の命令パターン(機械語のビット列)を検出して、それに対応する処理を実行します。
-
case 0xeeb00bc0:
(ABSD - 倍精度絶対値)- このケースは、ARMのVFP命令で倍精度浮動小数点数の絶対値を計算する命令 (
ABSD
) に対応する機械語パターンを検出します。 m->freglo[regd] = m->freglo[regm];
- 倍精度浮動小数点数は64ビットであり、Goのランタイムでは通常、2つの32ビットレジスタ(
freglo
とfreghi
)に分割して格納されます。freglo
は数値の下位32ビットを保持するため、絶対値演算では変更する必要がありません。したがって、ソースレジスタregm
の下位32ビットをそのままデスティネーションレジスタregd
の下位32ビットにコピーします。
- 倍精度浮動小数点数は64ビットであり、Goのランタイムでは通常、2つの32ビットレジスタ(
m->freghi[regd] = m->freghi[regm] & ((1<<31)-1);
freghi
は数値の上位32ビットを保持し、これには符号ビットが含まれます。((1<<31)-1)
は、バイナリで0111...111
(31個の1) となる32ビットのマスクです。これは、最上位ビット(符号ビット)が0で、残りの31ビットがすべて1であることを意味します。&
(ビットAND演算) を使用して、ソースレジスタregm
の上位32ビット (m->freghi[regm]
) とこのマスクを演算します。これにより、m->freghi[regm]
の最上位ビット(符号ビット)が強制的に0に設定され、数値が正の絶対値に変換されます。他のビットはマスクによって変更されません。
runtime·printf
はデバッグ用の出力であり、トレースが有効な場合に絶対値演算の結果を表示します。
- このケースは、ARMのVFP命令で倍精度浮動小数点数の絶対値を計算する命令 (
-
case 0xeeb00ac0:
(ABSF - 単精度絶対値)- このケースは、ARMのVFP命令で単精度浮動小数点数の絶対値を計算する命令 (
ABSF
) に対応する機械語パターンを検出します。 m->freglo[regd] = m->freglo[regm] & ((1<<31)-1);
- 単精度浮動小数点数は32ビットであり、
freglo
に格納されます。 - 倍精度の場合と同様に、
((1<<31)-1)
マスクを使用して、ソースレジスタregm
のfreglo
の最上位ビット(符号ビット)を0に設定し、絶対値を取得します。
- 単精度浮動小数点数は32ビットであり、
runtime·printf
はデバッグ用の出力です。
- このケースは、ARMのVFP命令で単精度浮動小数点数の絶対値を計算する命令 (
このコードは、IEEE 754浮動小数点数の表現における符号ビットの特性を直接利用して、効率的に絶対値を計算するソフトウェア実装を示しています。ハードウェアがこれらの命令をサポートしない場合でも、Goプログラムが正確な浮動小数点絶対値演算を実行できることを保証します。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
- Go言語のARMポートに関する情報: https://go.dev/doc/go1.1#arm (Go 1.1リリースノート、ARMサポートについて言及)
- ARMアーキテクチャリファレンスマニュアル (VFP命令セットに関する詳細): https://developer.arm.com/documentation/ (具体的なドキュメントはバージョンによって異なるため、適切なものを検索してください)
- IEEE 754 浮動小数点標準: https://standards.ieee.org/ieee/754/6748/
参考にした情報源リンク
- Go言語のソースコード (GitHub): https://github.com/golang/go
- Go言語のIssue Tracker (Gerrit): https://go.dev/cl/ (コミットメッセージに記載されている
https://golang.org/cl/6225051
はGerritの変更リストへのリンクです) - IEEE 754 Floating Point Standard Explained: https://en.wikipedia.org/wiki/IEEE_754
- ARM Architecture and Instruction Set: https://en.wikipedia.org/wiki/ARM_architecture
- VFP (Vector Floating Point) Extension: https://en.wikipedia.org/wiki/ARM_architecture#Floating-point_and_SIMD_extensions
- Plan 9 Assembly (Goのアセンブリ言語のルーツ): https://9p.io/sys/doc/asm.html
- Go Assembly Language: https://go.dev/doc/asm