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

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

このコミットは、Go言語の初期開発における重要な変更点を示しています。具体的には、Goのツールチェインを構成するアセンブラ (8a)、Cコンパイラ (8c)、リンカ (8l) が、Go環境でビルドできるようにするための調整が行われています。主要な変更は、C言語のデータ型である long を固定幅整数型 int32 に置き換えること、不要なコードの削除、そして enam.c8.out.hmkenam といったビルド関連ファイルを 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オペレーティングシステムのツールチェイン(8a8c8lなど)から派生しています。初期のGo開発では、これらのツールをGo自身の開発環境に適合させ、より堅牢で移植性の高いものにする必要がありました。

主な背景は以下の通りです。

  1. Go環境への統合: 8a (アセンブラ)、8c (Cコンパイラ)、8l (リンカ) は、Go言語のコンパイルとリンクのプロセスにおいて中心的な役割を担っていました。これらのツールがGo自身のビルドシステム内でシームレスに動作するように、ビルドスクリプト(Makefile)やコードベースの調整が必要でした。
  2. データ型の一貫性と移植性: C言語の long 型は、そのサイズがコンパイラやターゲットアーキテクチャによって異なる「実装定義」の型です(通常32ビットまたは64ビット)。これに対し、int32<stdint.h> で定義される固定幅整数型であり、常に32ビットであることが保証されます。Go言語はクロスプラットフォームでの動作を重視するため、ツールチェイン内部で利用されるデータ型も、環境に依存しない固定幅の型を使用することが望ましいと判断されました。これにより、異なるシステム上でのビルドの安定性と予測可能性が向上します。
  3. コードベースの整理と最適化: 開発が進むにつれて、不要になった「デッドコード」が発生します。これらを削除することで、コードベースの保守性を高め、ビルドサイズを削減し、将来的な開発の複雑さを軽減します。
  4. リンカの役割の明確化: enam.c8.out.hmkenam といったファイルは、アセンブリ命令の名前定義やオブジェクトファイルの構造に関連するものです。これらをコンパイラ (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ビット整数です。これらの型は、特定のビット幅を持つ整数が必要な場合に、移植性を確保するために使用されます。例えば、ネットワークプロトコルやファイルフォーマットなど、データのバイナリ表現が厳密に定義されている場合に不可欠です。

このコミットでは、longint32 に置き換えることで、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独自の環境で動作させるための重要なステップです。

  1. ビルド環境の変更:

    • src/cmd/8a/Makefilesrc/cmd/8c/Makefile が新規作成され、それぞれのツールがGoのビルドシステム (Make.conf をインクルード) に従ってビルドされるように変更されました。これにより、Goのソースツリー内で 8a8c8l を一貫してビルドできるようになります。
    • リンカ (8l) のMakefileも更新され、enam.c の生成プロセスが 8.out.h に依存するように明示されています。
  2. 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 に変更されており、これもデータ表現の一貫性を保つためのものです。
  3. デッドコードの削除:

    • 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) が不要になったことを示唆しており、より標準的なメモリ管理手法への移行を意味します。
  4. ファイルの移動とリンカの役割強化:

    • src/cmd/8c/8.out.hsrc/cmd/8l/8.out.h に、src/cmd/8c/mkenamsrc/cmd/8l/mkenam にそれぞれリネーム(実質的な移動)されました。
    • これにより、8.out.hmkenam がリンカ (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.hsrc/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 は、8a8c がGoのビルドシステムに組み込まれることを示しています。include ../../Make.conf は、Goプロジェクト全体で共有されるビルド設定を読み込むことを意味します。これにより、Goのツールチェインのビルドプロセスが標準化され、Goのソースツリー内で一貫して管理できるようになります。

8lMakefile に追加された enam.c: 8.out.h ルールは、enam.c8.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.hmkenam8c から 8l のディレクトリに移動されたことは、リンカ (8l) の役割が強化されたことを示しています。リンカは最終的な実行可能ファイルを生成する際に、アセンブリ命令の構造やシンボリックな表現に関する詳細な情報が必要です。これらのファイルをリンカの管轄下に置くことで、リンカがより自己完結的になり、コンパイラとリンカ間の依存関係が整理されます。これは、ツールチェインの各コンポーネントの責任範囲を明確にし、全体的な設計の整合性を高める上で重要です。

これらの変更は、Go言語がその初期段階で、Plan 9の強力な影響を受けつつも、独自の道を切り開き、現代のソフトウェア開発のニーズに応えるための基盤を築いていった過程を明確に示しています。

関連リンク

参考にした情報源リンク