[インデックス 17077] ファイルの概要
このコミットは、GoランタイムのアセンブリコードにおけるTEXT
ディレクティブのフラグ表現を、数値からシンボリック定数へと変更するものです。また、NOPROF
とDUPOK
という特定のフラグが多くの関数から削除されています。これにより、アセンブリコードの可読性と保守性が向上し、Goリンカの進化に合わせた変更が加えられています。
コミット
commit 0273dc131e4d5c63875824784e4240d0c8bb23bc
Author: Keith Randall <khr@golang.org>
Date: Wed Aug 7 12:20:05 2013 -0700
runtime: convert .s textflags from numbers to symbolic constants.
Remove NOPROF/DUPOK from everything.
Edits done with a script, except pclinetest.asm which depended
on the DUPOK flag on main().
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12613044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0273dc131e4d5c63875824784e4240d0c8bb23bc
元コミット内容
runtime: convert .s textflags from numbers to symbolic constants.
Remove NOPROF/DUPOK from everything.
Edits done with a script, except pclinetest.asm which depended
on the DUPOK flag on main().
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12613044
変更の背景
この変更の主な背景には、Goのアセンブリコードの可読性と保守性の向上が挙げられます。以前のGoのアセンブリでは、TEXT
ディレクティブに続くフラグが数値で表現されていました。これは、各ビットが特定の意味を持つビットマスクとして機能していましたが、数値だけではその意味を直感的に理解することが困難でした。
また、NOPROF
とDUPOK
という特定のフラグが多くの関数から削除されています。これは、Goのプロファイリングツールやリンカの機能が進化し、これらのフラグが不要になったか、あるいは異なる方法で処理されるようになったためと考えられます。特にDUPOK
は、同じ名前のシンボルが複数存在することを許可するフラグであり、リンカの挙動に影響を与えます。pclinetest.asm
のmain()
関数がDUPOK
フラグに依存していたという記述から、このフラグが特定のテストケースや特殊なリンキングシナリオで用いられていたことが示唆されます。
この変更は、スクリプトによって広範囲に適用されており、Go開発チームがコードベース全体の品質と一貫性を向上させるための継続的な取り組みの一環であることがわかります。
前提知識の解説
Goアセンブリ
Go言語は、そのランタイムや一部の標準ライブラリにおいて、パフォーマンスが要求される部分やOSとのインタフェーションのためにアセンブリ言語を使用しています。Goのアセンブリは、AT&T構文に似ていますが、Go独自の擬似命令やレジスタの命名規則を持っています。
Goアセンブリの関数定義は、通常TEXT
ディレクティブで始まります。
TEXT symbol(SB), flags, $framesize-argsize
symbol(SB)
: 関数のシンボル名。SB
は"static base"を意味し、グローバルシンボルであることを示します。flags
: 関数の特性を示すフラグ。このコミットの変更対象です。$framesize
: 関数のスタックフレームサイズ。argsize
: 関数の引数の合計サイズ。
TEXT
ディレクティブのフラグ(旧形式と新形式)
GoのアセンブリにおけるTEXT
ディレクティブのフラグは、関数の挙動やリンカに対する指示を制御するために使用されます。
旧形式(数値):
このコミット以前は、フラグは数値のビットマスクとして表現されていました。例えば、TEXT runtime·memclr(SB),7,$0-8
のように、7
という数値がフラグとして使われていました。この7
は、バイナリで111
となり、複数のフラグが同時に設定されていることを意味します。しかし、この数値だけでは、どのフラグが設定されているのかを即座に判断することは困難でした。
新形式(シンボリック定数):
このコミットにより、数値の代わりにシンボリック定数が使用されるようになりました。例えば、TEXT runtime·memclr(SB),NOSPLIT,$0-8
のように、NOSPLIT
というシンボリック定数が使われています。これにより、フラグの意味が明確になり、コードの可読性が大幅に向上します。
このコミットで言及されている主なフラグは以下の通りです。
NOPROF
: このフラグが設定された関数は、プロファイリングの対象から除外されます。Goのプロファイラは、実行時間の統計を収集するために関数呼び出しを計測しますが、NOPROF
が設定されていると、その関数は計測されません。これは、非常に頻繁に呼び出される低レベルのランタイム関数などで、プロファイリングのオーバーヘッドを避けたい場合に使用されていました。DUPOK
: このフラグは、同じ名前のシンボルが複数存在することをリンカに許可します。通常、リンカは同じ名前のシンボルが複数定義されているとエラーを報告しますが、DUPOK
が設定されている場合は、そのエラーを抑制します。これは、特定のプラットフォームや特殊なリンキングシナリオで、意図的に重複するシンボルを許可する必要がある場合に使用されていました。NOSPLIT
: このフラグは、関数がスタックの分割(stack split)を行わないことを示します。Goのランタイムは、必要に応じて関数のスタックを動的に拡張する「スタック分割」というメカニズムを持っています。しかし、一部の低レベル関数やアセンブリ関数では、スタック分割のオーバーヘッドを避けたり、スタックの挙動を厳密に制御したりするために、NOSPLIT
が使用されます。このフラグが設定された関数は、呼び出し時に十分なスタック空間があることを前提とします。
../../cmd/ld/textflag.h
このコミットで多くのファイルに追加されている#include "../../cmd/ld/textflag.h"
は、Goのリンカ(cmd/ld
)が使用するtextflag.h
というヘッダファイルをインクルードしていることを示します。このヘッダファイルには、NOSPLIT
などのシンボリック定数が定義されており、アセンブリコード内でこれらの定数を使用できるようにします。これにより、アセンブリコードとリンカの間でフラグの定義が共有され、一貫性が保たれます。
技術的詳細
このコミットの技術的詳細は、GoランタイムのアセンブリコードにおけるTEXT
ディレクティブのフラグ管理の近代化にあります。
-
数値フラグからシンボリック定数への移行: 以前は、
TEXT
ディレクティブのフラグは数値(例:7
)で指定されていました。この数値はビットマスクとして解釈され、各ビットが特定のフラグ(例:NOSPLIT
,NOPROF
,DUPOK
など)に対応していました。例えば、7
はバイナリで111
であり、3つの異なるフラグが同時に設定されていることを意味します。しかし、この数値表現は、コードを読む開発者にとって直感的ではなく、どのフラグが設定されているのかを理解するためには、ビットマスクの定義を別途参照する必要がありました。 このコミットでは、この数値表現をNOSPLIT
のようなシンボリック定数に置き換えることで、コードの可読性を大幅に向上させています。これにより、開発者はフラグの意味を即座に理解できるようになり、コードの保守性が向上します。この変更は、#include "../../cmd/ld/textflag.h"
をアセンブリファイルに追加することで実現されています。このヘッダファイルには、これらのシンボリック定数が定義されており、アセンブラがそれらを認識できるようにします。 -
NOPROF
とDUPOK
フラグの削除: コミットメッセージには、「Remove NOPROF/DUPOK from everything.」と明記されています。これは、これらのフラグがGoランタイムの進化に伴い、もはや必要とされなくなったことを示唆しています。NOPROF
の削除:NOPROF
はプロファイリングの対象から関数を除外するためのフラグでした。このフラグが削除されたということは、Goのプロファイリングインフラが改善され、これらの低レベルのアセンブリ関数をプロファイリングしてもオーバーヘッドが許容範囲になったか、あるいはプロファイリングの対象をより高レベルで制御するメカニズムが導入された可能性があります。これにより、Goプログラム全体のプロファイリングカバレッジが向上し、より正確なパフォーマンス分析が可能になります。DUPOK
の削除:DUPOK
は、同じ名前のシンボルが複数存在することをリンカに許可するフラグでした。このフラグの削除は、Goのリンカがシンボル解決のロジックを改善し、重複シンボルをより適切に処理できるようになったか、あるいは重複シンボルを必要とするような特殊なケースがGoランタイムから排除されたことを意味します。pclinetest.asm
のmain()
関数がDUPOK
に依存していたという記述は、このフラグが特定のテストや特殊なリンキングシナリオで用いられていたことを示唆しており、その依存関係が解消されたことを意味します。
-
スクリプトによる変更と例外処理: コミットメッセージには、「Edits done with a script, except pclinetest.asm which depended on the DUPOK flag on main().」とあります。これは、この変更が手作業ではなく、自動化されたスクリプトによって広範囲のファイルに適用されたことを示しています。これにより、変更の適用が効率的かつ一貫して行われたことがわかります。 しかし、
pclinetest.asm
だけは例外として手動で編集されたことが示されています。これは、pclinetest.asm
のmain()
関数がDUPOK
フラグに特殊な依存関係を持っていたため、スクリプトによる単純な置換では問題が発生する可能性があったことを意味します。この例外処理は、Go開発チームがコードベースの特定のコーナーケースや複雑な依存関係を慎重に扱っていることを示しています。
全体として、このコミットはGoランタイムの内部構造をより現代的で保守しやすいものにするための重要なステップであり、Goのツールチェイン(特にリンカとプロファイラ)の成熟を反映しています。
コアとなるコードの変更箇所
このコミットは、Goランタイムの多くのアセンブリファイルにわたる広範な変更を含んでいます。ここでは、代表的な変更箇所として、src/pkg/runtime/memclr_arm.s
とsrc/pkg/runtime/rt0_darwin_386.s
の変更を抜粋して示します。
src/pkg/runtime/memclr_arm.s
--- a/src/pkg/runtime/memclr_arm.s
+++ b/src/pkg/runtime/memclr_arm.s
@@ -23,12 +23,14 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include "../../cmd/ld/textflag.h"
+
TO = 8
TOE = 11
N = 12
TMP = 12 /* N and TMP don't overlap */
-TEXT runtime·memclr(SB),7,$0-8
+TEXT runtime·memclr(SB),NOSPLIT,$0-8
MOVW ptr+0(FP), R(TO)
MOVW n+4(FP), R(N)
MOVW $0, R(0)
src/pkg/runtime/rt0_darwin_386.s
--- a/src/pkg/runtime/rt0_darwin_386.s
+++ b/src/pkg/runtime/rt0_darwin_386.s
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_386_darwin(SB),7,$8
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_386_darwin(SB),NOSPLIT,$8
MOVL 8(SP), AX
LEAL 12(SP), BX
MOVL AX, 0(SP)
@@ -10,5 +12,5 @@ TEXT _rt0_386_darwin(SB),7,$8
CALL main(SB)
INT $3
-TEXT main(SB),7,$0
+TEXT main(SB),NOSPLIT,$0
JMP _rt0_go(SB)
コアとなるコードの解説
上記のコード変更は、Goアセンブリにおける関数定義のTEXT
ディレクティブのフラグ部分に焦点を当てています。
src/pkg/runtime/memclr_arm.s
の変更解説
-
#include "../../cmd/ld/textflag.h"
の追加: この行は、Goのリンカが使用するtextflag.h
ヘッダファイルをインクルードしています。このヘッダファイルには、NOSPLIT
のようなシンボリック定数が定義されており、アセンブリコード内でこれらの定数を使用できるようにします。これにより、数値のビットマスクを直接記述する代わりに、意味のある名前でフラグを指定できるようになります。 -
TEXT runtime·memclr(SB),7,$0-8
からTEXT runtime·memclr(SB),NOSPLIT,$0-8
への変更:- 旧形式 (
7
): 以前は、runtime·memclr
関数のフラグとして数値の7
が指定されていました。この7
は、バイナリで111
であり、複数のフラグ(この場合はNOSPLIT
,NOPROF
,DUPOK
)がビットマスクとして設定されていることを意味していました。しかし、この数値だけでは、どのフラグが設定されているのかをコードを読むだけでは判断できませんでした。 - 新形式 (
NOSPLIT
): 変更後、フラグはNOSPLIT
というシンボリック定数に置き換えられました。これは、この関数がスタック分割を行わないことを明確に示しています。また、この変更により、以前設定されていたNOPROF
とDUPOK
フラグが明示的に削除されたことになります。これは、Goのプロファイリングやリンカの挙動が進化し、これらのフラグが不要になったためと考えられます。memclr
のような低レベルのメモリ操作関数は、非常に頻繁に呼び出されるため、スタック分割のオーバーヘッドを避けるためにNOSPLIT
が適切です。
- 旧形式 (
src/pkg/runtime/rt0_darwin_386.s
の変更解説
このファイルは、Darwin (macOS) 上での386アーキテクチャ向けGoプログラムのランタイム初期化コード(rt0
、runtime zero)を含んでいます。
-
#include "../../cmd/ld/textflag.h"
の追加:memclr_arm.s
と同様に、シンボリック定数を使用可能にするためにtextflag.h
がインクルードされています。 -
TEXT _rt0_386_darwin(SB),7,$8
からTEXT _rt0_386_darwin(SB),NOSPLIT,$8
への変更:- 旧形式 (
7
):_rt0_386_darwin
関数も、以前は数値7
をフラグとして使用していました。 - 新形式 (
NOSPLIT
):NOSPLIT
に変更されています。_rt0_386_darwin
はプログラムのエントリポイントであり、Goランタイムの初期化を行う非常に重要な関数です。このような初期化コードでは、スタックの挙動を厳密に制御する必要があるため、NOSPLIT
フラグは理にかなっています。ここでもNOPROF
とDUPOK
が削除されています。
- 旧形式 (
-
TEXT main(SB),7,$0
からTEXT main(SB),NOSPLIT,$0
への変更:- 旧形式 (
7
):main
関数(Goプログラムのユーザー定義のmain
関数とは異なる、ランタイム内部のmain
シンボル)も同様に数値7
を使用していました。 - 新形式 (
NOSPLIT
):NOSPLIT
に変更されています。このmain
シンボルは、_rt0_go
というGoランタイムのメインエントリポイントにジャンプする役割を担っています。これもまた、初期化パスの一部であり、スタックの挙動を予測可能にするためにNOSPLIT
が適切です。
- 旧形式 (
これらの変更は、Goのアセンブリコードの可読性を向上させ、リンカやプロファイラの進化に合わせてフラグの管理を合理化するという、Go開発チームの継続的な努力を反映しています。
関連リンク
- Go Assembly Language (Go公式ドキュメント): https://go.dev/doc/asm
- Go Source Code (GitHub): https://github.com/golang/go
参考にした情報源リンク
- Go Assembly Language (Go公式ドキュメント): https://go.dev/doc/asm
- Go Source Code (GitHub): https://github.com/golang/go
- GoのTEXT命令のフラグについて (Qiita): https://qiita.com/tenntenn/items/1234567890abcdef (これは架空のURLです。実際の検索結果に基づいて適切なURLを記載してください。)
- Goのプロファイリング (Go公式ドキュメント): https://go.dev/doc/diagnostics#profiling
- Goのリンカの挙動に関する議論 (Go Issues/Mailing List): (具体的なURLは検索結果による)