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

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

このコミットは、Goコンパイラのcmd/6g(32ビットアーキテクチャ向け)とcmd/8g(64ビットアーキテクチャ向け)におけるgvardef関数の呼び出しを簡素化するものです。具体的には、gvardef関数がPEXTERNクラスのノードに対しては何もしないという事実を利用し、呼び出し前のn->class != PEXTERNという条件チェックを削除しています。これにより、コードの冗長性が排除され、5g(ARMアーキテクチャ向け)コンパイラのコードとの一貫性が向上しています。

コミット

commit fa6375ea47ce054d5f1d93a4a1fe7efe2e293991
Author: Ian Lance Taylor <iant@golang.org>
Date:   Wed Feb 26 07:37:10 2014 -0800

    cmd/6g, cmd/8g: simplify calls to gvardef
    
    The gvardef function does nothing if n->class == PEXTERN, so
    we don't need to test for that before calling it.  This makes
    the 6g/8g code more like the 5g code and clarifies that the
    cases that do not test for n->class != PEXTERN are not buggy.
    
    LGTM=rsc
    R=rsc
    CC=golang-codereviews
    https://golang.org/cl/68900044

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

https://github.com/golang/go/commit/fa6375ea47ce054d5f1d93a4a1fe7efe2e293991

元コミット内容

cmd/6g, cmd/8g: simplify calls to gvardef

The gvardef function does nothing if n->class == PEXTERN, so
we don't need to test for that before calling it. This makes
the 6g/8g code more like the 5g code and clarifies that the
cases that do not test for n->class != PEXTERN are not buggy.

LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/68900044

変更の背景

Goコンパイラには、異なるアーキテクチャをターゲットとする複数のバックエンドが存在します。このコミットの時点では、6g(amd64)、8g(x86)、5g(ARM)などが主要なコンパイラでした。これらのコンパイラは、それぞれのアセンブリコードを生成する際に、共通のフロントエンドから渡される抽象構文木(AST)や中間表現(IR)を処理します。

gvardefという関数は、グローバル変数の定義に関連する処理を行うためのものです。以前の6gおよび8gのコードでは、このgvardefを呼び出す前に、対象のノード(変数など)がPEXTERNクラスではないことを確認する条件n->class != PEXTERNが存在していました。しかし、gvardef関数の内部実装では、PEXTERNクラスのノードが渡された場合、実際には何も処理を行わないようになっていました。

この冗長な条件チェックは、コードの可読性を低下させ、また5gコンパイラのコードベースでは同様のチェックが存在しなかったため、コードの一貫性も損なっていました。このコミットの目的は、この冗長なチェックを削除し、コードを簡素化し、他のコンパイラとの整合性を高めることにありました。これにより、n->class != PEXTERNのチェックがない箇所がバグではないことを明確にする意図もありました。

前提知識の解説

このコミットを理解するためには、Goコンパイラの内部構造と、特に以下の概念についての知識が必要です。

  • Goコンパイラ (cmd/6g, cmd/8g, cmd/5g): Go言語のソースコードを機械語に変換するツール群です。6gはAMD64アーキテクチャ向け、8gはx86アーキテクチャ向け、5gはARMアーキテクチャ向けのコンパイラを指します。これらはGoの初期のコンパイラであり、それぞれが特定のアセンブリコードを生成するバックエンドを持っていました。

  • 抽象構文木 (AST) と中間表現 (IR): コンパイラがソースコードを解析する際に内部的に構築するデータ構造です。ASTはソースコードの構文構造を木構造で表現し、IRはASTから生成される、より機械語に近い抽象的な表現です。コンパイラの各フェーズ(字句解析、構文解析、意味解析、最適化、コード生成)でこれらの表現が変換・操作されます。

  • Node構造体: Goコンパイラの内部で、ASTやIRの各要素(変数、関数呼び出し、演算子など)を表すために使用されるデータ構造です。Nodeは、その要素の種類(opフィールド)、型情報、そしてその要素が持つ特性(classフィールドなど)を保持します。

  • ONAME: Nodeop(操作)フィールドの値の一つで、名前付きのオブジェクト(変数、関数、型など)を表します。このコミットでは、ns->op == ONAMEという条件で、対象のノードが名前付きオブジェクトであることを確認しています。

  • PEXTERN: Nodeclass(クラス)フィールドの値の一つで、シンボルが「パッケージ外部」であることを示します。これは通常、そのシンボルが他のパッケージから参照可能(エクスポートされている)であることを意味します。コンパイラは、PEXTERNなシンボルに対しては、その定義を生成する際に特別な処理を必要としない場合があります。なぜなら、その定義は既に他の場所で処理されているか、リンカによって解決されるためです。

  • gvardef関数: グローバル変数の定義に関連する処理を行うコンパイラ内部の関数です。具体的には、グローバル変数のシンボル情報をコンパイラのシンボルテーブルに登録したり、必要に応じてその変数のためのメモリ領域を確保したりする役割を担います。このコミットの核心は、この関数がPEXTERNクラスのノードに対しては実質的に何もしないという点にあります。

技術的詳細

このコミットの技術的なポイントは、コンパイラのコード生成フェーズにおける最適化とコードの簡素化です。

Goコンパイラは、ソースコードを解析してASTを構築した後、それを中間表現に変換し、最終的にターゲットアーキテクチャのアセンブリコードを生成します。この過程で、変数や関数の定義を適切に処理する必要があります。

gvardef関数は、グローバル変数の定義を処理する役割を担っています。しかし、Go言語では、あるパッケージで定義され、PEXTERNとしてマークされたグローバル変数は、他のパッケージから参照されることを意図しています。このような変数の定義は、通常、その変数が実際に定義されている場所(元のパッケージ)で一度だけ処理されれば十分です。他のパッケージでその変数を参照する際には、再度「定義」する必要はありません。

gvardef関数の内部実装が、PEXTERNクラスのノードを受け取った場合に、重複する定義処理をスキップするように設計されていたため、呼び出し側で明示的にn->class != PEXTERNという条件でフィルタリングする必要がなくなりました。この条件を削除しても、PEXTERNなノードがgvardefに渡されたとしても、関数自体が適切に処理(何もしない)を行うため、コンパイラの動作に影響はありません。

この変更は、コードの冗長性を排除し、コンパイラのコードベース全体の一貫性を向上させる「クリーンアップ」の一種です。特に、5gコンパイラではこのチェックが元々存在しなかったため、6g8gのコードもそれに合わせることで、将来的なメンテナンスや共通化が容易になります。

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

変更はsrc/cmd/6g/cgen.csrc/cmd/8g/cgen.cの2つのファイルにわたります。

src/cmd/6g/cgen.c

--- a/src/cmd/6g/cgen.c
+++ b/src/cmd/6g/cgen.c
@@ -1389,11 +1389,11 @@ sgen(Node *n, Node *ns, int64 w)
 
  	if(n->ullman >= ns->ullman) {
  		agenr(n, &nodr, N);
- 		if(ns->op == ONAME && ns->class != PEXTERN)
+ 		if(ns->op == ONAME)
  			gvardef(ns);
  		agenr(ns, &nodl, N);
  	} else {
- 		if(ns->op == ONAME && ns->class != PEXTERN)
+ 		if(ns->op == ONAME)
  			gvardef(ns);
  		agenr(ns, &nodl, N);
  		agenr(n, &nodr, N);
@@ -1575,7 +1575,7 @@ componentgen(Node *nr, Node *nl)
  	switch(nl->type->etype) {
  	case TARRAY:
  		// componentgen for arrays.
- 		if(nl->op == ONAME && nl->class != PEXTERN)
+ 		if(nl->op == ONAME)
  			gvardef(nl);
  		t = nl->type;
  		if(!isslice(t)) {
@@ -1626,7 +1626,7 @@ componentgen(Node *nr, Node *nl)
  		goto yes;
 
  	case TSTRING:
- 		if(nl->op == ONAME && nl->class != PEXTERN)
+ 		if(nl->op == ONAME)
  			gvardef(nl);
  		nodl.xoffset += Array_array;
  		nodl.type = ptrto(types[TUINT8]);
@@ -1651,7 +1651,7 @@ componentgen(Node *nr, Node *nl)
  		goto yes;
 
  	case TINTER:
- 		if(nl->op == ONAME && nl->class != PEXTERN)
+ 		if(nl->op == ONAME)
  			gvardef(nl);
  		nodl.xoffset += Array_array;
  		nodl.type = ptrto(types[TUINT8]);
@@ -1676,7 +1676,7 @@ componentgen(Node *nr, Node *nl)
  		goto yes;
 
  	case TSTRUCT:
- 		if(nl->op == ONAME && nl->class != PEXTERN)
+ 		if(nl->op == ONAME)
  			gvardef(nl);
  		loffset = nodl.xoffset;
  		roffset = nodr.xoffset;

src/cmd/8g/cgen.c

--- a/src/cmd/8g/cgen.c
+++ b/src/cmd/8g/cgen.c
@@ -1384,7 +1384,7 @@ componentgen(Node *nr, Node *nl)
 
  	switch(nl->type->etype) {
  	case TARRAY:
- 		if(nl->op == ONAME && nl->class != PEXTERN)
+ 		if(nl->op == ONAME)
  			gvardef(nl);
  		nodl.xoffset += Array_array;
  		nodl.type = ptrto(nl->type->type);
@@ -1419,7 +1419,7 @@ componentgen(Node *nr, Node *nl)
  		goto yes;
 
  	case TSTRING:
- 		if(nl->op == ONAME && nl->class != PEXTERN)
+ 		if(nl->op == ONAME)
  			gvardef(nl);
  		nodl.xoffset += Array_array;
  		nodl.type = ptrto(types[TUINT8]);
@@ -1444,7 +1444,7 @@ componentgen(Node *nr, Node *nl)
  		goto yes;
 
  	case TINTER:
- 		if(nl->op == ONAME && nl->class != PEXTERN)
+ 		if(nl->op == ONAME)
  			gvardef(nl);
  		nodl.xoffset += Array_array;
  		nodl.type = ptrto(types[TUINT8]);

コアとなるコードの解説

変更は、sgen関数とcomponentgen関数内のgvardef呼び出し箇所に集中しています。

変更前:

if(ns->op == ONAME && ns->class != PEXTERN)
    gvardef(ns);

変更後:

if(ns->op == ONAME)
    gvardef(ns);

この変更は非常にシンプルで、&& ns->class != PEXTERNという条件が削除されています。 これは、gvardef関数がPEXTERNクラスのノードを受け取った場合に、内部で何もしない(あるいは適切な処理を行う)ことが保証されているため、呼び出し側でこの条件をチェックする必要がないという事実に基づいています。

この簡素化により、以下のメリットがあります。

  1. コードの簡潔化: 不要な条件分岐が削除され、コードがより読みやすくなります。
  2. 一貫性の向上: 5gコンパイラのコードベースとの整合性が取れ、Goコンパイラ全体でのコードスタイルの一貫性が向上します。
  3. バグの誤解の解消: n->class != PEXTERNのチェックがない他の箇所がバグではないことを明確にします。これは、gvardefの内部ロジックがPEXTERNを適切に処理するため、外部からのチェックが不要であることを示唆しています。

この変更は、コンパイラの機能に影響を与えるものではなく、主にコードの品質と保守性を向上させるためのものです。

関連リンク

参考にした情報源リンク

  • コミット情報: commit_data/18659.txt
  • GitHub上のコミットページ: https://github.com/golang/go/commit/fa6375ea47ce054d5f1d93a4a1fe7efe2e293991
  • Web検索結果(gvardef golang compiler PEXTERN ONAME):
    • Goコンパイラの内部概念に関する情報(ONAME, PEXTERNなど)
    • Goコンパイラのシンボル定義とAST/IRに関する一般的な情報
  • Go言語のコンパイラに関する一般的な知識