[インデックス 154] ファイルの概要
このコミットは、Go言語の初期のコンパイラ(gc
および 6g
)における、主にインポート/エクスポート機構、関数およびメソッド呼び出しの処理、デバッグ出力の制御、そして式の評価順序の最適化に関する複数の改善を含んでいます。
変更されたファイルは以下の通りです。
src/cmd/6g/gen.c
: 64-bit Goコンパイラのコード生成部分。関数呼び出しとメソッド呼び出しの処理ロジックが変更されています。src/cmd/6g/gg.h
: 64-bit Goコンパイラの共通ヘッダファイル。グローバル変数の宣言が調整されています。src/cmd/6g/gsubr.c
: 64-bit Goコンパイラのサブルーチン。アドレス解決ロジックにメソッド関連の処理が追加されています。src/cmd/gc/dcl.c
: Goコンパイラの宣言処理部分。デバッグ出力の制御が改善されています。src/cmd/gc/export.c
: Goコンパイラのインポート/エクスポート処理部分。インポートされたシンボルと型の処理が大幅に修正されています。src/cmd/gc/go.h
: Goコンパイラの共通ヘッダファイル。ASTノード構造体とグローバル変数が更新されています。src/cmd/gc/go.y
: Go言語の文法定義(Yaccファイル)。関数宣言の処理が変更されています。src/cmd/gc/lex.c
: Goコンパイラの字句解析部分。インポートファイルのパス処理とインポートシステムの状態管理が変更されています。src/cmd/gc/walk.c
: GoコンパイラのASTウォーク(走査)と最適化処理部分。式の評価順序の再編成ロジックが改善されています。
コミット
- コミットハッシュ:
2254a8ee991899f0f72d7fbd116c189582441ea0
- 作者: Ken Thompson ken@golang.org
- 日付: Wed Jun 11 21:06:26 2008 -0700
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2254a8ee991899f0f72d7fbd116c189582441ea0
元コミット内容
import/export
SVN=122309
変更の背景
このコミットは、Go言語がまだオープンソース化される前の、非常に初期の開発段階(2008年)に行われたものです。当時のGoコンパイラは、現在の go
コマンドとは異なり、gc
(Go compiler frontend) と 6g
(64-bit Go compiler backend) のように、アーキテクチャ固有のバックエンドを持つ構成でした。
主な変更の背景には、以下の点が挙げられます。
- インポート/エクスポート機構の成熟: Go言語のモジュールシステムとパッケージ管理の基盤となるインポート/エクスポート機能は、言語の初期から重要な要素でした。このコミットは、パッケージ間のシンボル(変数、関数、型など)のやり取りをより堅牢かつ正確に行うための改善を目的としています。特に、インポートされた型の再宣言の扱いや、シンボル解決のロジックが洗練されています。
- 関数・メソッド呼び出しの内部処理の改善: Go言語の特徴の一つであるメソッドは、その呼び出しメカニズムがコンパイラ内部で適切に処理される必要があります。このコミットでは、ASTノードにメソッド呼び出しを示すフラグを追加し、コード生成段階でこれを適切に扱うことで、メソッド呼び出しのセマンティクスを正確に反映しようとしています。
- コンパイラのデバッグと診断の強化: コンパイラ開発において、内部状態のデバッグ出力は非常に重要です。このコミットでは、デバッグ出力の有効/無効をより細かく制御するためのメカニズムが導入されており、特にインポート処理中のデバッグ出力の抑制など、特定のコンテキストでの出力を調整できるようになっています。
- 式の評価順序の最適化: コンパイラは、ソースコードの式を効率的に評価するための最適化を行います。特に、副作用を持つ関数呼び出しを含む複雑な式の場合、評価順序は重要です。このコミットでは、式の再順序付けロジックを改善し、一時変数を活用することで、より正確で効率的なコード生成を目指しています。
これらの変更は、Goコンパイラの安定性と正確性を向上させ、言語の基本的な機能が正しく動作するための基盤を固める上で不可欠なステップでした。
前提知識の解説
このコミットの技術的詳細を理解するためには、以下の前提知識が役立ちます。
- Goコンパイラ (
gc
,6g
) の役割:gc
(Go Compiler Frontend): Go言語のソースコードを解析し、抽象構文木(AST)を構築し、型チェック、シンボル解決、一部の最適化など、プラットフォーム非依存の処理を行います。6g
(64-bit Go Compiler Backend):gc
によって生成された中間表現(AST)を受け取り、特定のアーキテクチャ(この場合はAMD64)向けの機械語コードを生成します。8g
はARM64、5g
はARM32など、数字はターゲットアーキテクチャを示します。
- 抽象構文木 (AST - Abstract Syntax Tree):
- コンパイラがソースコードを解析した結果として内部的に構築するツリー構造のデータ表現です。ソースコードの構造を抽象化し、プログラムの意味を表現します。
- Goコンパイラでは、
Node
構造体がASTの各ノードを表します。各ノードは、演算子(op
)、型(type
)、シンボル(sym
)、子ノード(left
,right
,list
)などの情報を持っています。
- シンボルテーブル (
Sym
):- コンパイラがプログラム内で宣言された識別子(変数名、関数名、型名など)の情報を管理するために使用するデータ構造です。各シンボルは、その名前、型、スコープ、ストレージクラスなどの属性を持ちます。
pkglookup
のような関数は、パッケージ名とシンボル名に基づいてシンボルテーブルからエントリを検索するために使用されます。
- インポート/エクスポート:
- Go言語では、
import
キーワードを使用して他のパッケージの機能を利用します。コンパイラは、インポートされたパッケージの公開されたシンボル(エクスポートされたシンボル)を認識し、現在のパッケージで使用できるようにする必要があります。 - エクスポートされた情報は、通常、コンパイル済みのパッケージファイル(Goの初期では
.6
や.8
などの拡張子を持つファイル)に格納されます。
- Go言語では、
- Ullman Number (ウルマン数):
- コンパイラ最適化の文脈で使われる概念で、式の評価順序を決定する際に、レジスタ割り当ての効率などを考慮するために用いられます。各ノードに割り当てられる数値で、そのノードを評価するために必要なレジスタの最小数を示唆します。
walk.c
のreorder1
関数でullman
フィールドが参照されているのは、この概念に基づいています。
- コンパイラ最適化の文脈で使われる概念で、式の評価順序を決定する際に、レジスタ割り当ての効率などを考慮するために用いられます。各ノードに割り当てられる数値で、そのノードを評価するために必要なレジスタの最小数を示唆します。
- Yacc/Lex:
- Yacc (Yet Another Compiler Compiler): 文法定義ファイル(
.y
)からパーサー(構文解析器)を生成するツールです。Goコンパイラのgo.y
は、Go言語の文法規則を定義しており、これに基づいてASTが構築されます。 - Lex (Lexical Analyzer Generator): 字句定義ファイルからレキサー(字句解析器)を生成するツールです。Goコンパイラの
lex.c
は、ソースコードをトークン(識別子、キーワード、演算子など)に分割する処理に関連しています。
- Yacc (Yet Another Compiler Compiler): 文法定義ファイル(
技術的詳細
このコミットは、Goコンパイラの複数の側面に対して、相互に関連する重要な変更を加えています。
1. インポート/エクスポート機構の改善 (src/cmd/gc/export.c
, src/cmd/gc/go.h
, src/cmd/gc/lex.c
)
getimportsym
関数の変更と移動:- 以前は
importfuncnam
の後に定義されていたgetimportsym
関数が、importlooktype
の前に移動し、より汎用的に使用されるようになりました。 - この関数は、インポートされたシンボル(
ss->sym->name
)とパッケージ名(ss->psym->name
またはpkgmyname
)に基づいて、シンボルテーブルからSym
エントリを取得します。 - 重要な変更点として、
s->opackage = ss->osym->name;
という行があります。これは、インポートされたシンボルs
の元のパッケージ名(osym->name
)をopackage
フィールドに保存しています。これにより、シンボルがどのパッケージからエクスポートされたかという情報が保持され、後のシンボル解決やデバッグに役立ちます。
- 以前は
importaddtyp
の型再宣言の扱い:importaddtyp
関数は、インポートされた型t
をシンボルss
に関連付けます。- 変更前は
if(s->otype == T || !eqtype(t, s->otype, 0))
という条件で、既存の型がT
(unknown type) であるか、または新しい型t
と既存の型s->otype
が異なる場合にのみaddtyp
を呼び出していました。 - 変更後は、まず
if(s->otype == T)
で既存の型が不明な場合にaddtyp
を呼び出し、return
します。 - 次に
if(!eqtype(t, s->otype, 0))
で型が異なる場合にprint
文で再宣言を報告し、addtyp
を呼び出します。 - 最後に
print("sametype %S %lT => %lT\\n", s, s->otype, t);
が追加され、同じ型が再宣言された場合にもデバッグ出力が行われるようになりました。 - この変更により、インポート時の型の一貫性チェックがより明示的になり、デバッグ情報も強化されています。
Node
構造体へのmethod
フィールド追加 (src/cmd/gc/go.h
):Node
構造体にuchar method;
フィールドが追加されました。これは、ASTノードがメソッド呼び出しに関連しているかどうかを示すフラグとして使用されます。
- インポートファイル拡張子の変更 (
src/cmd/gc/lex.c
):importfile
関数内で、インポートするパッケージのファイル名を生成する際に、snprint(namebuf, sizeof(namebuf), "%Z.8", f->sval);
がsnprint(namebuf, sizeof(namebuf), "%Z.6", f->sval);
に変更されました。- これは、Goコンパイラの初期段階において、コンパイル済みパッケージファイルの拡張子がターゲットアーキテクチャを示す慣習(例:
6
はamd64、8
はarm64)に従っていたためと考えられます。この変更は、当時のデフォルトのターゲットアーキテクチャが6
(amd64) に統一されたことを示唆している可能性があります。
inimportsys
フラグの導入 (src/cmd/gc/go.h
,src/cmd/gc/lex.c
):EXTERN int inimportsys;
がgo.h
に追加されました。lex.c
のunimportfile
でinimportsys = 0;
が設定され、cannedimports
でinimportsys = 1;
が設定されます。- このフラグは、コンパイラが内部的なシステムインポート(例えば、
unsafe
パッケージのような組み込みパッケージ)を処理しているかどうかを示すために使用されます。
2. 関数・メソッド呼び出しの処理の改善 (src/cmd/6g/gen.c
, src/cmd/6g/gsubr.c
)
cgen_callmeth
の変更 (src/cmd/6g/gen.c
):cgen_callmeth
はメソッド呼び出しのコード生成を担当します。if(l->op != ODOTMETH) fatal("cgen_callmeth: not dotmethod: %N");
というチェックが追加され、ODOTMETH
(ドット記法によるメソッド呼び出し) でない場合は致命的なエラーを発生させるようになりました。これにより、メソッド呼び出しのAST構造が期待通りであることを保証します。
cgen_call
の変更 (src/cmd/6g/gen.c
):cgen_call
は一般的な関数呼び出しのコード生成を担当します。n->left->method = 1;
という行がgins(ACALL, N, n->left);
の直前に追加されました。これは、呼び出し対象のノード(n->left
)がメソッドであることを明示的にマークしています。このmethod
フラグは、go.h
でNode
構造体に追加されたものです。
naddr
でのmethod
フラグの利用 (src/cmd/6g/gsubr.c
):naddr
関数は、ノードのアドレスを解決する際に使用されます。if(n->method)
ブロックが追加され、ノードがメソッドである場合に、その型(n->type
)のパッケージ情報(n->type->sym->opackage
)を使用してシンボルをpkglookup
するロジックが追加されました。これにより、インポートされたパッケージのメソッドが正しく解決されるようになります。
3. スタックと引数管理の調整 (src/cmd/6g/gen.c
, src/cmd/6g/gg.h
, src/cmd/gc/go.h
, src/cmd/gc/go.y
)
maxarg
とstksize
の初期化位置の変更:src/cmd/6g/gen.c
のcompile
関数からmaxarg = 0;
とstksize = 0;
の初期化が削除されました。src/cmd/6g/gg.h
からmaxarg
とstksize
のEXTERN
宣言が削除されました。- これらの変数は
src/cmd/gc/go.h
に移動し、EXTERN long maxarg;
とEXTERN long stksize;
として再宣言されました。 - そして、
src/cmd/gc/go.y
のxfndcl
(関数宣言) ルール内で、{ maxarg = 0; stksize = 0; }
というアクションが追加されました。 - この変更は、
maxarg
(最大引数サイズ) とstksize
(スタックサイズ) の初期化を、関数全体のコンパイル開始時ではなく、個々の関数宣言がパーサーによって処理される直前に行うように変更したことを意味します。これにより、各関数のコンパイルが独立して行われ、これらの変数が適切にリセットされることが保証されます。
4. デバッグ出力の制御強化 (src/cmd/gc/dcl.c
)
dflag()
関数の導入:dcl.c
にdflag()
という新しい関数が導入されました。- この関数は、
debug['d']
(一般的なデバッグフラグ) が設定されているかどうかをチェックし、さらにdebug['y']
(Yaccデバッグフラグ) やinimportsys
(インポートシステム内かどうか) の状態に応じて、デバッグ出力を有効にするかどうかを決定します。 - 具体的には、
inimportsys
がtrue
の場合(つまり、コンパイラが内部的なインポート処理を行っている場合)は、debug['d']
が設定されていてもデバッグ出力を抑制します。これは、内部インポート処理中に大量のデバッグ出力が発生するのを防ぐためと考えられます。
debug['d']
の置き換え:dcl.c
内の既存のif(debug['d'])
の条件がすべてif(dflag())
に置き換えられました。- これにより、デバッグ出力の制御が一元化され、より柔軟な条件でデバッグ情報を表示できるようになりました。
5. 式の再順序付け (reorder1
) の改善 (src/cmd/gc/walk.c
)
reorder1
関数の大幅な変更:reorder1
関数は、式の評価順序を再編成し、最適化を行うための重要なパスです。特に、副作用を持つ関数呼び出しを含む複雑な式を扱う際に、評価順序を決定します。- 変更前は、
yyerror("reorder1: too many function calls evaluating parameters");
というエラーメッセージがありましたが、これが削除されました。これは、新しいロジックがより多くの関数呼び出しを適切に処理できるようになったことを示唆しています。 pass2
ロジックの再構築:- 新しい
pass2
ロジックは、関数呼び出しを一時変数に割り当てることで、評価順序を制御します。 g
は一時変数に割り当てられた関数呼び出しのリスト、f
はスタックに割り当てられた単一の関数呼び出し、r
は関数呼び出しではないノードや一時変数に割り当てられたノードのリストを表します。if(l->ullman < UINF)
: Ullman数が無限大(UINF
)未満のノード(つまり、副作用がないか、評価が単純なノード)は、直接r
リストに追加されます。if(f == N)
:f
がまだ設定されていない場合、現在のノードl
をf
に設定します。- 関数呼び出しの一時変数への割り当て:
a = nod(OXXX, N, N); tempname(a, l->right->type);
で一時変数を表すノードa
を作成します。a = nod(OAS, a, l->right);
で、関数呼び出しの結果(l->right
)をこの一時変数a
に代入するOAS
(assignment) ノードを作成します。- この代入ノード
a
はg
リスト(一時変数に割り当てられた関数呼び出しのリスト)に追加されます。 - 元の関数呼び出しノード
l
の右辺(呼び出し結果)は、この一時変数a->left
に置き換えられます。
- この複雑なロジックは、複数の関数呼び出しが引数として渡される場合などに、それらの評価順序を明確にし、中間結果を一時変数に格納することで、コード生成を簡素化し、潜在的なバグを防ぐことを目的としています。
- 新しい
コアとなるコードの変更箇所
src/cmd/gc/export.c
(インポート処理の改善)
--- a/src/cmd/gc/export.c
+++ b/src/cmd/gc/export.c
@@ -266,18 +266,34 @@ dumpexport(void)
/*
* ******* import *******
*/
+Sym*
+getimportsym(Node *ss)
+{
+ char *pkg;
+ Sym *s;
+
+ if(ss->op != OIMPORT)
+ fatal("getimportsym: oops1 %N\\n", ss);
+
+ pkg = ss->psym->name;
+ if(pkgmyname != S)
+ pkg = pkgmyname->name;
+
+ s = pkglookup(ss->sym->name, pkg);
+
+ /* botch - need some diagnostic checking for the following assignment */
+ s->opackage = ss->osym->name;
+ return s;
+}
+
Type*
importlooktype(Node *n)
{
Sym *s;
-\tif(n->op != OIMPORT)\n-\t\tfatal("importlooktype: oops1 %N\\n", n);\n-\n-\ts = pkglookup(n->sym->name, n->psym->name);\n+\ts = getimportsym(n);\
if(s->otype == T)
fatal("importlooktype: oops2 %S\\n", s);
-\n \treturn s->otype;\
}
Type*
@@ -367,31 +383,22 @@ importfuncnam(Type *t)
}
}
-Sym*
-getimportsym(Node *ss)
-{
- char *pkg;
- Sym *s;
-
-\tpkg = ss->psym->name;\n-\tif(pkgmyname != S)\n-\t\tpkg = pkgmyname->name;\n-\n-\ts = pkglookup(ss->sym->name, pkg);\n-\t/* botch - need some diagnostic checking for the following assignment */\n-\ts->opackage = ss->osym->name;\n-\treturn s;\
-}
-\n void
+void
importaddtyp(Node *ss, Type *t)
{
Sym *s;
s = getimportsym(ss);
-\tif(s->otype == T || !eqtype(t, s->otype, 0)) {\n+\tif(s->otype == T) {\
\t\taddtyp(newtype(s), t, PEXTERN);\
+\t\treturn;\
+\t}\
+\tif(!eqtype(t, s->otype, 0)) {\
+\t\tprint("redeclaring %S %lT => %lT\\n", s, s->otype, t);\
+\t\taddtyp(newtype(s), t, PEXTERN);\
+\t\treturn;\
}\
+\tprint("sametype %S %lT => %lT\\n", s, s->otype, t);\
}
/*
src/cmd/gc/go.h
(Node構造体とグローバル変数)
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -137,6 +137,7 @@ struct Node
uchar trecur; // to detect loops
uchar etype; // op for OASOP, etype for OTYPE, exclam for export
uchar class; // PPARAM, PAUTO, PEXTERN, PSTATIC
+ uchar method; // OCALLMETH name
long vargen; // unique name for OTYPE/ONAME
ulong lineno;
vlong xoffset;
@@ -353,12 +354,15 @@ EXTERN Dcl* externdcl;
EXTERN Dcl* exportlist;
EXTERN int dclcontext; // PEXTERN/PAUTO
EXTERN int importflag;
+EXTERN int inimportsys;
EXTERN Node* booltrue;
EXTERN Node* boolfalse;
EXTERN ulong iota;
EXTERN long vargen;
EXTERN long exportgen;
+EXTERN long maxarg;
+EXTERN long stksize;
EXTERN Node* retnil;
EXTERN Node* fskel;
src/cmd/gc/walk.c
(式の再順序付け)
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -1301,7 +1301,7 @@ Node*
reorder1(Node *n)
{
Iter save;
-\tNode *l, *r, *f;\
+\tNode *l, *r, *f, *a, *g;\
int c, t;\
/*
@@ -1321,10 +1321,6 @@ loop1:
\tif(l == N) {\
\t\tif(c == 0 || t == 1)\
\t\t\treturn n;\
-\t\tif(c > 1) {\
-\t\t\tyyerror("reorder1: too many function calls evaluating parameters");\
-\t\t\treturn n;\
-\t\t}\
\t\tgoto pass2;\
\t}\
\tif(l->op == OLIST)\
@@ -1338,23 +1334,50 @@ loop1:
pass2:
\tl = listfirst(&save, &n);\
-\tr = N;\t// rest\
-\tf = N;\t// fncall\
+\tg = N;\t// fncalls assigned to tempnames\
+\tf = N;\t// one fncall assigned to stack\
+\tr = N;\t// non fncalls and tempnames assigned to stack\
\n loop2:\
\tif(l == N) {\
-\t\tr = nod(OLIST, f, r);\
\t\tr = rev(r);\
+\t\tg = rev(g);\
+\t\tif(g != N)\
+\t\t\tf = nod(OLIST, g, f);\
+\t\tr = nod(OLIST, f, r);\
\t\treturn r;\
\t}\
-\tif(l->ullman >= UINF)\
+\tif(l->ullman < UINF) {\
+\t\tif(r == N)\
+\t\t\tr = l;\
+\t\telse\
+\t\t\tr = nod(OLIST, l, r);\
+\t\tgoto more;\
+\t}\
+\tif(f == N) {\
\t\tf = l;\
+\t\tgoto more;\
+\t}\
+\n\t// make assignment of fncall to tempname\
+\ta = nod(OXXX, N, N);\
+\ttempname(a, l->right->type);\
+\ta = nod(OAS, a, l->right);\
+\n\tif(g == N)\
+\t\tg = a;\
\telse\
+\t\tg = nod(OLIST, a, g);\
+\n\t// put normal arg assignment on list\
+\t// with fncall replaced by tempname\n+\tl->right = a->left;\
\tif(r == N)\
\t\tr = l;\
\telse\
\t\tr = nod(OLIST, l, r);\
\n+more:\
\tl = listnext(&save);\
\tgoto loop2;\
}\
コアとなるコードの解説
src/cmd/gc/export.c
の変更
getimportsym
の再定義と利用:- この関数は、インポートされたシンボルをシンボルテーブルから検索し、そのシンボルがどのパッケージから来たのかという情報(
s->opackage = ss->osym->name;
)を付与する役割を担います。これにより、コンパイラはシンボルの完全な識別子(パッケージ名とシンボル名)を保持し、名前の衝突解決やデバッグに役立てることができます。 importlooktype
から直接pkglookup
を呼び出す代わりにgetimportsym
を呼び出すように変更されたことで、シンボル取得ロジックが一元化され、コードの重複が排除されました。
- この関数は、インポートされたシンボルをシンボルテーブルから検索し、そのシンボルがどのパッケージから来たのかという情報(
importaddtyp
の型再宣言ロジック:- この関数は、インポートされた型をシンボルテーブルに追加する際に、既存の型との整合性をチェックします。
- 変更前は、既存の型が不明 (
T
) であるか、または新しい型と既存の型が異なる場合にのみaddtyp
を呼び出していました。 - 変更後は、まず既存の型が不明な場合に
addtyp
を呼び出し、その後、型が異なる場合に「redeclaring」というメッセージを出力してaddtyp
を呼び出します。最後に、型が同じ場合でも「sametype」というメッセージを出力するように変更されました。 - これにより、インポート時の型の整合性チェックがより厳密になり、開発者が型の不一致や冗長な宣言をデバッグしやすくなりました。
src/cmd/gc/go.h
の変更
Node
構造体へのmethod
フィールド追加:uchar method;
の追加は、ASTノードがメソッド呼び出しを表す場合に、そのノードに特別なマークを付けることを可能にします。これにより、コンパイラの他の部分(特にコード生成バックエンド)が、通常の関数呼び出しとメソッド呼び出しを区別し、それぞれに適切な処理を適用できるようになります。Go言語のメソッドは、レシーバ(メソッドが呼び出されるオブジェクト)を暗黙の第一引数として渡すなど、通常の関数とは異なる呼び出し規約を持つため、この区別は重要です。
maxarg
,stksize
,inimportsys
のEXTERN
宣言:maxarg
とstksize
がgo.h
に移動し、EXTERN
宣言されたことで、これらの変数がコンパイラ全体で共有されるグローバル変数であることが明確になりました。これにより、異なるコンパイルフェーズやファイル間でこれらのスタック関連の情報を共有・更新できるようになります。inimportsys
フラグは、コンパイラが内部的なインポート処理を行っているかどうかを示す状態変数です。これにより、デバッグ出力の抑制など、特定のコンテキストでのコンパイラの挙動を調整することが可能になります。
src/cmd/gc/walk.c
の変更
reorder1
関数のpass2
ロジックの再構築:- この変更は、Goコンパイラの最適化フェーズにおける重要な改善です。
reorder1
は、式の評価順序を決定し、特に副作用を持つ関数呼び出しを含む複雑な式を扱う際に、その評価順序を最適化します。 - 以前のバージョンでは、引数評価における関数呼び出しが多すぎる場合にエラーを発生させていましたが、このエラーが削除されました。これは、新しいロジックがより多くの関数呼び出しを適切に処理できるようになったことを示唆しています。
- 新しい
pass2
ロジックは、関数呼び出しの結果を一時変数に割り当てることで、評価順序を明確に制御します。l->ullman < UINF
の条件は、ノードが副作用を持たないか、評価が単純な場合に真となります。これらのノードは直接r
リスト(非関数呼び出しおよび一時変数に割り当てられたノードのリスト)に追加されます。- 副作用を持つ関数呼び出し(
l->ullman >= UINF
)の場合、その結果を格納するための一時変数(a
)が作成され、関数呼び出しの結果がその一時変数に代入されます(a = nod(OAS, a, l->right);
)。 - この代入ノードは
g
リスト(一時変数に割り当てられた関数呼び出しのリスト)に追加され、元の関数呼び出しノードの右辺(結果)は、この一時変数に置き換えられます(l->right = a->left;
)。
- このメカニズムにより、コンパイラは複雑な式をより予測可能で効率的な方法で評価できるようになり、コード生成の正確性と堅牢性が向上します。
- この変更は、Goコンパイラの最適化フェーズにおける重要な改善です。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
- Goコンパイラのソースコード (現在のバージョン): https://github.com/golang/go/tree/master/src/cmd/compile
- Go言語の初期の歴史に関する情報: https://go.dev/doc/history
- コンパイラ設計の基礎(AST、シンボルテーブル、最適化など)に関する一般的な情報源。
参考にした情報源リンク
- Go言語のソースコード (GitHub): https://github.com/golang/go
- Go言語の初期のコミット履歴 (SVNからGitへの移行前の情報を含む)
- コンパイラ設計に関する一般的な知識 (AST、シンボルテーブル、コード生成、最適化など)
- Ullman Numberに関するコンパイラ最適化の文献
- Yacc/Lexに関するドキュメント