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

[インデックス 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 であった場合、アセンブリコード内で sc、そして戻り値の 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の宣言と異なる名前で参照していると、以下のような問題が生じます。

  1. 可読性の低下: Goのコードを読んでいる開発者が、対応するアセンブリコードを読んだ際に、どの変数がGoのどの引数や戻り値に対応するのかを即座に理解するのが難しくなります。これは、デバッグやコードレビューの際に混乱を招きます。
  2. 保守性の低下: Goの関数宣言が変更された場合、アセンブリコード内の対応する変数名も手動で更新する必要が生じます。名前が一致していないと、この更新作業が見落とされやすくなり、バグの原因となる可能性があります。
  3. ツールとの連携: 将来的に、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.IndexBytes []byte 引数)
    • len+4(FP) -> s+4(FP) (例: bytes.IndexBytes []byte の長さ部分)
    • b+12(FP) -> c+12(FP) (例: bytes.IndexBytec byte 引数)
    • x+0(FP) -> f+0(FP) (例: math.Frexpf float64 引数)
    • x+0(FP) -> p+0(FP) (例: math.Hypotp float64 引数)
    • y+8(FP) -> q+8(FP) (例: math.Hypotq float64 引数)
    • oldlo+4(FP) -> old+4(FP) (例: sync/atomicold 引数)
    • deltalo+4(FP) -> delta+4(FP) (例: sync/atomicdelta 引数)
  • 戻り値名の変更:

    • ret+16(FP) -> r+16(FP) (例: bytes.IndexByteint 戻り値)
    • ret+24(FP) -> r+24(FP) (例: bytes.Equalbool 戻り値)
    • f+8(FP) -> frac+8(FP) (例: math.Frexpfrac float64 戻り値)
    • e+16(FP) -> exp+16(FP) (例: math.Frexpexp int 戻り値)
    • i+8(FP) -> int+8(FP) (例: math.Modfint float64 戻り値)
    • s+8(FP) -> sin+8(FP) (例: math.Sincossin float64 戻り値)
    • c+16(FP) -> cos+16(FP) (例: math.Sincoscos float64 戻り値)
    • ret+12(FP) -> swapped+12(FP) (例: sync/atomic.CompareAndSwapUint32swapped bool 戻り値)
    • ret+8(FP) -> new+8(FP) (例: sync/atomic.AddUint32new uint32 戻り値)
    • retlo+12(FP) -> new+12(FP) (例: sync/atomic.AddUint64new uint64 戻り値)
    • ret+4(FP) -> val+4(FP) (例: sync/atomic.LoadUint32val uint32 戻り値)

これらの変更は、Goの関数宣言で明示的に名前が付けられている引数や戻り値に対して、アセンブリコード内でもその名前を使用するように統一しています。これにより、Goのソースコードとアセンブリコード間のマッピングがより明確になり、開発者が両者を並行して理解する際の認知負荷が軽減されます。

特に、math/big パッケージのアセンブリファイルでは、n+4(FP)n+8(FP) のような汎用的な引数名が、z+4(FP)z+8(FP) のように、Goの関数宣言における具体的な引数名(この場合は z []Wordz)に置き換えられています。これは、n が「要素数」を意味する一般的な変数名であるのに対し、z は特定の配列を指すため、より文脈に即した命名と言えます。

この変更は、機能的な振る舞いを一切変更せず、純粋にコードの可読性と保守性を向上させるためのリファクタリングです。

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

このコミットのコアとなる変更は、Go言語の標準ライブラリ内のアセンブリファイル(.s 拡張子を持つファイル)における、関数引数および戻り値の参照名の変更です。

以下に、いくつかの代表的な変更例を挙げます。

1. src/pkg/bytes/asm_386.sIndexByte 関数:

変更前:

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 と宣言されており、アセンブリコード内の ps に、lens の長さ部分に、bc に、そして戻り値の retr にそれぞれ対応するように変更されています。

2. src/pkg/math/frexp_386.sFrexp 関数:

変更前:

// 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) と宣言されており、アセンブリコード内の xf に、戻り値の ffrac に、eexp にそれぞれ対応するように変更されています。

3. src/pkg/sync/atomic/asm_386.sCompareAndSwapUint32 関数:

変更前:

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) と宣言されており、戻り値の retswapped に変更されています。

これらの変更は、アセンブリコード内の変数参照が、対応する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の関数シグネチャとアセンブリコード内の変数参照をより密接にリンクさせることを可能にするための基盤ともなります。これにより、将来的にコンパイラやリンカがより高度な最適化や検証を行う際に役立つ可能性があります。

関連リンク

参考にした情報源リンク

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 であった場合、アセンブリコード内で sc、そして戻り値の 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の宣言と異なる名前で参照していると、以下のような問題が生じます。

  1. 可読性の低下: Goのコードを読んでいる開発者が、対応するアセンブリコードを読んだ際に、どの変数がGoのどの引数や戻り値に対応するのかを即座に理解するのが難しくなります。これは、デバッグやコードレビューの際に混乱を招きます。
  2. 保守性の低下: Goの関数宣言が変更された場合、アセンブリコード内の対応する変数名も手動で更新する必要が生じます。名前が一致していないと、この更新作業が見落とされやすくなり、バグの原因となる可能性があります。
  3. ツールとの連携: 将来的に、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.IndexBytes []byte 引数)
    • len+4(FP) -> s+4(FP) (例: bytes.IndexBytes []byte の長さ部分)
    • b+12(FP) -> c+12(FP) (例: bytes.IndexBytec byte 引数)
    • x+0(FP) -> f+0(FP) (例: math.Frexpf float64 引数)
    • x+0(FP) -> p+0(FP) (例: math.Hypotp float64 引数)
    • y+8(FP) -> q+8(FP) (例: math.Hypotq float64 引数)
    • oldlo+4(FP) -> old+4(FP) (例: sync/atomicold 引数)
    • deltalo+4(FP) -> delta+4(FP) (例: sync/atomicdelta 引数)
  • 戻り値名の変更:

    • ret+16(FP) -> r+16(FP) (例: bytes.IndexByteint 戻り値)
    • ret+24(FP) -> r+24(FP) (例: bytes.Equalbool 戻り値)
    • f+8(FP) -> frac+8(FP) (例: math.Frexpfrac float64 戻り値)
    • e+16(FP) -> exp+16(FP) (例: math.Frexpexp int 戻り値)
    • i+8(FP) -> int+8(FP) (例: math.Modfint float64 戻り値)
    • s+8(FP) -> sin+8(FP) (例: math.Sincossin float64 戻り値)
    • c+16(FP) -> cos+16(FP) (例: math.Sincoscos float64 戻り値)
    • ret+12(FP) -> swapped+12(FP) (例: sync/atomic.CompareAndSwapUint32swapped bool 戻り値)
    • ret+8(FP) -> new+8(FP) (例: sync/atomic.AddUint32new uint32 戻り値)
    • retlo+12(FP) -> new+12(FP) (例: sync/atomic.AddUint64new uint64 戻り値)
    • ret+4(FP) -> val+4(FP) (例: sync/atomic.LoadUint32val uint32 戻り値)

これらの変更は、Goの関数宣言で明示的に名前が付けられている引数や戻り値に対して、アセンブリコード内でもその名前を使用するように統一しています。これにより、Goのソースコードとアセンブリコード間のマッピングがより明確になり、開発者が両者を並行して理解する際の認知負荷が軽減されます。

特に、math/big パッケージのアセンブリファイルでは、n+4(FP)n+8(FP) のような汎用的な引数名が、z+4(FP)z+8(FP) のように、Goの関数宣言における具体的な引数名(この場合は z []Wordz)に置き換えられています。これは、n が「要素数」を意味する一般的な変数名であるのに対し、z は特定の配列を指すため、より文脈に即した命名と言えます。

この変更は、機能的な振る舞いを一切変更せず、純粋にコードの可読性と保守性を向上させるためのリファクタリングです。

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

このコミットのコアとなる変更は、Go言語の標準ライブラリ内のアセンブリファイル(.s 拡張子を持つファイル)における、関数引数および戻り値の参照名の変更です。

以下に、いくつかの代表的な変更例を挙げます。

1. src/pkg/bytes/asm_386.sIndexByte 関数:

変更前:

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 と宣言されており、アセンブリコード内の ps に、lens の長さ部分に、bc に、そして戻り値の retr にそれぞれ対応するように変更されています。

2. src/pkg/math/frexp_386.sFrexp 関数:

変更前:

// 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) と宣言されており、アセンブリコード内の xf に、戻り値の ffrac に、eexp にそれぞれ対応するように変更されています。

3. src/pkg/sync/atomic/asm_386.sCompareAndSwapUint32 関数:

変更前:

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) と宣言されており、戻り値の retswapped に変更されています。

これらの変更は、アセンブリコード内の変数参照が、対応する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の関数シグネチャとアセンブリコード内の変数参照をより密接にリンクさせることを可能にするための基盤ともなります。これにより、将来的にコンパイラやリンカがより高度な最適化や検証を行う際に役立つ可能性があります。

関連リンク

参考にした情報源リンク