[インデックス 1410] ファイルの概要
このコミットは、Go言語の初期開発における重要な変更点を示しています。具体的には、Goのツールチェインを構成するアセンブラ (8a
)、Cコンパイラ (8c
)、リンカ (8l
) が、Go環境でビルドできるようにするための調整が行われています。主要な変更は、C言語のデータ型である long
を固定幅整数型 int32
に置き換えること、不要なコードの削除、そして enam.c
、8.out.h
、mkenam
といったビルド関連ファイルを 8c
から 8l
へ移動することです。これにより、Goツールチェインの移植性と堅牢性が向上しています。
コミット
af0143ce03c6a1516a615f07c84a6f029f82399d
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/af0143ce03c6a1516a615f07c84a6f029f82399d
元コミット内容
make 8a, 8c, 8l build in go environment.
,s/int32/long/g in 8a, 8c, 8l.
delete dead code.
move enam.c, 8.out.h, mkenam from 8c to 8l.
R=r
DELTA=1850 (581 added, 983 deleted, 286 changed)
OCL=22119
CL=22129
変更の背景
このコミットは、Go言語の初期段階において、そのビルドシステムとツールチェインの基盤を固めるためのものです。Go言語のツールチェインは、Plan 9オペレーティングシステムのツールチェイン(8a
、8c
、8l
など)から派生しています。初期のGo開発では、これらのツールをGo自身の開発環境に適合させ、より堅牢で移植性の高いものにする必要がありました。
主な背景は以下の通りです。
- Go環境への統合:
8a
(アセンブラ)、8c
(Cコンパイラ)、8l
(リンカ) は、Go言語のコンパイルとリンクのプロセスにおいて中心的な役割を担っていました。これらのツールがGo自身のビルドシステム内でシームレスに動作するように、ビルドスクリプト(Makefile)やコードベースの調整が必要でした。 - データ型の一貫性と移植性: C言語の
long
型は、そのサイズがコンパイラやターゲットアーキテクチャによって異なる「実装定義」の型です(通常32ビットまたは64ビット)。これに対し、int32
は<stdint.h>
で定義される固定幅整数型であり、常に32ビットであることが保証されます。Go言語はクロスプラットフォームでの動作を重視するため、ツールチェイン内部で利用されるデータ型も、環境に依存しない固定幅の型を使用することが望ましいと判断されました。これにより、異なるシステム上でのビルドの安定性と予測可能性が向上します。 - コードベースの整理と最適化: 開発が進むにつれて、不要になった「デッドコード」が発生します。これらを削除することで、コードベースの保守性を高め、ビルドサイズを削減し、将来的な開発の複雑さを軽減します。
- リンカの役割の明確化:
enam.c
、8.out.h
、mkenam
といったファイルは、アセンブリ命令の名前定義やオブジェクトファイルの構造に関連するものです。これらをコンパイラ (8c
) 側からリンカ (8l
) 側に移動することで、リンカが最終的な実行可能ファイルを生成する際に必要な情報を一元的に管理し、ツールチェインの各コンポーネントの責任範囲をより明確にする狙いがあります。これは、Plan 9のツールチェインがコンパイラとリンカでアセンブリプロセスを分担している設計思想にも合致します。
これらの変更は、Go言語がその後の急速な発展を遂げる上で不可欠な、堅牢で移植性の高い基盤を築くための初期の取り組みの一部でした。
前提知識の解説
Plan 9ツールチェイン (8a, 8c, 8l)
Go言語の初期のツールチェインは、Bell Labsで開発されたオペレーティングシステム「Plan 9」のツールチェインに強く影響を受けています。Plan 9のツールチェインは、そのシンプルさと移植性で知られています。
- 命名規則: Plan 9のツールは、ターゲットとするCPUアーキテクチャを示す数字と、ツールの種類を示す文字の組み合わせで命名されます。
8
: Intel 386アーキテクチャ(32ビット)c
: Cコンパイラa
: アセンブラl
: リンカ
8a
(アセンブラ): Intel 386アーキテクチャ向けのアセンブラです。Go言語のアセンブリコードをオブジェクトファイルに変換する役割を担っていました。現代のGoではgo tool asm
に相当します。8c
(Cコンパイラ): Intel 386アーキテクチャ向けのCコンパイラです。Go言語のコンパイラ自体がC言語で書かれていたため、このコンパイラがGoコンパイラ自身のビルドに使われていました。現代のGoではgo tool compile
に相当します。8l
(リンカ): Intel 386アーキテクチャ向けのリンカです。オブジェクトファイルを結合して実行可能ファイルを生成する役割を担っていました。現代のGoではgo tool link
に相当します。
これらのツールは、現代のGo開発では直接コマンドラインから呼び出すことは稀ですが、go build
コマンドの内部でこれらの機能が呼び出されています。
int32
vs long
(C言語における固定幅整数型)
C言語には、整数型として int
, long
, short
などがありますが、これらの型のビット幅はC標準によって「最小限の保証」しかされておらず、具体的なサイズはコンパイラや実行環境(アーキテクチャ)に依存します。
long
: C言語の基本整数型の一つで、少なくとも32ビット(4バイト)であることが保証されています。しかし、32ビットシステムでは32ビット、64ビットシステムでは64ビットとなることが一般的であり、そのサイズは環境によって変動します。この「実装定義」の性質は、異なるプラットフォーム間でコードを移植する際に問題を引き起こす可能性があります。例えば、あるシステムでlong
が32ビットであると仮定して書かれたコードが、別のシステムでlong
が64ビットになると予期せぬバグ(オーバーフローなど)を引き起こす可能性があります。int32
(またはint32_t
): C99標準で導入された<stdint.h>
ヘッダで定義される「固定幅整数型」の一つです。int32_t
は、その名の通り、符号付き32ビット整数であることが厳密に保証されます。同様にuint32_t
は符号なし32ビット整数です。これらの型は、特定のビット幅を持つ整数が必要な場合に、移植性を確保するために使用されます。例えば、ネットワークプロトコルやファイルフォーマットなど、データのバイナリ表現が厳密に定義されている場合に不可欠です。
このコミットでは、long
を int32
に置き換えることで、Goツールチェイン内部の数値計算やデータ構造が、どのプラットフォームでビルドされても一貫した32ビット幅を持つようにし、移植性と信頼性を向上させています。
8.out.h
, enam.c
, mkenam
これらはPlan 9のツールチェインにおけるビルドプロセスに関連するファイル群です。
8.out.h
: Plan 9のIntel 386アーキテクチャ向けオブジェクトファイルフォーマット (8.out
) の定義を含むヘッダファイルです。オブジェクトファイルの構造や、アセンブリ命令のオペコードなどが定義されている可能性があります。mkenam
: これはシェルスクリプトまたは小さなプログラムで、enam.c
を生成するために使用されます。通常、8.out.h
のようなヘッダファイルからアセンブリ命令のニーモニック(例:MOV
,ADD
)を抽出し、それらをC言語の文字列配列としてenam.c
に書き出す役割を担います。enam.c
:mkenam
によって生成されるCソースファイルです。アセンブリ命令のニーモニックの文字列配列(例:char* anames[] = {"MOV", "ADD", ...}
)を含んでおり、これはツールチェイン内部で命令コードと文字列表現を相互変換するために使用されます。
これらのファイルが 8c
(コンパイラ) から 8l
(リンカ) に移動されたのは、リンカが最終的な実行可能ファイルを生成する際に、アセンブリ命令に関する詳細な知識(命令名、オペコードなど)を必要とするためです。これにより、リンカの自己完結性が高まり、ツールチェインのモジュール性が向上します。
技術的詳細
このコミットは、Go言語のビルドシステムをPlan 9のツールチェインから独立させ、Go独自の環境で動作させるための重要なステップです。
-
ビルド環境の変更:
src/cmd/8a/Makefile
とsrc/cmd/8c/Makefile
が新規作成され、それぞれのツールがGoのビルドシステム (Make.conf
をインクルード) に従ってビルドされるように変更されました。これにより、Goのソースツリー内で8a
、8c
、8l
を一貫してビルドできるようになります。- リンカ (
8l
) のMakefileも更新され、enam.c
の生成プロセスが8.out.h
に依存するように明示されています。
-
long
からint32
への型変換:src/cmd/8a/a.h
,src/cmd/8a/a.y
,src/cmd/8c/cgen.c
,src/cmd/8c/cgen64.c
,src/cmd/8c/div.c
,src/cmd/8c/gc.h
,src/cmd/8c/mul.c
,src/cmd/8c/reg.c
,src/cmd/8c/sgen.c
,src/cmd/8c/swt.c
,src/cmd/8c/txt.c
,src/cmd/8l/asm.c
,src/cmd/8l/l.h
,src/cmd/8l/obj.c
,src/cmd/8l/pass.c
,src/cmd/8l/span.c
など、多数のCソースファイルおよびヘッダファイルでlong
型がint32
またはuint32
に変更されています。- これは、Goツールチェインが扱う数値データ(オフセット、行番号、サイズ、アドレスなど)のビット幅を明示的に32ビットに固定し、異なるアーキテクチャやコンパイラ環境でのビルド結果の一貫性を保証するためのものです。特に、ポインタのサイズが64ビットになる64ビットシステムにおいても、これらの内部データが32ビットで扱われることで、メモリ使用量の削減やデータ構造の簡素化に寄与します。
8.out.h
内のIeee
構造体(浮動小数点数の内部表現)のメンバーもlong
からint32
に変更されており、これもデータ表現の一貫性を保つためのものです。
-
デッドコードの削除:
src/cmd/8a/a.h
から、mywait
,mycreat
,systemtype
,pathchar
,mygetwd
,myexec
,mydup
,myfork
,mypipe
,mysbrk
といったPlan 9固有のシステムコールラッパー関数の宣言が削除されています。これらはGo環境では不要になったか、Go自身のランタイムによって提供される機能に置き換えられたためと考えられます。src/cmd/8c/cgen64.c
から、cast
,swapregs
,swappairs
,saveme
,saveit
,restoreit
,forcereg
といった関数が削除されています。これらは64ビットコード生成における特定の最適化やレジスタ管理に関連するもので、Goの新しいコード生成戦略やランタイムの進化に伴い不要になった可能性があります。src/cmd/8l/obj.c
からgethunk
関数が削除され、mal
(おそらくmalloc
のようなメモリ割り当て関数) の直接呼び出しに置き換えられています。これは、独自のメモリプール管理 (hunk
) が不要になったことを示唆しており、より標準的なメモリ管理手法への移行を意味します。
-
ファイルの移動とリンカの役割強化:
src/cmd/8c/8.out.h
がsrc/cmd/8l/8.out.h
に、src/cmd/8c/mkenam
がsrc/cmd/8l/mkenam
にそれぞれリネーム(実質的な移動)されました。- これにより、
8.out.h
とmkenam
がリンカ (8l
) のディレクトリに配置され、リンカがオブジェクトファイルの構造やアセンブリ命令に関する情報を直接参照・利用できるようになります。これは、リンカが最終的なバイナリ生成においてより多くの責任を持つという設計思想を反映しています。
これらの変更は、Go言語のツールチェインがPlan 9の遺産から脱却し、Go自身の設計思想(シンプルさ、堅牢性、移植性)に合致する、より独立した効率的なビルドシステムへと進化する過程を示しています。
コアとなるコードの変更箇所
このコミットでは広範囲にわたるファイルが変更されていますが、特に重要な変更箇所をいくつか抜粋して解説します。
1. Makefile
の変更 (Go環境への適応)
src/cmd/8a/Makefile
(新規作成) と src/cmd/8c/Makefile
(新規作成) は、Goのビルドシステムに統合されることを示しています。
--- /dev/null
+++ b/src/cmd/8a/Makefile
@@ -0,0 +1,42 @@
+# Copyright 2009 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../Make.conf
+
+TARG=\
+ 8a\
+
+HFILES=\
+ a.h\
+ y.tab.h\
+ ../8l/8.out.h\
+ compat.h\
+
+OFILES=\
+ y.tab.$O\
+ lex.$O\
+ compat.$O\
+ ../8l/enam.$O\
+
+YFILES=\
+ a.y\
+
+$(TARG): $(OFILES)
+ $(LD) -o $(TARG) -L$(GOROOT)/lib $(OFILES) -lbio -l9
+
+$(OFILES): $(HFILES)
+
+lex.$O: ../cc/macbody ../cc/lexbody
+
+y.tab.h: $(YFILES)
+ bison -y $(YFLAGS) $(YFILES)
+
+y.tab.c: y.tab.h
+ test -f y.tab.c && touch y.tab.c
+
+clean:
+ rm -f $(OFILES) $(TARG) *.6 enam.c 6.out a.out y.tab.h y.tab.c
+
+install: $(TARG)
+ cp $(TARG) $(BIN)/$(TARG)
同様の変更が src/cmd/8c/Makefile
にも適用されています。
src/cmd/8l/Makefile
も更新され、enam.c
の生成が 8.out.h
に依存することが明示されています。
--- a/src/cmd/8l/Makefile
+++ b/src/cmd/8l/Makefile
@@ -20,6 +20,7 @@
$(OFILES): $(HFILES)
+enam.c: 8.out.h
+ sh mkenam
+
clean:
rm -f $(OFILES) $(TARG) *.8 enam.c 8.out a.out
2. long
から int32
への型変換の例
src/cmd/8a/a.h
における value
, offset
, line
, lineno
, nhunk
, pc
, thunk
などの型が long
から int32
に変更されています。
--- a/src/cmd/8a/a.h
+++ b/src/cmd/8a/a.h
@@ -63,7 +65,7 @@ struct Sym
Sym* link;
Ref* ref;
char* macro;
- long value;
+ int32 value;
ushort type;
char *name;
char sym;
@@ -101,7 +103,7 @@ struct Gen
{
double dval;
char sval[8];
- long offset;
+ int32 offset;
Sym* sym;
short type;
short index;
@@ -117,8 +119,8 @@ struct Hist
{
Hist* link;
char* name;
- long line;
- long offset;
+ int32 line;
+ int32 offset;
};
#define H ((Hist*)0)
@@ -143,24 +145,24 @@ EXTERN char* include[NINCLUDE];
EXTERN Io* iofree;
EXTERN Io* ionext;
EXTERN Io* iostack;
-EXTERN long lineno;
+EXTERN int32 lineno;
EXTERN int nerrors;
-EXTERN long nhunk;
+EXTERN int32 nhunk;
EXTERN int ninclude;
EXTERN Gen nullgen;
EXTERN char* outfile;
EXTERN int pass;
EXTERN char* pathname;
-EXTERN long pc;
+EXTERN int32 pc;
EXTERN int peekc;
EXTERN int sym;
EXTERN char symb[NSYMB];
EXTERN int thechar;
EXTERN char* thestring;
-EXTERN long thunk;
+EXTERN int32 thunk;
EXTERN Biobuf obuf;
-void* allocn(void*, long, long);
+void* allocn(void*, int32, int32);
void errorexit(void);
void pushio(void);
void newio(void);
@@ -168,7 +170,7 @@ void newfile(char*, int);
Sym* slookup(char*);
Sym* lookup(void);
void syminit(Sym*);
-long yylex(void);
+int32 yylex(void);
int getc(void);
int getnsc(void);
void unget(int);
@@ -195,7 +197,7 @@ void maclin(void);
void macif(int);
void macend(void);
void dodefine(char*);
-void prfile(long);
+void prfile(int32);
void linehist(char*, int);
void gethunk(void);
void yyerror(char*, ...);
3. デッドコードの削除の例
src/cmd/8a/a.h
からPlan 9固有のシステムコールラッパーの宣言が削除されています。
--- a/src/cmd/8a/a.h
+++ b/src/cmd/8a/a.h
@@ -195,19 +197,3 @@ void maclin(void);
void macif(int);
void macend(void);
void dodefine(char*);
-void prfile(long);
+void prfile(int32);
void linehist(char*, int);
void gethunk(void);
void yyerror(char*, ...);
int yyparse(void);
void setinclude(char*);
int assemble(char*);
-
-/*
- * Posix.c/Inferno.c/Nt.c
- */
-enum /* keep in synch with ../cc/cc.h */
-{
- Plan9 = 1<<0,
- Unix = 1<<1,
- Windows = 1<<2
-};
-int mywait(int*);
-int mycreat(char*, int);
-int systemtype(int);
-int pathchar(void);
-char* mygetwd(char*, int);
-int myexec(char*, char*[]);
-int mydup(int, int);
-int myfork(void);
-int mypipe(int*);
-void* mysbrk(ulong);
4. ファイルの移動の例
src/cmd/8c/8.out.h
が src/cmd/8l/8.out.h
にリネームされています。
--- a/src/cmd/8c/8.out.h
+++ b/src/cmd/8l/8.out.h
@@ -468,8 +468,8 @@ enum
typedef struct ieee Ieee;
struct ieee
{
- long l; /* contains ls-man 0xffffffff */
- long h; /* contains sign 0x80000000
+ int32 l; /* contains ls-man 0xffffffff */
+ int32 h; /* contains sign 0x80000000
exp 0x7ff00000
ms-man 0x000fffff */
};
コアとなるコードの解説
1. Makefile
の変更
新規作成された Makefile
は、8a
と 8c
がGoのビルドシステムに組み込まれることを示しています。include ../../Make.conf
は、Goプロジェクト全体で共有されるビルド設定を読み込むことを意味します。これにより、Goのツールチェインのビルドプロセスが標準化され、Goのソースツリー内で一貫して管理できるようになります。
8l
の Makefile
に追加された enam.c: 8.out.h
ルールは、enam.c
が 8.out.h
を基に mkenam
スクリプトによって生成されることを明示しています。これは、リンカがアセンブリ命令のシンボリックな名前を内部的に利用するために必要なステップです。
2. long
から int32
への型変換
この変更は、Goツールチェインの移植性と堅牢性を大幅に向上させます。long
型はシステムによってサイズが異なるため、異なる環境でビルドすると予期せぬ動作を引き起こす可能性がありました。int32
(または uint32
) に統一することで、Goツールチェインが扱う内部データ(例えば、メモリのアドレスオフセット、ファイル内の行番号、シンボルの値など)が常に32ビット幅で表現されることが保証されます。これにより、32ビットシステムと64ビットシステムの両方で、Goツールチェインが同じように動作することが期待でき、クロスコンパイルや異なる環境での開発が容易になります。
3. デッドコードの削除
src/cmd/8a/a.h
から削除されたPlan 9固有のシステムコールラッパーは、Goのランタイムが独自のシステムインターフェースを提供するため、もはや不要になったことを示しています。これは、GoがPlan 9の基盤から独立し、独自の実行環境を構築している証拠です。
src/cmd/8c/cgen64.c
から削除された関数群は、64ビットコード生成における特定の最適化やレジスタ管理に関連するものです。これらの削除は、Goのコンパイラがより洗練されたコード生成戦略を採用したか、あるいはGoのランタイムがこれらの低レベルな最適化をより効率的に処理できるようになったことを示唆しています。
src/cmd/8l/obj.c
から gethunk
が削除され、mal
に置き換えられたことは、Goツールチェインが独自のメモリプール管理から、より標準的なメモリ割り当てメカニズムに移行したことを意味します。これにより、メモリ管理の複雑さが軽減され、コードの可読性と保守性が向上します。
4. ファイルの移動
8.out.h
と mkenam
が 8c
から 8l
のディレクトリに移動されたことは、リンカ (8l
) の役割が強化されたことを示しています。リンカは最終的な実行可能ファイルを生成する際に、アセンブリ命令の構造やシンボリックな表現に関する詳細な情報が必要です。これらのファイルをリンカの管轄下に置くことで、リンカがより自己完結的になり、コンパイラとリンカ間の依存関係が整理されます。これは、ツールチェインの各コンポーネントの責任範囲を明確にし、全体的な設計の整合性を高める上で重要です。
これらの変更は、Go言語がその初期段階で、Plan 9の強力な影響を受けつつも、独自の道を切り開き、現代のソフトウェア開発のニーズに応えるための基盤を築いていった過程を明確に示しています。
関連リンク
- Go言語のツールチェインの歴史:
- Plan 9のツールチェイン:
- C言語の固定幅整数型: