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

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

このコミットは、Go言語のリンカ (cmd/ld) およびランタイムのアセンブリコード (pkg/runtime/asm_*.s) における textflag 定数の管理方法を変更するものです。具体的には、これまで各リンカのヘッダファイル (5.out.h, 6.out.h, 8.out.h) に散在していたこれらの定数を、src/cmd/ld/textflag.h という新しい共通ヘッダファイルに集約し、アセンブリコード内で数値リテラルではなく意味のある定数名を使用できるように改善しています。

コミット

commit 5a54696d78003a1e5c17ea9d818dc00e85624c2c
Author: Keith Randall <khr@golang.org>
Date:   Wed Aug 7 10:23:24 2013 -0700

    cmd/ld: Put the textflag constants in a separate file.
    We can then include this file in assembly to replace
    cryptic constants like "7" with meaningful constants
    like "(NOPROF|DUPOK|NOSPLIT)".
    
    Converting just pkg/runtime/asm*.s for now.  Dropping NOPROF
    and DUPOK from lots of places where they aren't needed.
    More .s files to come in a subsequent changelist.
    
    A nonzero number in the textflag field now means
    "has not been converted yet".
    
    R=golang-dev, daniel.morsing, rsc, khr
    CC=golang-dev
    https://golang.org/cl/12568043

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

https://github.com/golang/go/commit/5a54696d78003a1e5c17ea9d818dc00e85624c2c

元コミット内容

cmd/ld: Put the textflag constants in a separate file. We can then include this file in assembly to replace cryptic constants like "7" with meaningful constants like "(NOPROF|DUPOK|NOSPLIT)".

Converting just pkg/runtime/asm*.s for now. Dropping NOPROF and DUPOK from lots of places where they aren't needed. More .s files to come in a subsequent changelist.

A nonzero number in the textflag field now means "has not been converted yet".

変更の背景

Go言語のコンパイラとリンカは、アセンブリコード内で関数やデータセクションの特性を定義するために、特定のフラグ(textflag)を使用します。これまでは、これらのフラグが数値リテラル(例: 7)としてアセンブリファイルに直接記述されており、その意味を理解するためにはリンカのヘッダファイル(例: 5.out.h)を参照する必要がありました。これはコードの可読性を著しく低下させ、メンテナンスを困難にしていました。

このコミットの主な目的は、以下の課題を解決することです。

  1. 可読性の向上: 7 のようなマジックナンバーを NOSPLITRODATA といった意味のある定数名に置き換えることで、アセンブリコードの意図をより明確にする。
  2. 一元管理: textflag 定数を共通のヘッダファイルに集約することで、リンカとアセンブラ間での定数定義の整合性を保ち、将来的な変更や拡張を容易にする。
  3. 不要なフラグの削除: 多くの場所で不要となっていた NOPROFDUPOK フラグを削除し、コードの簡潔化と最適化を図る。

この変更は、Goコンパイラとリンカの内部構造を改善し、開発者がアセンブリコードをより容易に理解し、保守できるようにするための重要なステップです。

前提知識の解説

このコミットを理解するためには、以下の概念についての知識が必要です。

Go言語のアセンブリ

Go言語は、一部のパフォーマンスクリティカルな部分や、OSとのインタフェース部分にアセンブリ言語を使用しています。Goのアセンブリは、AT&T構文に似ていますが、独自の擬似命令やレジスタの命名規則を持っています。Goのアセンブリファイルは通常 .s 拡張子を持ち、TEXT ディレクティブで関数を、DATA ディレクティブでデータを定義します。

TEXT ディレクティブと textflag

Goのアセンブリにおける TEXT ディレクティブは、関数の定義に使用されます。その構文は以下のようになります。

TEXT symbol(SB), textflag, $framesize-argsize
  • symbol(SB): 関数のシンボル名。SB は "static base" を意味し、グローバルシンボルであることを示します。
  • textflag: 関数の特性を定義するフラグのビットマスク。これがこのコミットの主要な変更点です。
  • $framesize: 関数のスタックフレームサイズ。
  • argsize: 関数の引数の合計サイズ。

textflag は、リンカに対して関数がどのように扱われるべきかを指示するビットフラグの集合です。このコミットで言及されている主な textflag 定数は以下の通りです。

  • NOPROF (1<<0): プロファイリングの対象から除外されることを示します。このコミットでは「deprecated(非推奨)」とされています。
  • DUPOK (1<<1): リンカがこのシンボルの複数の定義を見つけても問題ないことを示します。リンカは重複する定義の中から一つを選択します。
  • NOSPLIT (1<<2): スタックチェックのプリアンブル(関数呼び出し時にスタックが足りるかを確認するコード)を挿入しないことを示します。これは、スタックを消費しない、または非常に小さいスタックしか消費しない関数(例: ランタイムの低レベル関数)で使用されます。スタックチェックはGoのgoroutineスタック管理において重要な役割を果たしますが、一部のクリティカルな関数ではオーバーヘッドを避けるために無効化されます。
  • RODATA (1<<3): このデータが読み取り専用セクションに配置されることを示します。
  • NOPTR (1<<4): このデータにポインタが含まれていないことを示します。ガベージコレクタは、この情報を使用して、このデータセクションをスキャンする必要があるかどうかを判断します。ポインタが含まれていない場合、GCのオーバーヘッドを削減できます。

リンカのヘッダファイル (*.out.h)

Goのツールチェーンでは、各アーキテクチャ(例: 386, amd64, arm)のリンカ (5l, 6l, 8l) がそれぞれ独自のヘッダファイルを持っていました。これらのヘッダファイルには、リンカが使用する定数や構造体の定義が含まれていました。以前は NOPROF などの textflag 定数もこれらのファイル内に直接定義されていました。

ビットマスク

textflag はビットマスクとして機能します。これは、複数のフラグを単一の整数値に結合するために使用される手法です。例えば、NOPROF1<<0 (1) で、DUPOK1<<1 (2) で、NOSPLIT1<<2 (4) である場合、NOPROF | DUPOK | NOSPLIT1 | 2 | 4 = 7 となります。このコミット以前は、この 7 という数値がアセンブリコードに直接書かれていました。

技術的詳細

このコミットの技術的な核心は、textflag 定数の定義と利用方法の変更にあります。

新しい textflag.h ファイル

src/cmd/ld/textflag.h という新しいヘッダファイルが導入されました。このファイルには、Goのリンカとアセンブラが共通して使用する textflag 定数が定義されています。

// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file defines flags attached to various functions
// and data objects.  The compilers, assemblers, and linker must
// all agree on these values.

// Don't profile the marked routine.  This flag is deprecated.
#define NOPROF	(1<<0)
// It is ok for the linker to get multiple of these symbols.  It will
// pick one of the duplicates to use.
#define DUPOK	(1<<1)
// Don't insert stack check preamble.
#define NOSPLIT	(1<<2)
// Put this data in a read-only section.
#define RODATA	(1<<3)
// This data contains no pointers.
#define NOPTR	(1<<4)

このファイルはC言語のプリプロセッサディレクティブ (#define) を使用して定数を定義しています。これにより、これらの定数をC言語のコード(リンカの一部)とアセンブリコード(Goのアセンブラによって処理される)の両方で利用できるようになります。

リンカヘッダファイルの変更

既存のリンカヘッダファイル (src/cmd/5l/5.out.h, src/cmd/6l/6.out.h, src/cmd/8l/8.out.h) から、NOPROF, DUPOK, NOSPLIT, RODATA, NOPTR の定義が削除され、代わりに新しい textflag.h をインクルードするようになりました。

変更前 (例: src/cmd/5l/5.out.h):

#define NOPROF		(1<<0)
#define DUPOK		(1<<1)
#define NOSPLIT		(1<<2)
#define RODATA	(1<<3)
#define NOPTR	(1<<4)

変更後 (例: src/cmd/5l/5.out.h):

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

この変更により、textflag 定数の定義が一元化され、各リンカヘッダファイルは共通の定義を参照するようになりました。

アセンブリファイルの変更

最も顕著な変更は、pkg/runtime/asm_386.s, pkg/runtime/asm_amd64.s, pkg/runtime/asm_arm.s といったランタイムのアセンブリファイルです。これらのファイルでは、TEXT ディレクティブの textflag 引数として直接数値リテラルが使用されていた箇所が、新しい textflag.h で定義された定数名に置き換えられました。

また、NOPROFDUPOK といったフラグが不要な箇所から削除されました。これは、これらのフラグが特定の関数にとって意味がなかったり、もはや必要とされなくなったためと考えられます。特に NOPROF はコミットメッセージで「deprecated(非推奨)」と明記されており、将来的に廃止される方向性を示唆しています。

変更前 (例: TEXT runtime·breakpoint(SB),7,$0-0):

7NOPROF | DUPOK | NOSPLIT のビットマスクを表していました。

変更後 (例: TEXT runtime·breakpoint(SB),NOSPLIT,$0-0):

NOSPLIT のみが残され、NOPROFDUPOK は削除されました。これにより、この関数がスタックチェックをスキップする意図のみを持つことが明確になります。

この変更は、アセンブリコードの可読性を大幅に向上させ、開発者が各関数の意図をより迅速に理解できるようにします。

textflag フィールドの非ゼロ値の意味

コミットメッセージには「A nonzero number in the textflag field now means "has not been converted yet".」という重要な記述があります。これは、このコミットが段階的に適用されることを示唆しています。つまり、このコミットの適用後も、まだ数値リテラルが残っている textflag フィールドが存在する場合、それはまだ新しい定数名への変換が完了していないことを意味する「TODO」のようなマーカーとして機能します。これにより、将来のコミットで残りのアセンブリファイルを変換する際の追跡が容易になります。

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

このコミットで変更された主要なファイルは以下の通りです。

  • src/cmd/5l/5.out.h: 386アーキテクチャ用リンカのヘッダファイル。textflag 定数の定義が削除され、textflag.h がインクルードされました。
  • src/cmd/6l/6.out.h: AMD64アーキテクチャ用リンカのヘッダファイル。textflag 定数の定義が削除され、textflag.h がインクルードされました。
  • src/cmd/8l/8.out.h: ARMアーキテクチャ用リンカのヘッダファイル。textflag 定数の定義が削除され、textflag.h がインクルードされました。
  • src/cmd/ld/textflag.h: 新規作成された textflag 定数定義用の共通ヘッダファイル。
  • src/pkg/runtime/asm_386.s: 386アーキテクチャ用ランタイムアセンブリファイル。TEXT ディレクティブの textflag が数値から定数名に、GLOBL ディレクティブの flag が数値から定数名に変換されました。不要な NOPROFDUPOK が削除されました。
  • src/pkg/runtime/asm_amd64.s: AMD64アーキテクチャ用ランタイムアセンブリファイル。TEXT ディレクティブの textflag が数値から定数名に、GLOBL ディレクティブの flag が数値から定数名に変換されました。不要な NOPROFDUPOK が削除されました。
  • src/pkg/runtime/asm_arm.s: ARMアーキテクチャ用ランタイムアセンブリファイル。TEXT ディレクティブの textflag が数値から定数名に、GLOBL ディレクティブの flag が数値から定数名に変換されました。不要な NOPROFDUPOK が削除されました。

コアとなるコードの解説

src/cmd/ld/textflag.h の新規作成

このファイルは、textflag の各ビットに対応する定数を #define で定義しています。これにより、これらの定数がリンカとアセンブラの両方で一貫して使用できるようになります。

#define NOPROF	(1<<0)
#define DUPOK	(1<<1)
#define NOSPLIT	(1<<2)
#define RODATA	(1<<3)
#define NOPTR	(1<<4)

リンカヘッダファイルからの定数削除とインクルード

各アーキテクチャのリンカヘッダファイル (5.out.h, 6.out.h, 8.out.h) から、重複していた textflag 定数の定義が削除され、代わりに textflag.h がインクルードされました。これにより、定数の定義が単一のソースに集約され、保守性が向上します。

--- a/src/cmd/5l/5.out.h
+++ b/src/cmd/5l/5.out.h
@@ -31,12 +31,7 @@
 #define	NSNAME		8
 #define	NSYM		50
 #define	NREG		16
-
-#define NOPROF		(1<<0)
-#define DUPOK		(1<<1)
-#define NOSPLIT		(1<<2)
-#define RODATA	(1<<3)
-#define NOPTR	(1<<4)
+#include "../ld/textflag.h"

アセンブリファイルでの textflag の置き換え

ランタイムのアセンブリファイルでは、TEXT ディレクティブの textflag 引数として使用されていた数値リテラルが、対応する定数名に置き換えられました。

例: _rt0_go 関数の変更 (asm_386.s)

変更前: TEXT _rt0_go(SB),7,$0 変更後: TEXT _rt0_go(SB),NOSPLIT,$0

この変更は、_rt0_go 関数がスタックチェックをスキップする (NOSPLIT) 意図のみを持ち、以前含まれていた NOPROFDUPOK フラグが不要であったことを明確に示しています。

例: runtime·main·f グローバルデータの変更 (asm_386.s)

変更前: GLOBL runtime·main·f(SB),8,$4 変更後: GLOBL runtime·main·f(SB),RODATA,$4

81<<3 であり、RODATA に対応します。これにより、runtime·main·f が読み取り専用データセクションに配置されるべきであることが明確になります。

これらの変更は、アセンブリコードの可読性を大幅に向上させ、開発者が各関数やデータの特性を直感的に理解できるようにします。また、不要なフラグの削除は、コードの意図をより正確に反映し、将来的な混乱を防ぐのに役立ちます。

関連リンク

参考にした情報源リンク