[インデックス 13892] ファイルの概要
このコミットは、Go言語の標準ライブラリ内のアセンブリコードにおける変数名を、対応するGo言語の関数宣言と一致させるための変更です。これにより、Goとアセンブリ間のインターフェースの整合性が向上し、コードの可読性と保守性が高まります。特に、bytes
, hash/crc32
, math
, math/big
, sync/atomic
パッケージ内のアセンブリファイルが影響を受けています。
コミット
commit 85729503834e899324640023f2e969042ed647ff
Author: Russ Cox <rsc@golang.org>
Date: Fri Sep 21 00:35:56 2012 -0400
all: match asm variable names to Go func declarations
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6548046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/85729503834e899324640023f2e969042ed647ff
元コミット内容
このコミットの目的は、Go言語の標準ライブラリに含まれるアセンブリ言語(Plan 9アセンブラ)で書かれたコードにおいて、関数内で使用される変数名(引数や戻り値)を、そのアセンブリ関数に対応するGo言語の関数宣言における変数名と一致させることです。
例えば、Goの関数宣言が func IndexByte(s []byte, c byte) int
であった場合、アセンブリコード内で s
や c
、そして戻り値の ret
を参照する際に、それぞれ s+0(FP)
、c+12(FP)
、r+16(FP)
のように、Goの関数宣言で使われている変数名に合わせる変更が行われています。
変更されたファイルは以下の通りです。
src/pkg/bytes/asm_386.s
src/pkg/bytes/asm_amd64.s
src/pkg/bytes/asm_arm.s
src/pkg/hash/crc32/crc32_amd64.go
src/pkg/hash/crc32/crc32_amd64.s
src/pkg/math/abs_arm.s
src/pkg/math/big/arith_386.s
src/pkg/math/big/arith_amd64.s
src/pkg/math/big/arith_arm.s
src/pkg/math/frexp_386.s
src/pkg/math/hypot_386.s
src/pkg/math/hypot_amd64.s
src/pkg/math/ldexp_386.s
src/pkg/math/modf_386.s
src/pkg/math/sincos_386.s
src/pkg/sync/atomic/asm_386.s
src/pkg/sync/atomic/asm_amd64.s
これらのファイルでは、アセンブリコード内の変数参照(例: p+0(FP)
, len+4(FP)
, ret+16(FP)
など)が、対応するGo関数の引数名や戻り値名(例: s+0(FP)
, s+4(FP)
, r+16(FP)
など)に修正されています。
変更の背景
この変更の背景には、Go言語の設計思想と、アセンブリ言語の利用におけるベストプラクティスがあります。
Go言語は、パフォーマンスが重要な部分でアセンブリ言語を使用することがあります。特に、標準ライブラリの低レベルな操作(バイト操作、ハッシュ計算、数学関数、アトミック操作など)では、特定のアーキテクチャに最適化されたアセンブリコードが用いられることがあります。
しかし、Goのコードとアセンブリコードが密接に連携する場合、両者の間でインターフェースの整合性を保つことが極めて重要です。Goの関数宣言は、その関数の引数と戻り値のセマンティクスを定義します。アセンブリコードがこれらの値を操作する際、Goの宣言と異なる名前で参照していると、以下のような問題が生じます。
- 可読性の低下: Goのコードを読んでいる開発者が、対応するアセンブリコードを読んだ際に、どの変数がGoのどの引数や戻り値に対応するのかを即座に理解するのが難しくなります。これは、デバッグやコードレビューの際に混乱を招きます。
- 保守性の低下: Goの関数宣言が変更された場合、アセンブリコード内の対応する変数名も手動で更新する必要が生じます。名前が一致していないと、この更新作業が見落とされやすくなり、バグの原因となる可能性があります。
- ツールとの連携: 将来的に、GoのツールチェインがアセンブリコードとGoコード間の連携をより深く理解し、検証するようになる可能性があります。変数名の一貫性は、このようなツールの開発を容易にし、より堅牢なコードベースを構築する上で役立ちます。
このコミットは、このような問題を未然に防ぎ、Go言語のコードベース全体の品質と保守性を向上させるための、クリーンアップおよび標準化の取り組みの一環として行われました。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
1. Go言語のアセンブリ言語 (Plan 9 Assembler)
Go言語は、独自のツールチェインとアセンブリ言語の構文を使用します。これは、一般的なGAS (GNU Assembler) 構文やIntel構文とは異なります。Goのアセンブリは、元々Bell LabsのPlan 9オペレーティングシステムで使用されていたアセンブラの構文に基づいています。
主な特徴は以下の通りです。
- 擬似命令:
TEXT
,DATA
,GLOBL
などの擬似命令を使用して、関数やデータの宣言を行います。 - レジスタ名: アーキテクチャ固有のレジスタ名を使用します(例:
AX
,BX
,CX
,DX
for x86;R0
,R1
,R2
for ARM)。 - メモリ参照:
offset(base)(index*scale)
の形式でメモリを参照します。 - フレームポインタ (FP): Goのアセンブリでは、関数の引数や戻り値はスタック上に配置されます。これらの値は、
offset(FP)
の形式でアクセスされます。FP
はフレームポインタを表し、offset
はスタックフレームの先頭からのオフセットを示します。
2. Go言語の関数呼び出し規約とスタックフレーム
Go言語の関数呼び出し規約では、関数の引数と戻り値は通常、呼び出し元のスタックフレームに配置されます。
- 引数: 関数に渡される引数は、
TEXT
宣言で指定されたオフセット(通常は0(FP)
から始まる)でアクセスされます。 - 戻り値: 関数の戻り値も、引数の後に続くオフセットでスタック上に配置され、アセンブリコードから書き込まれます。Goの関数宣言で戻り値に名前が付けられている場合(例:
func f() (result int)
)、その名前がアセンブリコード内で参照されます。名前が付けられていない場合、Goコンパイラはデフォルトの名前(例:ret
)を割り当てることがあります。
例えば、func IndexByte(s []byte, c byte) int
というGo関数があった場合、スタック上では以下のように配置される可能性があります(具体的なオフセットはアーキテクチャやコンパイラのバージョンによって異なりますが、相対的な位置関係は保たれます)。
+-------------------+
| ... |
+-------------------+
| c (byte) | <- c+offset(FP)
+-------------------+
| s (slice header) | <- s+offset(FP)
+-------------------+
| return value (int)| <- r+offset(FP) (または ret+offset(FP))
+-------------------+
アセンブリコードは、これらのオフセットと変数名を組み合わせて、スタック上のデータにアクセスします。
3. Plan 9 Assemblerにおける変数名の解決
Plan 9 Assemblerは、TEXT
擬似命令で定義された関数内で、Goの関数宣言における引数名や戻り値名を直接参照することをサポートしています。これは、アセンブリコードの可読性を大幅に向上させる機能です。
例えば、Goの関数が func MyFunc(arg1 int, arg2 string) (retVal bool)
と宣言されている場合、アセンブリコード内では arg1+0(FP)
、arg2+8(FP)
、retVal+16(FP)
のように、Goの変数名をそのまま使用してスタック上の値にアクセスできます。
このコミットは、この機能が既存のアセンブリコードで十分に活用されていなかった部分を修正し、Goの関数宣言とアセンブリコード間の命名規則を統一することを目的としています。
技術的詳細
このコミットの技術的詳細は、Go言語のPlan 9アセンブラにおけるスタックフレームの管理と、変数名の解決メカニズムに深く関連しています。
Goのアセンブリコードでは、TEXT
擬似命令の後に続く関数名に加えて、引数と戻り値の合計サイズをバイト単位で指定します(例: TEXT ·IndexByte(SB),7,$0
の $0
はフレームサイズ、引数と戻り値のサイズは含まれない)。引数と戻り値は、呼び出し元のスタックフレームに配置され、フレームポインタ FP
を基準としたオフセットでアクセスされます。
コミットの変更は、主に以下のパターンに従っています。
-
引数名の変更:
p+0(FP)
->s+0(FP)
(例:bytes.IndexByte
のs []byte
引数)len+4(FP)
->s+4(FP)
(例:bytes.IndexByte
のs []byte
の長さ部分)b+12(FP)
->c+12(FP)
(例:bytes.IndexByte
のc byte
引数)x+0(FP)
->f+0(FP)
(例:math.Frexp
のf float64
引数)x+0(FP)
->p+0(FP)
(例:math.Hypot
のp float64
引数)y+8(FP)
->q+8(FP)
(例:math.Hypot
のq float64
引数)oldlo+4(FP)
->old+4(FP)
(例:sync/atomic
のold
引数)deltalo+4(FP)
->delta+4(FP)
(例:sync/atomic
のdelta
引数)
-
戻り値名の変更:
ret+16(FP)
->r+16(FP)
(例:bytes.IndexByte
のint
戻り値)ret+24(FP)
->r+24(FP)
(例:bytes.Equal
のbool
戻り値)f+8(FP)
->frac+8(FP)
(例:math.Frexp
のfrac float64
戻り値)e+16(FP)
->exp+16(FP)
(例:math.Frexp
のexp int
戻り値)i+8(FP)
->int+8(FP)
(例:math.Modf
のint float64
戻り値)s+8(FP)
->sin+8(FP)
(例:math.Sincos
のsin float64
戻り値)c+16(FP)
->cos+16(FP)
(例:math.Sincos
のcos float64
戻り値)ret+12(FP)
->swapped+12(FP)
(例:sync/atomic.CompareAndSwapUint32
のswapped bool
戻り値)ret+8(FP)
->new+8(FP)
(例:sync/atomic.AddUint32
のnew uint32
戻り値)retlo+12(FP)
->new+12(FP)
(例:sync/atomic.AddUint64
のnew uint64
戻り値)ret+4(FP)
->val+4(FP)
(例:sync/atomic.LoadUint32
のval uint32
戻り値)
これらの変更は、Goの関数宣言で明示的に名前が付けられている引数や戻り値に対して、アセンブリコード内でもその名前を使用するように統一しています。これにより、Goのソースコードとアセンブリコード間のマッピングがより明確になり、開発者が両者を並行して理解する際の認知負荷が軽減されます。
特に、math/big
パッケージのアセンブリファイルでは、n+4(FP)
や n+8(FP)
のような汎用的な引数名が、z+4(FP)
や z+8(FP)
のように、Goの関数宣言における具体的な引数名(この場合は z []Word
の z
)に置き換えられています。これは、n
が「要素数」を意味する一般的な変数名であるのに対し、z
は特定の配列を指すため、より文脈に即した命名と言えます。
この変更は、機能的な振る舞いを一切変更せず、純粋にコードの可読性と保守性を向上させるためのリファクタリングです。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、Go言語の標準ライブラリ内のアセンブリファイル(.s
拡張子を持つファイル)における、関数引数および戻り値の参照名の変更です。
以下に、いくつかの代表的な変更例を挙げます。
1. src/pkg/bytes/asm_386.s
の IndexByte
関数:
変更前:
TEXT ·IndexByte(SB),7,$0
MOVL p+0(FP), SI
MOVL len+4(FP), CX
MOVB b+12(FP), AL
...
MOVL $-1, ret+16(FP)
...
MOVL DI, ret+16(FP)
変更後:
TEXT ·IndexByte(SB),7,$0
MOVL s+0(FP), SI
MOVL s+4(FP), CX
MOVB c+12(FP), AL
...
MOVL $-1, r+16(FP)
...
MOVL DI, r+16(FP)
ここで、Goの bytes.IndexByte
関数は func IndexByte(s []byte, c byte) int
と宣言されており、アセンブリコード内の p
が s
に、len
が s
の長さ部分に、b
が c
に、そして戻り値の ret
が r
にそれぞれ対応するように変更されています。
2. src/pkg/math/frexp_386.s
の Frexp
関数:
変更前:
// func Frexp(x float64) (f float64, e int)
TEXT ·Frexp(SB),7,$0
FMOVD x+0(FP), F0 // F0=x
...
FMOVDP F0, f+8(FP) // F0=e
...
FMOVLP F0, e+16(FP) // (int=int32)
...
FMOVDP F0, f+8(FP) // F0=e
MOVL $0, e+16(FP) // e=0
変更後:
// func Frexp(f float64) (frac float64, exp int)
TEXT ·Frexp(SB),7,$0
FMOVD f+0(FP), F0 // F0=f
...
FMOVDP F0, frac+8(FP) // F0=e
...
FMOVLP F0, exp+16(FP) // (int=int32)
...
FMOVDP F0, frac+8(FP) // F0=e
MOVL $0, exp+16(FP) // exp=0
Goの math.Frexp
関数は func Frexp(f float64) (frac float64, exp int)
と宣言されており、アセンブリコード内の x
が f
に、戻り値の f
が frac
に、e
が exp
にそれぞれ対応するように変更されています。
3. src/pkg/sync/atomic/asm_386.s
の CompareAndSwapUint32
関数:
変更前:
TEXT ·CompareAndSwapUint32(SB),7,$0
...
SETEQ ret+12(FP)
変更後:
TEXT ·CompareAndSwapUint32(SB),7,$0
...
SETEQ swapped+12(FP)
Goの sync/atomic.CompareAndSwapUint32
関数は func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
と宣言されており、戻り値の ret
が swapped
に変更されています。
これらの変更は、アセンブリコード内の変数参照が、対応するGo関数のシグネチャで定義された変数名と完全に一致するように、手作業で修正されたことを示しています。
コアとなるコードの解説
このコミットにおけるコアとなるコードの変更は、Go言語のPlan 9アセンブラの命名規則と、Goコンパイラがスタックフレーム上で引数と戻り値をどのように配置するかという理解に基づいています。
Goのアセンブリでは、TEXT
擬似命令で関数を定義する際に、その関数がGoのどの関数に対応するかを明示します。例えば、TEXT ·IndexByte(SB),7,$0
は、Goの bytes
パッケージの IndexByte
関数に対応します。
アセンブリコード内でGoの関数引数や戻り値にアクセスする場合、変数名+オフセット(FP)
の形式を使用します。ここで、FP
はフレームポインタ(現在のスタックフレームの基準点)を指し、オフセット
はその基準点からのバイト単位の距離を示します。
このコミットの変更は、この 変数名
の部分を、Goの関数宣言で実際に使用されている引数名や戻り値名に合わせるというものです。
例えば、bytes.IndexByte
のGo宣言は func IndexByte(s []byte, c byte) int
です。
s []byte
は、Goの内部ではポインタと長さの2つのワードで表現されます。したがって、s+0(FP)
はs
のポインタ部分、s+4(FP)
(32-bit) またはs+8(FP)
(64-bit) はs
の長さ部分を指します。c byte
は、s
の後に続くオフセットで配置されます。- 戻り値の
int
は、Goの関数宣言で名前が付けられていない場合、コンパイラがret
のようなデフォルト名を割り当てることがありますが、このコミットではr
に統一されています。
変更前のアセンブリコードでは、これらの引数や戻り値に対して、p
, len
, b
, ret
のような汎用的な名前が使われていました。これらはアセンブラにとっては有効なシンボルですが、Goのソースコードを読んだ開発者にとっては、どのGoの変数に対応するのかが直感的ではありませんでした。
このコミットによって、例えば p+0(FP)
が s+0(FP)
に変更されることで、アセンブリコードを読んだ際に「これはGoの IndexByte
関数の s
引数のポインタ部分を参照している」ということが一目でわかるようになります。同様に、ret+16(FP)
が r+16(FP)
に変更されることで、「これはGoの IndexByte
関数の戻り値 r
を設定している」という意図が明確になります。
この変更は、Goのツールチェインがアセンブリコードを解析し、Goの関数シグネチャとアセンブリコード内の変数参照をより密接にリンクさせることを可能にするための基盤ともなります。これにより、将来的にコンパイラやリンカがより高度な最適化や検証を行う際に役立つ可能性があります。
関連リンク
- Go言語のアセンブリに関する公式ドキュメント:
- Plan 9 Assemblerに関する情報:
- Plan 9 from Bell Labs (オリジナルのPlan 9アセンブラのドキュメント)
参考にした情報源リンク
- Go Assembly Language
- A Quick Guide to Go's Assembler
- Go CL 6548046: all: match asm variable names to Go func declarations (このコミットに対応するGoのコードレビューシステム上の変更リスト)
- Go source code on GitHub (Go言語の公式リポジトリ)
- Go issue tracker (Go言語のイシュートラッカー)
- Go Wiki: AssemblyI have provided the detailed explanation of the commit as requested, following all the specified instructions and chapter structure. I have used the commit data and general knowledge about Go's assembly to construct the explanation. I did not need to use
google_web_search
for this specific request as the provided commit diff and my existing knowledge were sufficient to explain the changes.
The output is in Markdown format and printed to standard output only, as per the instructions.# [インデックス 13892] ファイルの概要
このコミットは、Go言語の標準ライブラリ内のアセンブリコードにおける変数名を、対応するGo言語の関数宣言と一致させるための変更です。これにより、Goとアセンブリ間のインターフェースの整合性が向上し、コードの可読性と保守性が高まります。特に、bytes
, hash/crc32
, math
, math/big
, sync/atomic
パッケージ内のアセンブリファイルが影響を受けています。
コミット
commit 85729503834e899324640023f2e969042ed647ff
Author: Russ Cox <rsc@golang.org>
Date: Fri Sep 21 00:35:56 2012 -0400
all: match asm variable names to Go func declarations
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6548046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/85729503834e899324640023f2e969042ed647ff
元コミット内容
このコミットの目的は、Go言語の標準ライブラリに含まれるアセンブリ言語(Plan 9アセンブラ)で書かれたコードにおいて、関数内で使用される変数名(引数や戻り値)を、そのアセンブリ関数に対応するGo言語の関数宣言における変数名と一致させることです。
例えば、Goの関数宣言が func IndexByte(s []byte, c byte) int
であった場合、アセンブリコード内で s
や c
、そして戻り値の ret
を参照する際に、それぞれ s+0(FP)
、c+12(FP)
、r+16(FP)
のように、Goの関数宣言で使われている変数名に合わせる変更が行われています。
変更されたファイルは以下の通りです。
src/pkg/bytes/asm_386.s
src/pkg/bytes/asm_amd64.s
src/pkg/bytes/asm_arm.s
src/pkg/hash/crc32/crc32_amd64.go
src/pkg/hash/crc32/crc32_amd64.s
src/pkg/math/abs_arm.s
src/pkg/math/big/arith_386.s
src/pkg/math/big/arith_amd64.s
src/pkg/math/big/arith_arm.s
src/pkg/math/frexp_386.s
src/pkg/math/hypot_386.s
src/pkg/math/hypot_amd64.s
src/pkg/math/ldexp_386.s
src/pkg/math/modf_386.s
src/pkg/math/sincos_386.s
src/pkg/sync/atomic/asm_386.s
src/pkg/sync/atomic/asm_amd64.s
これらのファイルでは、アセンブリコード内の変数参照(例: p+0(FP)
, len+4(FP)
, ret+16(FP)
など)が、対応するGo関数の引数名や戻り値名(例: s+0(FP)
, s+4(FP)
, r+16(FP)
など)に修正されています。
変更の背景
この変更の背景には、Go言語の設計思想と、アセンブリ言語の利用におけるベストプラクティスがあります。
Go言語は、パフォーマンスが重要な部分でアセンブリ言語を使用することがあります。特に、標準ライブラリの低レベルな操作(バイト操作、ハッシュ計算、数学関数、アトミック操作など)では、特定のアーキテクチャに最適化されたアセンブリコードが用いられることがあります。
しかし、Goのコードとアセンブリコードが密接に連携する場合、両者の間でインターフェースの整合性を保つことが極めて重要です。Goの関数宣言は、その関数の引数と戻り値のセマンティクスを定義します。アセンブリコードがこれらの値を操作する際、Goの宣言と異なる名前で参照していると、以下のような問題が生じます。
- 可読性の低下: Goのコードを読んでいる開発者が、対応するアセンブリコードを読んだ際に、どの変数がGoのどの引数や戻り値に対応するのかを即座に理解するのが難しくなります。これは、デバッグやコードレビューの際に混乱を招きます。
- 保守性の低下: Goの関数宣言が変更された場合、アセンブリコード内の対応する変数名も手動で更新する必要が生じます。名前が一致していないと、この更新作業が見落とされやすくなり、バグの原因となる可能性があります。
- ツールとの連携: 将来的に、GoのツールチェインがアセンブリコードとGoコード間の連携をより深く理解し、検証するようになる可能性があります。変数名の一貫性は、このようなツールの開発を容易にし、より堅牢なコードベースを構築する上で役立ちます。
このコミットは、このような問題を未然に防ぎ、Go言語のコードベース全体の品質と保守性を向上させるための、クリーンアップおよび標準化の取り組みの一環として行われました。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
1. Go言語のアセンブリ言語 (Plan 9 Assembler)
Go言語は、独自のツールチェインとアセンブリ言語の構文を使用します。これは、一般的なGAS (GNU Assembler) 構文やIntel構文とは異なります。Goのアセンブリは、元々Bell LabsのPlan 9オペレーティングシステムで使用されていたアセンブラの構文に基づいています。
主な特徴は以下の通りです。
- 擬似命令:
TEXT
,DATA
,GLOBL
などの擬似命令を使用して、関数やデータの宣言を行います。 - レジスタ名: アーキテクチャ固有のレジスタ名を使用します(例:
AX
,BX
,CX
,DX
for x86;R0
,R1
,R2
for ARM)。 - メモリ参照:
offset(base)(index*scale)
の形式でメモリを参照します。 - フレームポインタ (FP): Goのアセンブリでは、関数の引数や戻り値はスタック上に配置されます。これらの値は、
offset(FP)
の形式でアクセスされます。FP
はフレームポインタを表し、offset
はスタックフレームの先頭からのオフセットを示します。
2. Go言語の関数呼び出し規約とスタックフレーム
Go言語の関数呼び出し規約では、関数の引数と戻り値は通常、呼び出し元のスタックフレームに配置されます。
- 引数: 関数に渡される引数は、
TEXT
宣言で指定されたオフセット(通常は0(FP)
から始まる)でアクセスされます。 - 戻り値: 関数の戻り値も、引数の後に続くオフセットでスタック上に配置され、アセンブリコードから書き込まれます。Goの関数宣言で戻り値に名前が付けられている場合(例:
func f() (result int)
)、その名前がアセンブリコード内で参照されます。名前が付けられていない場合、Goコンパイラはデフォルトの名前(例:ret
)を割り当てることがあります。
例えば、func IndexByte(s []byte, c byte) int
というGo関数があった場合、スタック上では以下のように配置される可能性があります(具体的なオフセットはアーキテクチャやコンパイラのバージョンによって異なりますが、相対的な位置関係は保たれます)。
+-------------------+
| ... |
+-------------------+
| c (byte) | <- c+offset(FP)
+-------------------+
| s (slice header) | <- s+offset(FP)
+-------------------+
| return value (int)| <- r+offset(FP) (または ret+offset(FP))
+-------------------+
アセンブリコードは、これらのオフセットと変数名を組み合わせて、スタック上のデータにアクセスします。
3. Plan 9 Assemblerにおける変数名の解決
Plan 9 Assemblerは、TEXT
擬似命令で定義された関数内で、Goの関数宣言における引数名や戻り値名を直接参照することをサポートしています。これは、アセンブリコードの可読性を大幅に向上させる機能です。
例えば、Goの関数が func MyFunc(arg1 int, arg2 string) (retVal bool)
と宣言されている場合、アセンブリコード内では arg1+0(FP)
、arg2+8(FP)
、retVal+16(FP)
のように、Goの変数名をそのまま使用してスタック上の値にアクセスできます。
このコミットは、この機能が既存のアセンブリコードで十分に活用されていなかった部分を修正し、Goの関数宣言とアセンブリコード間の命名規則を統一することを目的としています。
技術的詳細
このコミットの技術的詳細は、Go言語のPlan 9アセンブラにおけるスタックフレームの管理と、変数名の解決メカニズムに深く関連しています。
Goのアセンブリコードでは、TEXT
擬似命令の後に続く関数名に加えて、引数と戻り値の合計サイズをバイト単位で指定します(例: TEXT ·IndexByte(SB),7,$0
の $0
はフレームサイズ、引数と戻り値のサイズは含まれない)。引数と戻り値は、呼び出し元のスタックフレームに配置され、フレームポインタ FP
を基準としたオフセットでアクセスされます。
コミットの変更は、主に以下のパターンに従っています。
-
引数名の変更:
p+0(FP)
->s+0(FP)
(例:bytes.IndexByte
のs []byte
引数)len+4(FP)
->s+4(FP)
(例:bytes.IndexByte
のs []byte
の長さ部分)b+12(FP)
->c+12(FP)
(例:bytes.IndexByte
のc byte
引数)x+0(FP)
->f+0(FP)
(例:math.Frexp
のf float64
引数)x+0(FP)
->p+0(FP)
(例:math.Hypot
のp float64
引数)y+8(FP)
->q+8(FP)
(例:math.Hypot
のq float64
引数)oldlo+4(FP)
->old+4(FP)
(例:sync/atomic
のold
引数)deltalo+4(FP)
->delta+4(FP)
(例:sync/atomic
のdelta
引数)
-
戻り値名の変更:
ret+16(FP)
->r+16(FP)
(例:bytes.IndexByte
のint
戻り値)ret+24(FP)
->r+24(FP)
(例:bytes.Equal
のbool
戻り値)f+8(FP)
->frac+8(FP)
(例:math.Frexp
のfrac float64
戻り値)e+16(FP)
->exp+16(FP)
(例:math.Frexp
のexp int
戻り値)i+8(FP)
->int+8(FP)
(例:math.Modf
のint float64
戻り値)s+8(FP)
->sin+8(FP)
(例:math.Sincos
のsin float64
戻り値)c+16(FP)
->cos+16(FP)
(例:math.Sincos
のcos float64
戻り値)ret+12(FP)
->swapped+12(FP)
(例:sync/atomic.CompareAndSwapUint32
のswapped bool
戻り値)ret+8(FP)
->new+8(FP)
(例:sync/atomic.AddUint32
のnew uint32
戻り値)retlo+12(FP)
->new+12(FP)
(例:sync/atomic.AddUint64
のnew uint64
戻り値)ret+4(FP)
->val+4(FP)
(例:sync/atomic.LoadUint32
のval uint32
戻り値)
これらの変更は、Goの関数宣言で明示的に名前が付けられている引数や戻り値に対して、アセンブリコード内でもその名前を使用するように統一しています。これにより、Goのソースコードとアセンブリコード間のマッピングがより明確になり、開発者が両者を並行して理解する際の認知負荷が軽減されます。
特に、math/big
パッケージのアセンブリファイルでは、n+4(FP)
や n+8(FP)
のような汎用的な引数名が、z+4(FP)
や z+8(FP)
のように、Goの関数宣言における具体的な引数名(この場合は z []Word
の z
)に置き換えられています。これは、n
が「要素数」を意味する一般的な変数名であるのに対し、z
は特定の配列を指すため、より文脈に即した命名と言えます。
この変更は、機能的な振る舞いを一切変更せず、純粋にコードの可読性と保守性を向上させるためのリファクタリングです。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、Go言語の標準ライブラリ内のアセンブリファイル(.s
拡張子を持つファイル)における、関数引数および戻り値の参照名の変更です。
以下に、いくつかの代表的な変更例を挙げます。
1. src/pkg/bytes/asm_386.s
の IndexByte
関数:
変更前:
TEXT ·IndexByte(SB),7,$0
MOVL p+0(FP), SI
MOVL len+4(FP), CX
MOVB b+12(FP), AL
...
MOVL $-1, ret+16(FP)
...
MOVL DI, ret+16(FP)
変更後:
TEXT ·IndexByte(SB),7,$0
MOVL s+0(FP), SI
MOVL s+4(FP), CX
MOVB c+12(FP), AL
...
MOVL $-1, r+16(FP)
...
MOVL DI, r+16(FP)
ここで、Goの bytes.IndexByte
関数は func IndexByte(s []byte, c byte) int
と宣言されており、アセンブリコード内の p
が s
に、len
が s
の長さ部分に、b
が c
に、そして戻り値の ret
が r
にそれぞれ対応するように変更されています。
2. src/pkg/math/frexp_386.s
の Frexp
関数:
変更前:
// func Frexp(x float64) (f float64, e int)
TEXT ·Frexp(SB),7,$0
FMOVD x+0(FP), F0 // F0=x
...
FMOVDP F0, f+8(FP) // F0=e
...
FMOVLP F0, e+16(FP) // (int=int32)
...
FMOVDP F0, f+8(FP) // F0=e
MOVL $0, e+16(FP) // e=0
変更後:
// func Frexp(f float64) (frac float64, exp int)
TEXT ·Frexp(SB),7,$0
FMOVD f+0(FP), F0 // F0=f
...
FMOVDP F0, frac+8(FP) // F0=e
...
FMOVLP F0, exp+16(FP) // (int=int32)
...
FMOVDP F0, frac+8(FP) // F0=e
MOVL $0, exp+16(FP) // exp=0
Goの math.Frexp
関数は func Frexp(f float64) (frac float64, exp int)
と宣言されており、アセンブリコード内の x
が f
に、戻り値の f
が frac
に、e
が exp
にそれぞれ対応するように変更されています。
3. src/pkg/sync/atomic/asm_386.s
の CompareAndSwapUint32
関数:
変更前:
TEXT ·CompareAndSwapUint32(SB),7,$0
...
SETEQ ret+12(FP)
変更後:
TEXT ·CompareAndSwapUint32(SB),7,$0
...
SETEQ swapped+12(FP)
Goの sync/atomic.CompareAndSwapUint32
関数は func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
と宣言されており、戻り値の ret
が swapped
に変更されています。
これらの変更は、アセンブリコード内の変数参照が、対応するGo関数のシグネチャで定義された変数名と完全に一致するように、手作業で修正されたことを示しています。
コアとなるコードの解説
このコミットにおけるコアとなるコードの変更は、Go言語のPlan 9アセンブラの命名規則と、Goコンパイラがスタックフレーム上で引数と戻り値をどのように配置するかという理解に基づいています。
Goのアセンブリでは、TEXT
擬似命令で関数を定義する際に、その関数がGoのどの関数に対応するかを明示します。例えば、TEXT ·IndexByte(SB),7,$0
は、Goの bytes
パッケージの IndexByte
関数に対応します。
アセンブリコード内でGoの関数引数や戻り値にアクセスする場合、変数名+オフセット(FP)
の形式を使用します。ここで、FP
はフレームポインタ(現在のスタックフレームの基準点)を指し、オフセット
はその基準点からのバイト単位の距離を示します。
このコミットの変更は、この 変数名
の部分を、Goの関数宣言で実際に使用されている引数名や戻り値名に合わせるというものです。
例えば、bytes.IndexByte
のGo宣言は func IndexByte(s []byte, c byte) int
です。
s []byte
は、Goの内部ではポインタと長さの2つのワードで表現されます。したがって、s+0(FP)
はs
のポインタ部分、s+4(FP)
(32-bit) またはs+8(FP)
(64-bit) はs
の長さ部分を指します。c byte
は、s
の後に続くオフセットで配置されます。- 戻り値の
int
は、Goの関数宣言で名前が付けられていない場合、コンパイラがret
のようなデフォルト名を割り当てることがありますが、このコミットではr
に統一されています。
変更前のアセンブリコードでは、これらの引数や戻り値に対して、p
, len
, b
, ret
のような汎用的な名前が使われていました。これらはアセンブラにとっては有効なシンボルですが、Goのソースコードを読んだ開発者にとっては、どのGoの変数に対応するのかが直感的ではありませんでした。
このコミットによって、例えば p+0(FP)
が s+0(FP)
に変更されることで、アセンブリコードを読んだ際に「これはGoの IndexByte
関数の s
引数のポインタ部分を参照している」ということが一目でわかるようになります。同様に、ret+16(FP)
が r+16(FP)
に変更されることで、「これはGoの IndexByte
関数の戻り値 r
を設定している」という意図が明確になります。
この変更は、Goのツールチェインがアセンブリコードを解析し、Goの関数シグネチャとアセンブリコード内の変数参照をより密接にリンクさせることを可能にするための基盤ともなります。これにより、将来的にコンパイラやリンカがより高度な最適化や検証を行う際に役立つ可能性があります。
関連リンク
- Go言語のアセンブリに関する公式ドキュメント:
- Plan 9 Assemblerに関する情報:
- Plan 9 from Bell Labs (オリジナルのPlan 9アセンブラのドキュメント)
参考にした情報源リンク
- Go Assembly Language
- A Quick Guide to Go's Assembler
- Go CL 6548046: all: match asm variable names to Go func declarations (このコミットに対応するGoのコードレビューシステム上の変更リスト)
- Go source code on GitHub (Go言語の公式リポジトリ)
- Go issue tracker (Go言語のイシュートラッカー)
- Go Wiki: Assembly