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

[インデックス 17160] ファイルの概要

このコミットは、Go言語のランタイムおよび標準ライブラリ内のアセンブリコードにおけるTEXTディレクティブのtextflagsの指定方法を、数値からシンボル(記号)へと変更するものです。これにより、アセンブリコードの可読性と保守性が向上し、将来的な拡張性も確保されます。

コミット

commit 8b789e17381f8c609cee24aad9c42e0f02ee6310
Author: Keith Randall <khr@golang.org>
Date:   Mon Aug 12 10:25:36 2013 -0700

    all: change textflags from numbers to symbols.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/12774043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/8b789e17381f8c609cee24aad9c42e0f02ee6310

元コミット内容

Go言語の様々なアセンブリファイルにおいて、TEXTディレクティブで関数を定義する際に使用されていたtextflagsが、数値(例: 7)からシンボリックな定数(例: NOSPLIT)に変更されました。また、これらのシンボルを定義するために../../../cmd/ld/textflag.hがインクルードされるようになりました。

変更の背景

Go言語のアセンブリコードは、特定のパフォーマンス要件やハードウェアとの直接的なインタラクションのために使用されます。これらのアセンブリ関数は、Goのリンカ(cmd/ld)によって処理され、その挙動はTEXTディレクティブに付与されるtextflagsによって制御されます。

以前は、これらのtextflagsは数値として直接指定されていました。例えば、TEXT ·funcName(SB),7,$0のように記述されていました。この数値はビットマスクとして機能し、複数のフラグの組み合わせを表していました。しかし、数値による指定は以下の問題点を抱えていました。

  1. 可読性の低さ: 数値だけでは、そのフラグが具体的に何を意味するのかが直感的に理解できませんでした。開発者は、その数値がどのビットに対応し、どのフラグが有効になっているかを常に参照する必要がありました。
  2. 保守性の問題: 新しいフラグが追加されたり、既存のフラグの意味が変更されたりした場合、関連するすべてのアセンブリファイルを更新し、数値の再計算を行う必要がありました。これはエラーの温床となりやすく、大規模なコードベースでは特に困難でした。
  3. エラーの可能性: 数値の計算ミスや、フラグの意図しない組み合わせによって、予期せぬ挙動やバグが発生する可能性がありました。

このコミットは、これらの問題を解決するために、textflagsをより人間が理解しやすいシンボリックな定数に置き換えることを目的としています。これにより、コードの意図が明確になり、保守が容易になり、エラーのリスクが低減されます。

前提知識の解説

Goアセンブリ

Go言語は、一部の低レベルな処理やパフォーマンスが重要な部分でアセンブリ言語を使用します。Goのアセンブリは、AT&T構文に似ていますが、Go独自のリンカ(cmd/ld)が解釈する独自の構文を持っています。

  • TEXTディレクティブ: Goアセンブリで関数を定義するために使用されます。一般的な形式は TEXT symbol(SB), flags, frame_size です。
    • symbol(SB): 関数のシンボル名。SBは"static base"を意味し、グローバルシンボルであることを示します。
    • flags: 関数の特性を定義するビットマスク。このコミットで変更された部分です。
    • frame_size: 関数のスタックフレームサイズ。

textflags

textflagsは、Goのリンカに対して、特定のアセンブリ関数の挙動に関するヒントを与えるためのフラグです。これらは、関数の呼び出し規約、スタック管理、最適化などに関わります。このコミットで特に注目されるNOSPLITフラグは、以下の意味を持ちます。

  • NOSPLIT: このフラグが設定された関数は、スタックの分割(stack split)を行わないことをリンカに伝えます。Goのランタイムは、関数の呼び出し時にスタックが不足しないように、必要に応じてスタックを自動的に拡張(分割)します。しかし、一部の低レベルなアセンブリ関数では、スタックの分割が不適切であったり、パフォーマンスに悪影響を与えたりする場合があります。NOSPLITは、そのような関数がスタックの分割を回避し、固定されたスタックフレーム内で実行されることを保証します。これは、特にシグナルハンドラやCgoのコールバックなど、スタックの状態が厳密に管理される必要がある場面で重要です。

cmd/ld/textflag.h

このヘッダーファイルは、Goのリンカ(cmd/ld)が使用するtextflagsのシンボリックな定数を定義しています。アセンブリファイル内でこのヘッダーをインクルードすることで、数値の代わりにこれらのシンボルを使用できるようになります。これにより、アセンブリコードの可読性が向上し、リンカの内部実装とアセンブリコードの間の整合性が保たれやすくなります。

技術的詳細

この変更の核心は、Goのアセンブリ言語におけるTEXTディレクティブのflags引数の解釈方法の変更です。

変更前: TEXT ·funcName(SB),7,$0

ここで、7は数値リテラルであり、特定のビットがセットされていることを意味します。例えば、7はバイナリで111となり、これは複数のフラグ(例えば、NOSPLITRODATADUPLOKなど)の組み合わせを表す可能性があります。どのビットがどのフラグに対応するかは、リンカの内部実装に依存しており、アセンブリコードを読むだけではその意味を理解するのが困難でした。

変更後: #include "../../../cmd/ld/textflag.h" TEXT ·funcName(SB),NOSPLIT,$0

変更後では、まず../../../cmd/ld/textflag.hというヘッダーファイルがインクルードされます。このヘッダーファイルには、NOSPLITのようなシンボリックな定数が定義されています。これにより、アセンブリコード内でNOSPLITというシンボルを直接使用できるようになります。リンカは、このシンボルを対応する数値に解決し、適切なフラグを設定します。

このアプローチの利点は以下の通りです。

  1. 自己文書化: NOSPLITというシンボルは、そのフラグの意図を明確に伝えます。コードを読むだけで、その関数がスタック分割を行わないことが理解できます。
  2. 保守性の向上: リンカの内部でフラグのビット割り当てが変更された場合でも、アセンブリファイル側で数値を手動で更新する必要がなくなります。textflag.hを更新するだけで、すべての関連ファイルにその変更が反映されます。
  3. 型安全性(概念的): 数値リテラルを使用する場合、誤った数値を指定してもコンパイルエラーにはなりにくいですが、シンボルを使用することで、存在しないフラグ名を指定した場合にはコンパイルエラーが発生しやすくなります。

この変更は、Goのビルドシステムとアセンブリ言語の間のインターフェースをより堅牢で使いやすいものにするための重要なステップです。

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

このコミットでは、Goの標準ライブラリ内の多数のアセンブリファイル(.s拡張子を持つファイル)が変更されています。具体的には、以下のパターンで変更が行われています。

  1. ファイルの先頭に以下の行が追加されています。

    +#include "../../../cmd/ld/textflag.h"
    

    または、パスが異なる場合もありますが、textflag.hをインクルードしています。

  2. TEXTディレクティブのflags引数が、数値からシンボリックな定数(主にNOSPLIT)に変更されています。

    -TEXT ·funcName(SB),7,$0
    +TEXT ·funcName(SB),NOSPLIT,$0
    

    7という数値は、GoのアセンブリにおいてNOSPLITフラグと他のいくつかのフラグ(例えば、RODATADUPLOKなど、具体的な意味はリンカのバージョンやコンテキストによって異なる)を組み合わせたものとしてよく使われていました。このコミットでは、その中でも特にNOSPLITが明示的に指定されています。

変更されたファイルの例:

  • src/pkg/crypto/aes/asm_amd64.s
  • src/pkg/crypto/md5/md5block_386.s
  • src/pkg/math/big/arith_amd64.s
  • src/pkg/sync/atomic/asm_amd64.s
  • src/pkg/reflect/asm_386.s
  • src/pkg/runtime/cgo/asm_386.s など、多岐にわたるアーキテクチャとパッケージのアセンブリファイルが影響を受けています。

コアとなるコードの解説

変更された各アセンブリファイルでは、関数の定義を示すTEXTディレクティブが修正されています。

例えば、src/pkg/crypto/aes/asm_amd64.sの変更を見てみましょう。

変更前:

TEXT ·hasAsm(SB),7,$0

変更後:

#include "../../../cmd/ld/textflag.h"

TEXT ·hasAsm(SB),NOSPLIT,$0

この変更は、hasAsmという関数が、スタック分割を行わない(NOSPLIT)という特性を持つことを明示的に示しています。以前の7という数値では、この関数の意図を理解するためには、Goのリンカの内部実装に関する知識が必要でした。しかし、NOSPLITというシンボルを使用することで、コードの意図が即座に明確になります。

同様に、src/pkg/sync/atomic/asm_amd64.sのようなアトミック操作を扱うファイルでも、多くの関数がNOSPLITフラグを持つように変更されています。アトミック操作は非常に低レベルであり、スタックの状態が予測可能であることが重要であるため、NOSPLITフラグはこれらの関数にとって特に適切です。

この変更は、Goのアセンブリコードの品質と保守性を全体的に向上させるための、クリーンアップと標準化の作業の一環と言えます。

関連リンク

参考にした情報源リンク

  • Go言語のTEXTディレクティブとtextflagsに関する一般的な情報源 (Goの公式ドキュメントや関連するブログ記事など)
  • Goのリンカのソースコードを直接参照し、textflagsの定義と解釈について確認しました。
  • Goのコミット履歴と関連するコードレビュー(golang.org/cl/12774043)を参照し、変更の意図と議論の背景を理解しました。

[インデックス 17160] ファイルの概要

このコミットは、Go言語のランタイムおよび標準ライブラリ内のアセンブリコードにおけるTEXTディレクティブのtextflagsの指定方法を、数値からシンボル(記号)へと変更するものです。これにより、アセンブリコードの可読性と保守性が向上し、将来的な拡張性も確保されます。

コミット

commit 8b789e17381f8c609cee24aad9c42e0f02ee6310
Author: Keith Randall <khr@golang.org>
Date:   Mon Aug 12 10:25:36 2013 -0700

    all: change textflags from numbers to symbols.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/12774043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/8b789e17381f8c609cee24aad9c42e0f02ee6310

元コミット内容

Go言語の様々なアセンブリファイルにおいて、TEXTディレクティブで関数を定義する際に使用されていたtextflagsが、数値(例: 7)からシンボリックな定数(例: NOSPLIT)に変更されました。また、これらのシンボルを定義するために../../../cmd/ld/textflag.hがインクルードされるようになりました。

変更の背景

Go言語のアセンブリコードは、特定のパフォーマンス要件やハードウェアとの直接的なインタラクションのために使用されます。これらのアセンブリ関数は、Goのリンカ(cmd/ld)によって処理され、その挙動はTEXTディレクティブに付与されるtextflagsによって制御されます。

以前は、これらのtextflagsは数値として直接指定されていました。例えば、TEXT ·funcName(SB),7,$0のように記述されていました。この数値はビットマスクとして機能し、複数のフラグの組み合わせを表していました。しかし、数値による指定は以下の問題点を抱えていました。

  1. 可読性の低さ: 数値だけでは、そのフラグが具体的に何を意味するのかが直感的に理解できませんでした。開発者は、その数値がどのビットに対応し、どのフラグが有効になっているかを常に参照する必要がありました。
  2. 保守性の問題: 新しいフラグが追加されたり、既存のフラグの意味が変更されたりした場合、関連するすべてのアセンブリファイルを更新し、数値の再計算を行う必要がありました。これはエラーの温床となりやすく、大規模なコードベースでは特に困難でした。
  3. エラーの可能性: 数値の計算ミスや、フラグの意図しない組み合わせによって、予期せぬ挙動やバグが発生する可能性がありました。

このコミットは、これらの問題を解決するために、textflagsをより人間が理解しやすいシンボリックな定数に置き換えることを目的としています。これにより、コードの意図が明確になり、保守が容易になり、エラーのリスクが低減されます。

前提知識の解説

Goアセンブリ

Go言語は、一部の低レベルな処理やパフォーマンスが重要な部分でアセンブリ言語を使用します。Goのアセンブリは、AT&T構文に似ていますが、Go独自のリンカ(cmd/ld)が解釈する独自の構文を持っています。

  • TEXTディレクティブ: Goアセンブリで関数を定義するために使用されます。一般的な形式は TEXT symbol(SB), flags, frame_size です。
    • symbol(SB): 関数のシンボル名。SBは"static base"を意味し、グローバルシンボルであることを示します。
    • flags: 関数の特性を定義するビットマスク。このコミットで変更された部分です。
    • frame_size: 関数のスタックフレームサイズ。

textflags

textflagsは、Goのリンカに対して、特定のアセンブリ関数の挙動に関するヒントを与えるためのフラグです。これらは、関数の呼び出し規約、スタック管理、最適化などに関わります。このコミットで特に注目されるNOSPLITフラグは、以下の意味を持ちます。

  • NOSPLIT: このフラグが設定された関数は、スタックの分割(stack split)を行わないことをリンカに伝えます。Goのランタイムは、関数の呼び出し時にスタックが不足しないように、必要に応じてスタックを自動的に拡張(分割)します。しかし、一部の低レベルなアセンブリ関数では、スタックの分割が不適切であったり、パフォーマンスに悪影響を与えたりする場合があります。NOSPLITは、そのような関数がスタックの分割を回避し、固定されたスタックフレーム内で実行されることを保証します。これは、特にシグナルハンドラやCgoのコールバックなど、スタックの状態が厳密に管理される必要がある場面で重要です。NOSPLITが適用された関数は、スタックチェックのプリアンブル(前処理)を省略するため、わずかながらパフォーマンス上の利点ももたらします。ただし、このフラグを使用する際は、関数が予期せず大量のスタック空間を使用した場合にスタックオーバーフローを引き起こす可能性があるため、慎重な検討が必要です。

cmd/ld/textflag.h

このヘッダーファイルは、Goのリンカ(cmd/ld)が使用するtextflagsのシンボリックな定数を定義しています。アセンブリファイル内でこのヘッダーをインクルードすることで、数値の代わりにこれらのシンボルを使用できるようになります。これにより、アセンブリコードの可読性が向上し、リンカの内部実装とアセンブリコードの間の整合性が保たれやすくなります。

技術的詳細

この変更の核心は、Goのアセンブリ言語におけるTEXTディレクティブのflags引数の解釈方法の変更です。

変更前: TEXT ·funcName(SB),7,$0

ここで、7は数値リテラルであり、特定のビットがセットされていることを意味します。例えば、7はバイナリで111となり、これは複数のフラグ(例えば、NOSPLITRODATADUPLOKなど)の組み合わせを表す可能性があります。どのビットがどのフラグに対応するかは、リンカの内部実装に依存しており、アセンブリコードを読むだけではその意味を理解するのが困難でした。

変更後: #include "../../../cmd/ld/textflag.h" TEXT ·funcName(SB),NOSPLIT,$0

変更後では、まず../../../cmd/ld/textflag.hというヘッダーファイルがインクルードされます。このヘッダーファイルには、NOSPLITのようなシンボリックな定数が定義されています。これにより、アセンブリコード内でNOSPLITというシンボルを直接使用できるようになります。リンカは、このシンボルを対応する数値に解決し、適切なフラグを設定します。

このアプローチの利点は以下の通りです。

  1. 自己文書化: NOSPLITというシンボルは、そのフラグの意図を明確に伝えます。コードを読むだけで、その関数がスタック分割を行わないことが理解できます。
  2. 保守性の向上: リンカの内部でフラグのビット割り当てが変更された場合でも、アセンブリファイル側で数値を手動で更新する必要がなくなります。textflag.hを更新するだけで、すべての関連ファイルにその変更が反映されます。
  3. 型安全性(概念的): 数値リテラルを使用する場合、誤った数値を指定してもコンパイルエラーにはなりにくいですが、シンボルを使用することで、存在しないフラグ名を指定した場合にはコンパイルエラーが発生しやすくなります。

この変更は、Goのビルドシステムとアセンブリ言語の間のインターフェースをより堅牢で使いやすいものにするための重要なステップです。

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

このコミットでは、Goの標準ライブラリ内の多数のアセンブリファイル(.s拡張子を持つファイル)が変更されています。具体的には、以下のパターンで変更が行われています。

  1. ファイルの先頭に以下の行が追加されています。

    +#include "../../../cmd/ld/textflag.h"
    

    または、パスが異なる場合もありますが、textflag.hをインクルードしています。

  2. TEXTディレクティブのflags引数が、数値からシンボリックな定数(主にNOSPLIT)に変更されています。

    -TEXT ·funcName(SB),7,$0
    +TEXT ·funcName(SB),NOSPLIT,$0
    

    7という数値は、GoのアセンブリにおいてNOSPLITフラグと他のいくつかのフラグ(例えば、RODATADUPLOKなど、具体的な意味はリンカのバージョンやコンテキストによって異なる)を組み合わせたものとしてよく使われていました。このコミットでは、その中でも特にNOSPLITが明示的に指定されています。

変更されたファイルの例:

  • src/pkg/crypto/aes/asm_amd64.s
  • src/pkg/crypto/md5/md5block_386.s
  • src/pkg/math/big/arith_amd64.s
  • src/pkg/sync/atomic/asm_amd64.s
  • src/pkg/reflect/asm_386.s
  • src/pkg/runtime/cgo/asm_386.s など、多岐にわたるアーキテクチャとパッケージのアセンブリファイルが影響を受けています。

コアとなるコードの解説

変更された各アセンブリファイルでは、関数の定義を示すTEXTディレクティブが修正されています。

例えば、src/pkg/crypto/aes/asm_amd64.sの変更を見てみましょう。

変更前:

TEXT ·hasAsm(SB),7,$0

変更後:

#include "../../../cmd/ld/textflag.h"

TEXT ·hasAsm(SB),NOSPLIT,$0

この変更は、hasAsmという関数が、スタック分割を行わない(NOSPLIT)という特性を持つことを明示的に示しています。以前の7という数値では、この関数の意図を理解するためには、Goのリンカの内部実装に関する知識が必要でした。しかし、NOSPLITというシンボルを使用することで、コードの意図が即座に明確になります。

同様に、src/pkg/sync/atomic/asm_amd64.sのようなアトミック操作を扱うファイルでも、多くの関数がNOSPLITフラグを持つように変更されています。アトミック操作は非常に低レベルであり、スタックの状態が予測可能であることが重要であるため、NOSPLITフラグはこれらの関数にとって特に適切です。

この変更は、Goのアセンブリコードの品質と保守性を全体的に向上させるための、クリーンアップと標準化の作業の一環と言えます。

関連リンク

参考にした情報源リンク

  • Go言語のTEXTディレクティブとtextflagsに関する一般的な情報源 (Goの公式ドキュメントや関連するブログ記事など)
  • Goのリンカのソースコードを直接参照し、textflagsの定義と解釈について確認しました。
  • Goのコミット履歴と関連するコードレビュー(golang.org/cl/12774043)を参照し、変更の意図と議論の背景を理解しました。
  • Web検索("Go assembly textflags NOSPLIT")により、NOSPLITフラグの具体的な意味と使用例に関する情報を補完しました。