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

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

このコミットは、Goコンパイラおよびアセンブラのソースコードにおけるリファクタリングを目的としています。具体的には、pragtextflagという関数が、src/cmd/6a/lex.cからsrc/cmd/cc/lexbodyへと移動されました。これは、コードの再利用性と構造の整理を促進するための変更です。

コミット

commit 0932b1f9b8e9b6e9b499a84702d632b9feac2ee6
Author: Russ Cox <rsc@golang.org>
Date:   Fri Mar 20 14:22:37 2009 -0700

    move pragtextflag into lexbody
    
    R=ken
    OCL=26581
    CL=26587
---
 src/cmd/6a/lex.c   | 7 -------
 src/cmd/cc/lexbody | 7 +++++++
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c
index 8dea599a65..22539fc7ea 100644
--- a/src/cmd/6a/lex.c
+++ b/src/cmd/6a/lex.c
@@ -1334,12 +1334,5 @@ praghjdicks(void)
 		;
 }
 
-void
-pragtextflag(void)\n{\n\twhile(getnsc() != '\\n')\n\t\t;\n}\n-\n #include "../cc/lexbody"
 #include "../cc/macbody"
diff --git a/src/cmd/cc/lexbody b/src/cmd/cc/lexbody
index 27e40fe49d..33734c7224 100644
--- a/src/cmd/cc/lexbody
+++ b/src/cmd/cc/lexbody
@@ -53,6 +53,13 @@ pragfpround(void)
 		;
 }
 
+void
+pragtextflag(void)
+{\
+\twhile(getnsc() != '\\n')
+\t\t;\n}\n+\n void
+pragprofile(void)
+{\

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

https://github.com/golang/go/commit/0932b1f9b8e9b6e9b499a84702d632b9feac2ee6

元コミット内容

move pragtextflag into lexbody
    
R=ken
OCL=26581
CL=26587

変更の背景

この変更の背景には、Goコンパイラおよびアセンブラのコードベースにおける共通機能の集約と再利用性の向上が挙げられます。初期のGoコンパイラは、異なるアーキテクチャ(例: 6aはamd64アセンブラ)やコンポーネント(例: ccはCコンパイラ)ごとに、類似または同一のコードが散在していることがありました。

pragtextflagのような「pragma」を処理する関数は、コンパイラやアセンブラがソースコードを解析する際に共通して必要となる機能です。このような共通のロジックを特定のコンポーネント(この場合は6aアセンブラのlex.c)に閉じ込めるのではなく、複数のコンポーネントからインクルードされる共有ファイル(lexbody)に移動することで、以下の利点が得られます。

  1. コードの重複排除: 同じ機能が複数の場所で実装されることを防ぎ、コードベース全体のサイズを削減します。
  2. 保守性の向上: 共通のロジックに対する変更が1箇所で済むため、バグ修正や機能追加が容易になります。
  3. 一貫性の確保: 異なるコンポーネント間で同じ動作が保証されます。
  4. コード構造の明確化: 共有されるべき機能が明確に分離され、コードの意図がより理解しやすくなります。

このコミットは、Goコンパイラ開発の初期段階における、よりクリーンで効率的なコードベース構築に向けた継続的な取り組みの一環と見ることができます。

前提知識の解説

このコミットを理解するためには、以下の概念について知っておく必要があります。

1. Goコンパイラとアセンブラの構造

Go言語のツールチェインは、ソースコードを機械語に変換するために複数のコンポーネントを使用します。

  • cmd/6a: これはGoのアセンブラの一つで、具体的にはAMD64(x86-64)アーキテクチャ用のGoアセンブラです。Goのアセンブラは、Go言語の構文に近い独自の擬似アセンブリ言語を機械語に変換します。
  • cmd/cc: これはGoのCコンパイラです。Goのツールチェインは、Go言語で書かれたコードだけでなく、C言語で書かれた部分(特にランタイムや一部のシステムコール)もコンパイルする必要があります。
  • lex.c: lexは「lexical analysis(字句解析)」の略で、コンパイラのフロントエンドの一部です。字句解析器(lexerまたはscanner)は、ソースコードの文字列をトークン(キーワード、識別子、演算子など、意味を持つ最小単位)のストリームに変換する役割を担います。lex.cは、この字句解析のロジックを含むC言語のソースファイルであることを示唆しています。
  • lexbody: これは、複数のコンパイラやアセンブラで共有される字句解析関連のコード断片を含むファイルであると推測されます。C言語では、#includeディレクティブを使って他のソースファイルの内容を現在のファイルに挿入することができます。lexbodyのようなファイルは、共通の関数やデータ構造を定義し、それを必要とする複数のlex.cのようなファイルからインクルードされることで、コードの再利用を実現します。

2. プラグマ (Pragma)

プログラミング言語における「プラグマ」(pragma)は、「pragmatic information」(実用的な情報)の略で、コンパイラやアセンブラに対して特別な指示を与えるためのディレクティブです。これらは通常、言語の標準構文の一部ではなく、コンパイラ固有の拡張として提供されます。

  • 目的: プラグマは、コンパイル時の動作(例: 最適化レベル、警告の抑制、特定のセクションへのコード配置)や、ランタイムの動作(例: ガベージコレクションのヒント、スレッドの優先度)に影響を与えるために使用されます。
  • Goにおけるプラグマ: Go言語自体はC/C++のような#pragmaディレクティブを直接持ちませんが、Goのコンパイラやアセンブラは、特定のコメント形式や組み込み関数を通じて、同様の目的を果たす「プラグマ」のような指示を処理することがあります。このコミットで言及されているpragtextflagは、Goのツールチェイン内部で処理される特定のプラグマの一種であり、おそらくコードのテキストセクションに関連するフラグを設定するためのものでしょう。

3. getnsc() 関数

コミットのコードスニペットにgetnsc()という関数が登場します。これは「get next source character」(次のソース文字を取得する)の略であると推測されます。字句解析器において、この関数は入力ストリームから次の文字を読み取る基本的な操作を提供します。pragtextflag関数内でwhile(getnsc() != '\n')というループが使われていることから、このプラグマは行末までを読み飛ばす(つまり、プラグマの引数や詳細は無視して、プラグマディレクティブ自体が存在することだけを認識する)ような単純な処理を行っていたことがわかります。

技術的詳細

このコミットの技術的な詳細は、Goコンパイラの字句解析フェーズにおけるプラグマ処理の構造化に焦点を当てています。

pragtextflagは、Goのツールチェインが特定のコンパイル時またはアセンブル時の動作を制御するために使用する内部的なプラグマです。このプラグマの具体的な機能は、コミットのコードスニペットからは「改行文字までを読み飛ばす」という非常に単純な動作しか読み取れません。これは、このプラグマが単に存在すること自体に意味があり、その後の引数や値は現在のところ重要ではないか、あるいは別のメカニズムで処理されることを示唆しています。

元々src/cmd/6a/lex.cに存在していたpragtextflag関数は、AMD64アセンブラの字句解析器の一部として機能していました。しかし、このようなプラグマ処理は、他のアーキテクチャのアセンブラやGoのCコンパイラなど、Goツールチェイン内の他のコンポーネントでも必要となる可能性があります。

src/cmd/cc/lexbodyは、Goツールチェインの複数のフロントエンド(コンパイラやアセンブラ)で共有される字句解析関連の共通コードをまとめたファイルです。このファイルは、C言語の#includeプリプロセッサディレクティブを通じて、src/cmd/6a/lex.cのような個別の字句解析器のソースファイルに組み込まれます。

pragtextflaglexbodyに移動することで、以下の技術的な利点が得られます。

  • 共通化されたプラグマ処理: pragtextflagのロジックがlexbodyに一元化されるため、将来的に他のコンポーネントがこのプラグマをサポートする必要が生じた場合でも、コードの重複なく容易に組み込むことができます。
  • 依存関係の整理: 6aアセンブラ固有のlex.cから、より汎用的なlexbodyへと機能が移されることで、コードの依存関係が整理され、モジュール性が向上します。
  • ビルドプロセスの効率化: 共通コードをlexbodyにまとめることで、コンパイラのビルドプロセスにおいて、同じコードが何度もコンパイルされるのを避けることができる可能性があります(ただし、これはCコンパイラの最適化やインクルードの扱いに依存します)。

この変更は、Goツールチェインの初期段階における設計判断であり、将来的な拡張性や保守性を考慮した上での賢明なリファクタリングと言えます。

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

このコミットにおけるコアとなるコードの変更は、以下の2つのファイルにまたがっています。

  1. src/cmd/6a/lex.c からの削除:

    --- a/src/cmd/6a/lex.c
    +++ b/src/cmd/6a/lex.c
    @@ -1334,12 +1334,5 @@ praghjdicks(void)
     		;
     }
     
    -void
    -pragtextflag(void)
    -{
    -	while(getnsc() != '\n')
    -		;
    -}
    -
     #include "../cc/lexbody"
     #include "../cc/macbody"
    

    pragtextflag関数の定義がsrc/cmd/6a/lex.cから完全に削除されています。

  2. src/cmd/cc/lexbody への追加:

    --- a/src/cmd/cc/lexbody
    +++ b/src/cmd/cc/lexbody
    @@ -53,6 +53,13 @@ pragfpround(void)
     		;
     }
     
    +void
    +pragtextflag(void)
    +{
    +	while(getnsc() != '\n')
    +		;
    +}
    +
     void
     pragprofile(void)
     {
    

    pragtextflag関数の定義がsrc/cmd/cc/lexbodyに追加されています。

コアとなるコードの解説

pragtextflag関数の実装は非常にシンプルです。

void
pragtextflag(void)
{
	while(getnsc() != '\n')
		;
}
  • void pragtextflag(void): この関数は引数を取らず、何も返しません。これは、プラグマの処理が主に副作用(例: 内部状態の変更)に依存していることを示唆しています。
  • while(getnsc() != '\n'): このループが関数の主要なロジックです。
    • getnsc(): 前述の通り、「get next source character」(次のソース文字を取得する)関数です。これは字句解析器の基本的なプリミティブであり、入力ストリームから1文字を読み進めます。
    • != '\n': ループは、読み取った文字が改行文字(\n)ではない限り継続します。
    • ;: ループ本体が空であるため、このループは単にgetnsc()を繰り返し呼び出し、改行文字に到達するまで入力ストリームを読み飛ばすだけです。

この実装からわかることは、pragtextflagプラグマは、そのディレクティブ自体が存在することだけが重要であり、その後に続く内容は(少なくともこの時点では)解析されることなく無視されるということです。これは、例えば「この行にtextflagプラグマがある」という事実だけをコンパイラに伝え、具体的なフラグの値などは別の方法で決定されるか、あるいはデフォルト値が使用されるようなシナリオで用いられます。

この関数がsrc/cmd/cc/lexbodyに移動されたことで、6aアセンブラだけでなく、ccコンパイラを含む他のGoツールチェインのコンポーネントも、#include "../cc/lexbody"を通じてこの共通のプラグマ処理ロジックを利用できるようになりました。これにより、Goツールチェイン全体でプラグマの処理方法が一貫し、コードの重複が排除され、保守性が向上します。

関連リンク

参考にした情報源リンク

  • コミット情報: /home/orange/Project/comemo/commit_data/1855.txt
  • GitHub上のコミットページ: https://github.com/golang/go/commit/0932b1f9b8e9b6e9b499a84702d632b9feac2ee6
  • Go言語のツールチェイン、コンパイラ、アセンブラ、プラグマに関する一般的な知識。
  • C言語のプリプロセッサディレクティブ(#include)に関する知識。
  • 字句解析の概念に関する一般的な知識。