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

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

このコミットは、Go言語の初期のコンパイラである6g(64ビットシステム向け)のコード生成部分における変更です。具体的には、src/cmd/6g/cgen.csrc/cmd/6g/gen.csrc/cmd/6g/gg.hsrc/cmd/6g/gsubr.cの4つのファイルが修正されています。これらのファイルは、Goのソースコードを機械語に変換する過程で、アドレス計算やコード生成を担当する重要なコンポーネントです。

コミット

このコミットは、sudoaddable関数のシグネチャから未使用の第2引数(Type* t)を削除し、同時にこの関数のコメントを改善することを目的としています。これにより、コードの簡潔性と可読性が向上し、将来的なメンテナンスが容易になります。

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

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

元コミット内容

comment sudoaddable;
remove unused second parameter.

R=ken
OCL=22126
CL=22126

変更の背景

この変更の背景には、コードの品質向上と効率化があります。sudoaddable関数は、Goコンパイラが特定のノード(変数、構造体のフィールド、配列の要素など)のアドレスを計算できるかどうかを判断し、計算可能であればそのアドレス情報を返す役割を担っています。

元のsudoaddable関数のシグネチャはint sudoaddable(Node *n, Type *t, Addr *a)でした。ここで、Node *nはアドレスを計算したい対象のノード、Addr *aは計算されたアドレス情報を格納するポインタです。しかし、第2引数のType *tは、関数の内部で実際に使用されていませんでした。このような未使用の引数は、コードの理解を妨げ、誤解を招く可能性があり、また、コンパイラが不要な情報を渡すことによるわずかなオーバーヘッドも発生させます。

したがって、このコミットでは、未使用のType *t引数を削除することで、関数のシグネチャをより正確にし、コードの意図を明確にしています。また、sudoaddable関数の役割と使用方法を説明するコメントを追加することで、コードの可読性と保守性を向上させています。これは、Go言語の初期開発段階における継続的なコードベースの洗練の一環です。

前提知識の解説

このコミットを理解するためには、Go言語のコンパイラの基本的な構造と、C言語で書かれたコンパイラコードにおけるいくつかの概念を理解しておく必要があります。

  • Goコンパイラ (6g): 6gは、Go言語の初期のコンパイラの一つで、主に64ビットアーキテクチャ(x86-64)をターゲットとしていました。Go言語のコンパイラは、ソースコードを解析し、中間表現を生成し、最終的にターゲットアーキテクチャの機械語に変換する役割を担います。
  • AST (Abstract Syntax Tree): コンパイラは、Goのソースコードを解析して抽象構文木(AST)を構築します。ASTは、プログラムの構造を木構造で表現したもので、各ノードは変数、式、文などのプログラム要素に対応します。
  • Node構造体: コンパイラ内部でASTの各要素を表すために使用されるデータ構造です。Node *nは、AST内の特定の要素へのポインタを意味します。
  • Type構造体: Go言語の型システムにおける型情報を表すデータ構造です。変数の型(int, string, structなど)や関数のシグネチャなどが含まれます。Type *tは、特定の型情報へのポインタを意味します。
  • Addr構造体: アドレス情報を表すデータ構造です。メモリ上の特定の位置や、レジスタ、スタック上のオフセットなど、コード生成時に必要となるアドレス関連の情報を保持します。Addr *aは、計算されたアドレス情報を格納するためのポインタです。
  • コード生成 (Code Generation): コンパイラの最終段階の一つで、中間表現をターゲットアーキテクチャの機械語命令に変換するプロセスです。この段階では、変数へのアクセス、関数呼び出し、演算などの各操作に対応する具体的な機械語命令が生成されます。
  • sudoaddable関数: この関数は、Goコンパイラのコード生成フェーズにおいて、特定のNode(例えば、変数や構造体のフィールド、配列の要素など)が「アドレス可能」であるかどうか、つまり、そのメモリ上のアドレスを効率的に計算できるかどうかを判断するために使用されます。もしアドレス可能であれば、そのアドレス情報(Addr構造体)を返します。これは、ポインタ演算や、変数の値の読み書きなど、メモリへのアクセスを伴う操作を行う際に不可欠なステップです。

技術的詳細

このコミットの技術的な核心は、sudoaddable関数のシグネチャ変更とその影響にあります。

変更前のsudoaddable関数のシグネチャ: int sudoaddable(Node *n, Type *t, Addr *a)

  • Node *n: アドレスを計算したい対象のASTノード。
  • Type *t: ノードnの型情報。
  • Addr *a: 計算されたアドレス情報を格納するためのポインタ。

変更後のsudoaddable関数のシグネチャ: int sudoaddable(Node *n, Addr *a)

  • Node *n: アドレスを計算したい対象のASTノード。
  • Addr *a: 計算されたアドレス情報を格納するためのポインタ。

この変更により、Type *t引数が削除されました。コミットメッセージにあるように、この引数は関数の内部で実際に使用されていなかったため、冗長でした。sudoaddable関数は、Node *nが持つ内部の型情報(n->type)を利用してアドレス可能性を判断しており、外部から別途Type *tを渡す必要がなかったのです。

この変更は、以下の点で重要です。

  1. コードの簡潔性: 不要な引数を削除することで、関数のシグネチャが簡潔になり、関数の目的がより明確になります。
  2. 可読性の向上: 開発者が関数を呼び出す際に、どの引数が必須で、どの引数が内部で利用されるのかをより容易に理解できるようになります。
  3. 保守性の向上: 未使用の引数がある場合、将来的にその引数が誤って使用されたり、混乱を招いたりする可能性があります。これを削除することで、そのようなリスクを排除できます。
  4. コンパイラの最適化: 非常にわずかではありますが、不要な引数を渡すためのレジスタ割り当てやスタック操作が不要になるため、コンパイラ自身のパフォーマンスにも寄与する可能性があります。

また、src/cmd/6g/gsubr.c内のsudoaddable関数の実装に、その役割と使用方法を説明する詳細なコメントが追加されました。このコメントは、関数が「配列や構造体内の(おそらくネストされた)フィールドの参照であるnのアドレスを計算するコードを生成する」こと、成功時にはaにアドレスが格納されること、そして呼び出し元がsudocleanを呼び出してレジスタを解放する責任があることを明確にしています。これは、コンパイラの複雑なコードベースを理解し、メンテナンスする上で非常に価値のあるドキュメントです。

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

このコミットによる主要なコード変更は、sudoaddable関数のシグネチャの変更と、それに伴う呼び出し箇所の修正、そして関数のコメント追加です。

src/cmd/6g/cgen.c

cgen関数内でsudoaddableの呼び出し箇所が3箇所修正されています。

--- a/src/cmd/6g/cgen.c
+++ b/src/cmd/6g/cgen.c
@@ -66,7 +66,7 @@ cgen(Node *n, Node *res)
 			break;
 		}
 
-		if(sudoaddable(res, n->type, &addr)) {
+		if(sudoaddable(res, &addr)) {
 			a = optoas(OAS, res->type);
 			if(f) {
 				regalloc(&n2, res->type, N);
@@ -104,7 +104,7 @@ cgen(Node *n, Node *res)
 		goto ret;
 	}
 
-	if(sudoaddable(n, res->type, &addr)) {
+	if(sudoaddable(n, &addr)) {
 		a = optoas(OAS, n->type);
 		if(res->op == OREGISTER) {
 			p1 = gins(a, N, res);
@@ -340,7 +340,7 @@ abop:	// asymmetric binary
 		regalloc(&n1, nl->type, res);
 		cgen(nl, &n1);
 
-		if(sudoaddable(nr, nl->type, &addr)) {
+		if(sudoaddable(nr, &addr)) {
 			p1 = gins(a, N, &n1);
 			p1->from = addr;
 			gmove(&n1, res);

sudoaddableの第2引数n->typeまたはnl->typeが削除されています。

src/cmd/6g/gen.c

cgen_asop関数内でsudoaddableの呼び出し箇所が3箇所修正されています。

--- a/src/cmd/6g/gen.c
+++ b/src/cmd/6g/gen.c
@@ -987,7 +987,7 @@ cgen_asop(Node *n)
 			gins(optoas(OINC, nl->type), N, nl);
 			goto ret;
 		}
-		if(sudoaddable(nl, nr->type, &addr)) {
+		if(sudoaddable(nl, &addr)) {
 			p1 = gins(optoas(OINC, nl->type), N, N);
 			p1->to = addr;
 			sudoclean();
@@ -1003,7 +1003,7 @@ cgen_asop(Node *n)
 			gins(optoas(ODEC, nl->type), N, nl);
 			goto ret;
 		}
-		if(sudoaddable(nl, nr->type, &addr)) {
+		if(sudoaddable(nl, &addr)) {
 			p1 = gins(optoas(ODEC, nl->type), N, N);
 			p1->to = addr;
 			sudoclean();
@@ -1031,7 +1031,7 @@ cgen_asop(Node *n)
 		goto ret;
 	}
 	if(nr->ullman < UINF)
-		if(sudoaddable(nl, nr->type, &addr)) {
+		if(sudoaddable(nl, &addr)) {
 			if(smallintconst(nr)) {
 				p1 = gins(optoas(n->etype, nl->type), nr, N);
 				p1->to = addr;

ここでもsudoaddableの第2引数nr->typeが削除されています。

src/cmd/6g/gg.h

sudoaddable関数のプロトタイプ宣言が修正されています。

--- a/src/cmd/6g/gg.h
+++ b/src/cmd/6g/gg.h
@@ -215,7 +215,7 @@ Plist*	newplist(void);
 int	isfat(Type*);
 void	setmaxarg(Type*);
 void	sudoclean(void);
-int	sudoaddable(Node*, Type*, Addr*);
+int	sudoaddable(Node*, Addr*);
 
 /*
  * list.c

ヘッダーファイルでの宣言からType*引数が削除され、関数のシグネチャが更新されています。

src/cmd/6g/gsubr.c

sudoaddable関数の定義が修正され、詳細なコメントが追加されています。

--- a/src/cmd/6g/gsubr.c
+++ b/src/cmd/6g/gsubr.c
@@ -1857,8 +1857,19 @@ sudoclean(void)
 	cleani -= 2;
 }
 
+/*
+ * generate code to compute address of n,
+ * a reference to a (perhaps nested) field inside
+ * an array or struct.  
+ * return 0 on failure, 1 on success.
+ * on success, leaves usable address in a.
+ *
+ * caller is responsible for calling sudoclean
+ * after successful sudoaddable,
+ * to release the register used for a.
+ */
 int
-sudoaddable(Node *n, Type *t, Addr *a)
+sudoaddable(Node *n, Addr *a)
 {
 	int o, i, w;
 	int oary[10];
@@ -1866,8 +1877,9 @@ sudoaddable(Node *n, Type *t, Addr *a)
 	Node n1, n2, *nn, *l, *r;\n 	Node *reg, *reg1;\n 	Prog *p1;\n+\tType *t;\n \n-\tif(n->type == T || t == T)\n+\tif(n->type == T)\n 	\treturn 0;\
 \n 	switch(n->op) {

関数の定義からType* t引数が削除され、関数内部でType *t;としてローカル変数宣言されています。これは、以前は引数として受け取っていたtが、実際にはn->typeから取得できるため、引数として不要になったことを示しています。また、関数の冒頭に詳細なコメントが追加され、その機能と使用上の注意点が説明されています。if(n->type == T || t == T)の条件式から|| t == Tが削除され、n->type == Tのみが残っています。これは、tが引数として渡されなくなったため、そのチェックが不要になったことを意味します。

コアとなるコードの解説

このコミットの核となる変更は、sudoaddable関数のシグネチャからType *t引数を削除したことです。

  1. sudoaddable関数のシグネチャ変更:

    • src/cmd/6g/gg.h (ヘッダーファイル): 関数のプロトタイプ宣言がint sudoaddable(Node*, Type*, Addr*);からint sudoaddable(Node*, Addr*);に変更されました。これにより、コンパイラの他の部分がこの関数を呼び出す際に、Type型の引数を渡す必要がなくなります。
    • src/cmd/6g/gsubr.c (実装ファイル): 関数の定義も同様にint sudoaddable(Node *n, Type *t, Addr *a)からint sudoaddable(Node *n, Addr *a)に変更されました。関数内部では、以前tとして渡されていた型情報が必要な場合、n->type(ノードn自身の型情報)が使用されます。また、関数内部でType *t;というローカル変数が宣言されていますが、これは以前の引数名と同じ名前を使用しているだけで、引数として受け取っていたtとは異なります。このローカル変数tは、関数内部で必要に応じてn->typeから値が代入されるか、あるいは単に未使用のままになっている可能性があります(このコミットの範囲では、その後のtの使用箇所は変更されていませんが、if(n->type == T || t == T)の条件から|| t == Tが削除されていることから、tが引数として不要になったことが明確です)。
  2. 呼び出し箇所の修正:

    • src/cmd/6g/cgen.csrc/cmd/6g/gen.c内のsudoaddable関数のすべての呼び出し箇所で、第2引数(n->typenr->typeなど)が削除されました。これは、関数のシグネチャ変更に合わせた必須の修正です。これにより、コンパイラがsudoaddableを呼び出す際のコードが簡潔になります。
  3. コメントの追加:

    • src/cmd/6g/gsubr.csudoaddable関数の定義の前に、詳細なコメントが追加されました。このコメントは、関数の目的(配列や構造体内のフィールドのアドレス計算)、戻り値(成功/失敗)、成功時のAddr構造体の内容、および呼び出し元がsudocleanを呼び出す責任があることを明確にしています。このようなドキュメンテーションは、複雑なコンパイラコードの理解とメンテナンスに不可欠です。

これらの変更は、コードの冗長性を排除し、関数の意図を明確にすることで、Goコンパイラのコードベースの品質と保守性を向上させるためのものです。

関連リンク

参考にした情報源リンク