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

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

このコミットは、Goコンパイラおよびリンカ内部で使用される特定の擬似命令の名称変更に関するものです。具体的には、AFATVARDEFという名称がAVARDEFへと変更されています。この変更は、コードの意図をより正確に反映させるためのものであり、機能的な変更は伴いません。

コミット

commit 801e40a0a4320827d52eca60bb42946cc5f81fc4
Author: Russ Cox <rsc@golang.org>
Date:   Thu Feb 13 22:17:22 2014 -0500

    cmd/gc: rename AFATVARDEF to AVARDEF
    
    The "fat" referred to being used for multiword values only.
    We're going to use it for non-fat values sometimes too.
    
    No change other than the renaming.
    
    TBR=iant
    CC=golang-codereviews
    https://golang.org/cl/63650043

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

https://github.com/golang/go/commit/801e40a0a4320827d52eca60bb42946cc5f81fc4

元コミット内容

cmd/gc: rename AFATVARDEF to AVARDEF

"fat"という言葉は、複数ワードの値にのみ使用されることを指していました。 しかし、今後は「fat」ではない値にも使用されることがあります。

名称変更以外の変更はありません。

変更の背景

このコミットの背景には、Goコンパイラ(cmd/gc)および関連するアセンブラ/リンカ(cmd/5g, cmd/6g, cmd/8g, cmd/5l, cmd/6l, cmd/8l, src/liblink)の内部における、変数定義を扱う擬似命令の命名規則の改善があります。

元々AFATVARDEFという名称が使われていましたが、これは「fat」(複数ワード、つまりポインタや構造体などのサイズが大きい値)な変数にのみ適用されるという誤解を招く可能性がありました。しかし、Goコンパイラの進化に伴い、この擬似命令が「fat」ではない(単一ワードの)値にも適用されるケースが出てくることが予見されました。

このような状況でAFATVARDEFという名称を維持することは、コードの可読性を損ない、将来的な誤解やバグの原因となる可能性がありました。そのため、より汎用的なAVARDEFという名称に変更することで、この擬似命令が扱う変数の種類に関する制約がないことを明確にし、コードベースの一貫性と正確性を向上させる目的でこの変更が行われました。機能的な変更は一切なく、純粋にセマンティックな改善が目的です。

前提知識の解説

このコミットを理解するためには、以下のGoコンパイラおよびリンカに関する基本的な概念を理解しておく必要があります。

  1. Goコンパイラ (cmd/gc):

    • Go言語のソースコードを機械語に変換する主要なコンパイラです。
    • コンパイルの過程で、抽象構文木(AST)を生成し、型チェック、最適化、コード生成などを行います。
    • コード生成の段階で、アセンブラに渡すための命令列(擬似命令を含む)を生成します。
  2. Goアセンブラ (cmd/5g, cmd/6g, cmd/8g):

    • Goコンパイラが生成した中間コード(Goアセンブリ)を、特定のアーキテクチャ(例: 5gはARM、6gはx86-64、8gはx86-32)のオブジェクトファイルに変換します。
    • これらのアセンブラは、Go言語のランタイムや標準ライブラリの低レベルな部分を実装するためにも使用されます。
  3. Goリンカ (cmd/5l, cmd/6l, cmd/8l):

    • アセンブラによって生成されたオブジェクトファイルや、他のライブラリを結合して実行可能ファイルを生成します。
    • シンボル解決、再配置、セクションの結合などを行います。
  4. 擬似命令 (Pseudo-instructions):

    • 実際のマシン語命令には直接対応しないが、コンパイラやアセンブラの内部処理で特定の意味を持つ命令です。
    • これらは、コンパイル時やリンク時に特別な処理をトリガーするために使用されます。
    • AFATVARDEFAVARDEFは、このような擬似命令の一種であり、変数の定義に関連するメタデータや処理を示すために使われます。
  5. Node構造体:

    • Goコンパイラの内部で、抽象構文木(AST)の各要素を表すために使用されるデータ構造です。
    • 変数、関数、式など、プログラムのあらゆる構成要素がNodeとして表現されます。
  6. Prog構造体:

    • Goコンパイラが生成する中間コード(アセンブリ命令)を表すデータ構造です。
    • Progインスタンスは、単一のアセンブリ命令または擬似命令を表し、オペランドやリンク情報などを含みます。
  7. isfat関数:

    • Goコンパイラ内部で使用されるヘルパー関数で、与えられた型が「fat」(複数ワード)であるかどうかを判定します。
    • 「fat」な型とは、例えば構造体や配列、インターフェース、スライスなど、単一のレジスタやワードに収まらないサイズのデータを指します。

これらの概念を理解することで、AFATVARDEFからAVARDEFへの名称変更が、単なる文字列の置換ではなく、コンパイラ内部のセマンティクスをより正確に表現するための重要な改善であることがわかります。

技術的詳細

このコミットは、Goコンパイラおよびリンカの内部で使用される擬似命令AFATVARDEFAVARDEFにリネームするものです。この変更は、Goコンパイラのコード生成フェーズと、それに続くアセンブラおよびリンカの処理に影響を与えます。

Goコンパイラは、ソースコードを解析し、中間表現(IR)を生成します。このIRは、最終的にターゲットアーキテクチャの機械語に変換されるアセンブリ命令のシーケンスに似たProg構造体のリストとして表現されます。AFATVARDEF(そして新しいAVARDEF)は、このProg構造体で使用される擬似命令の一つです。

元々、AFATVARDEFは「fat」(複数ワード)な変数の定義を示すために導入されました。これは、Goの型システムにおいて、ポインタ、スライス、インターフェース、構造体など、単一のCPUレジスタやメモリワードに収まらないサイズのデータ型を効率的に扱うための内部的なメカニズムに関連しています。これらの「fat」な値は、コンパイラが特別な方法でメモリを割り当てたり、初期化したり、ガベージコレクションの対象として追跡したりする必要がありました。AFATVARDEFは、このような変数の定義ポイントをマークし、コンパイラやリンカが適切な処理を行うためのヒントとして機能していました。

しかし、Goコンパイラの開発が進むにつれて、この擬似命令が「fat」ではない(単一ワードの)値に対しても、特定の最適化やコード生成のシナリオで利用されるケースが出てくることが予見されました。例えば、スタック上の変数の初期化や、特定のスコープでの変数の生存期間管理など、サイズに関わらず変数の定義をマークする必要がある場面です。

AFATVARDEFという名称は、「fat」な値に限定されるという誤解を招く可能性があり、将来的なコードの拡張性や保守性を阻害する恐れがありました。そこで、Russ Cox氏はこの擬似命令の名称をAVARDEF(A-Variable-Definition)に変更することを決定しました。これにより、この擬似命令が「変数の定義」というより一般的な概念を表現するようになり、その用途が「fat」な値に限定されないことを明確にしました。

この変更は、主に以下のファイル群に影響を与えています。

  • src/cmd/*/ggen.c: Goコンパイラのジェネレータ部分で、コード生成ロジックが含まれます。gfatvardef関数の呼び出しがgvardefに変更されています。
  • src/cmd/*/peep.c: Goコンパイラのピーフォール最適化部分です。AFATVARDEFAVARDEFに置き換えられています。
  • src/cmd/*/prog.c: Prog構造体に関連する情報(命令の種類やプロパティ)を定義する部分です。AFATVARDEFAVARDEFに置き換えられています。
  • src/cmd/*/?.out.h: リンカが使用する命令コードの定義ファイルです。AFATVARDEFAVARDEFに置き換えられています。
  • src/cmd/gc/gen.c: Goコンパイラの汎用コード生成部分です。gfatvardef関数の呼び出しがgvardefに変更されています。
  • src/cmd/gc/go.h: Goコンパイラのグローバルヘッダファイルで、gfatvardef関数の宣言がgvardefに変更されています。
  • src/cmd/gc/pgen.c: Goコンパイラの擬似命令生成部分です。gfatvardef関数自体がgvardefにリネームされ、その内部ロジック(特にfatal("gvardef: node is not fat")というエラーメッセージ)も更新されています。また、removefatvardef関数もremovevardefにリネームされています。
  • src/cmd/gc/plive.c: ライブネス解析(変数がいつ使用され、いつ不要になるかを追跡する)に関連する部分です。prog->as == AFATVARDEFの比較がprog->as == AVARDEFに変更されています。
  • src/liblink/asm?.c: リンカのバックエンド部分で、アセンブリ命令の処理に関連します。AFATVARDEFAVARDEFに置き換えられています。

このリネームは、コンパイラとリンカの複数のサブシステムにわたる広範な変更ですが、その性質上、機能的な動作には一切影響を与えません。これは、コードベースのセマンティックな正確性を高め、将来のメンテナンスと拡張を容易にするための「クリーンアップ」作業の一環と見なすことができます。

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

このコミットにおけるコアとなるコードの変更は、AFATVARDEFという文字列をAVARDEFに置き換えることです。これは、Goコンパイラおよびリンカの複数のファイルにわたって行われています。

主な変更箇所は以下の通りです。

  1. 擬似命令の定義:

    • src/cmd/5l/5.out.h, src/cmd/6l/6.out.h, src/cmd/8l/8.out.h などのリンカのヘッダファイルで、擬似命令の列挙型定義が変更されています。
      -	AFATVARDEF,
      +	AVARDEF,
      
  2. 擬似命令の生成関数:

    • src/cmd/gc/go.h で関数の宣言が変更されています。
      -void	gfatvardef(Node*);
      +void	gvardef(Node*);
      
    • src/cmd/gc/pgen.c で関数定義自体が変更されています。
      -gfatvardef(Node *n)
      +gvardef(Node *n)
       {
       	if(n == N || !isfat(n->type))
      -		fatal("gfatvardef: node is not fat");
      +		fatal("gvardef: node is not fat");
       	switch(n->class) {
       	case PAUTO:
       	case PPARAM:
       	case PPARAMOUT:
      -		gins(AFATVARDEF, N, n);
      +		gins(AVARDEF, N, n);
       	}
       }
      
  3. 擬似命令の参照箇所:

    • src/cmd/*/ggen.c (例: src/cmd/5g/ggen.c) で、gfatvardefの呼び出しがgvardefに変更されています。
      -	gfatvardef(nl);
      +	gvardef(nl);
      
    • src/cmd/*/peep.c (例: src/cmd/5g/peep.c) で、AFATVARDEFの参照がAVARDEFに変更されています。
      -	case AFATVARDEF:
      +	case AVARDEF:
      
    • src/cmd/*/prog.c (例: src/cmd/5g/prog.c) で、progtable内の擬似命令参照が変更されています。
      -	[AFATVARDEF]=	{Pseudo | RightWrite},
      +	[AVARDEF]=	{Pseudo | RightWrite},
      
    • src/cmd/gc/gen.c で、gfatvardefの呼び出しがgvardefに変更されています。
      -	gfatvardef(res);
      +	gvardef(res);
      
    • src/cmd/gc/plive.c で、ライブネス解析における擬似命令のチェックが変更されています。
      -					if(from->node != nil && (!isfat(from->node->type) || prog->as == AFATVARDEF))
      +					if(from->node != nil && (!isfat(from->node->type) || prog->as == AVARDEF))
      
    • src/liblink/asm?.c (例: src/liblink/asm6.c) で、アセンブラの命令テーブル内の参照が変更されています。
      -	{ AFATVARDEF },
      +	{ AVARDEF },
      
  4. 関連するヘルパー関数のリネーム:

    • src/cmd/gc/pgen.c で、removefatvardef関数がremovevardefにリネームされています。
      -removefatvardef(Prog *firstp)
      +removevardef(Prog *firstp)
       {
       	Prog *p;
       
       	for(p = firstp; p != P; p = p->link) {
      -		while(p->link != P && p->link->as == AFATVARDEF)
      +		while(p->link != P && p->link->as == AVARDEF)
       			p->link = p->link->link;
       		if(p->to.type == D_BRANCH)
      -			while(p->to.u.branch != P && p->to.u.branch->as == AFATVARDEF)
      +			while(p->to.u.branch != P && p->to.u.branch->as == AVARDEF)
       				p->to.u.branch = p->to.u.branch->link;
       	}
       }
      

これらの変更は、Goコンパイラとリンカの複数のサブシステムにわたる広範なリネーム作業であり、AFATVARDEFという古い名称の痕跡を完全に排除することを目的としています。

コアとなるコードの解説

このコミットの核心は、Goコンパイラとリンカの内部で変数の定義をマークするために使用される擬似命令の名称をAFATVARDEFからAVARDEFに変更することです。この変更は、機能的な動作には影響を与えず、コードのセマンティックな正確性と将来の拡張性を向上させることを目的としています。

具体的に、この変更が影響する主要なコードパスと概念は以下の通りです。

  1. gvardef (旧 gfatvardef) 関数:

    • この関数は、Goコンパイラのコード生成フェーズで呼び出され、特定のNode(抽象構文木のノード、ここでは変数)に対してAVARDEF擬似命令を生成します。
    • src/cmd/gc/pgen.c にその実装があります。
    • 変更前は、この関数はisfat(n->type)(変数の型が「fat」であるか)のチェックを行い、!isfat(n->type)であればfatal("gfatvardef: node is not fat")というエラーを発生させていました。これは、AFATVARDEFが「fat」な値にのみ使用されるという当初の意図を反映していました。
    • 名称変更後もこのisfatチェックは残っていますが、コミットメッセージにあるように、将来的には「fat」ではない値にもこの擬似命令が使用される可能性があるため、このエラーメッセージはfatal("gvardef: node is not fat")と変更され、より一般的なAVARDEFの文脈に合うように調整されています。これは、この擬似命令の適用範囲が拡大する可能性を示唆しています。
    • gins(AVARDEF, N, n)という呼び出しは、AVARDEFという擬似命令を、指定された変数nをオペランドとして、現在の命令ストリームに挿入することを意味します。
  2. removevardef (旧 removefatvardef) 関数:

    • src/cmd/gc/pgen.c に実装されており、コンパイルの最終段階で、不要になったAVARDEF擬似命令を命令ストリームから削除する役割を担います。
    • AVARDEFのような擬似命令は、コード生成の過程で一時的なマーカーとして使用されることがありますが、最終的な機械語には変換されないため、リンカに渡す前に削除する必要があります。
    • この関数は、命令リストを走査し、p->link->as == AVARDEFまたはp->to.u.branch->as == AVARDEFという条件でAVARDEF命令を見つけてスキップすることで、命令ストリームから効果的に削除します。
  3. ProgInfo progtable:

    • src/cmd/*/prog.c に定義されているprogtableは、各アセンブリ命令(および擬似命令)のプロパティ(例: Pseudo, RightWriteなど)を定義するテーブルです。
    • [AVARDEF]={Pseudo | RightWrite}というエントリは、AVARDEFが擬似命令であり(Pseudoフラグ)、その右オペランドが書き込み操作であることを示しています(RightWriteフラグ)。これは、変数の定義がメモリへの書き込み操作と見なされるためです。
  4. ライブネス解析 (src/cmd/gc/plive.c):

    • ライブネス解析は、変数がプログラムのどの時点で「生きている」(つまり、将来使用される可能性がある)かを判断するコンパイラの最適化フェーズです。
    • plive.c内のコードでは、prog->as == AFATVARDEFという条件で、特定の擬似命令が変数のライブネスにどのように影響するかを判断していました。この条件がprog->as == AVARDEFに変更されたことで、ライブネス解析が新しい名称の擬似命令を正しく認識し、変数の生存期間を正確に追跡できるようになります。
  5. リンカの命令定義 (src/cmd/*/?.out.h, src/liblink/asm?.c):

    • リンカは、コンパイラが生成したオブジェクトファイルを処理する際に、これらの擬似命令の定義を認識する必要があります。
    • ヘッダファイル(例: 5.out.h)でenum as内の名称が変更され、リンカのバックエンドコード(例: asm6.c)で命令テーブル内の参照が更新されたことで、リンカがAVARDEFを正しく解釈できるようになります。

このコミットは、Goコンパイラの内部構造における命名の一貫性を高め、将来的な機能拡張や最適化のための基盤を強化するものです。特に、AVARDEFが「fat」ではない値にも適用されるようになるという示唆は、コンパイラが変数の定義をより柔軟に、かつ統一的に扱うようになる可能性を示しています。

関連リンク

  • Go言語の公式リポジトリ: https://github.com/golang/go
  • Goコンパイラの内部構造に関するドキュメント(Goのソースコード内や関連する論文を参照)

参考にした情報源リンク

  • Go言語のソースコード (特に src/cmd/gc, src/cmd/5g, src/cmd/6g, src/cmd/8g, src/cmd/5l, src/cmd/6l, src/cmd/8l, src/liblink ディレクトリ)
  • Goコンパイラの設計に関する論文やブログ記事 (例: Russ Cox氏のブログ、Goの公式ブログ)
  • GoのIssueトラッカーやコードレビューシステム (Go CL: https://golang.org/cl/63650043)
  • Goの内部構造に関するコミュニティの議論やドキュメント