[インデックス 1299] ファイルの概要
このコミットは、Go言語のコンパイラ(gc
)と特定のアーキテクチャ向けコンパイラ(6g
、x86-64向け)に対する変更を含んでいます。主な目的は、Go言語における unsafe
パッケージの基盤を構築することです。具体的には、unsafe
パッケージの型定義をコンパイラに組み込むためのメカニズムを導入し、関連するビルドプロセスとコード構造を更新しています。
コミット
commit 1d4daa2d3919f3df37c780fca651f23c6762b3e1
Author: Ken Thompson <ken@golang.org>
Date: Mon Dec 8 19:46:39 2008 -0800
foundation for import unsafe
R=r
OCL=20794
CL=20794
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1d4daa2d3919f3df37c780fca651f23c6762b3e1
元コミット内容
foundation for import unsafe
R=r
OCL=20794
CL=20794
変更の背景
Go言語は、システムプログラミングの領域でも利用されることを想定して設計されました。そのため、低レベルなメモリ操作や、Goの型システムでは表現できないような操作が必要となる場合があります。このような操作を可能にするために、unsafe
パッケージの導入が不可欠でした。
このコミットは、unsafe
パッケージがGoプログラムから import "unsafe"
として利用できるようになるための初期段階の作業です。具体的には、コンパイラが unsafe
パッケージの定義(特に unsafe.Pointer
型)を認識し、内部的に利用できるようにするための基盤を整備しています。当時のGo言語はまだ開発の非常に初期段階であり、言語のコア機能や標準ライブラリの設計が活発に行われていました。unsafe
パッケージは、Goの型安全性を損なわずに、特定の高度なユースケースに対応するための重要な要素として位置づけられました。
前提知識の解説
- Go言語のコンパイラ (
gc
): Go言語の公式コンパイラであり、Goソースコードを機械語に変換する役割を担います。このコミットの時点では、src/cmd/gc
ディレクトリにそのソースコードが存在していました。 6g
: x86-64アーキテクチャ向けのGoコンパイラです。Goのコンパイラは、ターゲットアーキテクチャごとに異なる名前(例:6g
for amd64,8g
for arm,5g
for arm64など)を持つことがありました。unsafe
パッケージ: Go言語において、型安全性をバイパスして低レベルなメモリ操作を可能にするための特別なパッケージです。主にunsafe.Pointer
型を提供し、任意の型のポインタとuintptr
(符号なし整数型)の間で変換を行うことができます。これは、C言語との相互運用や、特定のパフォーマンス最適化、あるいはGoランタイム自体の実装などで利用されます。sys
パッケージ: このコミット以前から存在していた、Goランタイムの内部で利用される低レベルな関数や型を定義するパッケージです。unsafe
パッケージと同様に、コンパイラによって特別に扱われます。mksys
ユーティリティ:src/cmd/gc/mksys.c
に実装されているC言語のプログラムで、sys.go
やunsafe.go
のようなGoソースファイルから、コンパイラが内部的に利用するC言語のヘッダファイル(sysimport.c
など)を生成する役割を担っていました。これは、Goの初期のコンパイラがC言語で書かれていたため、Goの定義をC言語のコードから利用可能にするための仕組みでした。go.y
(Yacc/Bison): Goコンパイラのパーサー定義ファイルです。Yacc (Yet Another Compiler Compiler) または Bison は、文法定義からパーサーを生成するためのツールです。go.y
はGo言語の構文規則を定義しており、コンパイラがGoソースコードを解析する際に使用されます。any
型: このコミットの時点でのany
型は、現在のGo言語におけるinterface{}
型(任意の型の値を保持できるインターフェース)に相当します。コミット内でthe any type is restricted
というエラーメッセージが見られることから、any
型の利用には特定の制約があったことが伺えます。
技術的詳細
このコミットの核心は、Goコンパイラが unsafe
パッケージの定義を内部的に取り込むための新しいビルドメカニズムとコードパスを導入した点にあります。
-
mksys
ユーティリティの汎用化:- 以前は
sys.go
のみを処理していたmksys
が、引数として渡されたパッケージ名(例:sys
やunsafe
)に基づいて、対応するGoソースファイル(例:sys.go
やunsafe.go
)を処理し、C言語のコードを生成するように変更されました。 - 生成されるCコード内の変数名も、
sysimport
からunsafeimport
のように、パッケージ名に応じて動的に生成されるようになりました。 sys.go
やunsafe.go
の内部でpackage PACKAGE
というプレースホルダーを使用し、mksys
がこれを実際のパッケージ名に置換することで、単一のテンプレートファイルで複数のパッケージに対応できるようにしています。
- 以前は
-
unsafe.go
の導入とsysimport.c
への組み込み:src/cmd/gc/unsafe.go
という新しいファイルが追加されました。このファイルはpackage PACKAGE
とexport type pointer *any;
を含んでいます。これはunsafe.Pointer
型の初期定義です。src/cmd/gc/sysimport.c
にchar *unsafeimport = ...
という新しいC言語の文字列定数が追加されました。この定数には、unsafe.go
の内容がコンパイラが解釈できる形式で埋め込まれています。これは、Goコンパイラが起動時にunsafe
パッケージの定義を「焼き付けられた」形でロードするためのメカニズムです。
-
コンパイラの
import
処理の拡張:src/cmd/gc/lex.c
のimportfile
関数が変更され、import "unsafe"
が検出された場合に、cannedimports("unsafe.6", unsafeimport)
を呼び出すようになりました。cannedimports
関数自体も汎用化され、インポートするファイル名と、その内容を含むC文字列ポインタを引数として受け取るようになりました。これにより、sys
とunsafe
の両方の「組み込みインポート」を同じメカニズムで処理できるようになりました。
-
uintptr
型名の標準化:src/cmd/6g/align.c
でuptrint
という型名がuintptr
に変更されました。これは、Go言語の型名に関する命名規則の標準化の一環と考えられます。uintptr
は、ポインタを保持できる符号なし整数型であり、unsafe
パッケージと密接に関連します。
-
any
型の制約:src/cmd/gc/go.y
にif($1->otype != T && $1->otype->etype == TANY) yyerror("the any type is restricted");
というコードが追加されました。これは、any
型(現在のinterface{}
)の利用に特定の制約を課すもので、おそらくunsafe.Pointer
の導入に伴い、型安全性を維持するための措置と考えられます。
これらの変更により、Goコンパイラは unsafe
パッケージの存在を認識し、その型定義を内部的に利用できるようになりました。これは、Go言語が低レベルな操作をサポートしつつ、その型システムを維持するための重要な一歩でした。
コアとなるコードの変更箇所
src/cmd/gc/Makefile
--- a/src/cmd/gc/Makefile
+++ b/src/cmd/gc/Makefile
@@ -39,10 +39,13 @@ y.tab.h: $(YFILES)
y.tab.c: y.tab.h
test -f y.tab.c && touch y.tab.c
-sysimport.c: sys.go mksys.c
+sysimport.c: sys.go unsafe.go mksys.c
gcc -o mksys mksys.c
6g sys.go
-\t./mksys sys.6 >_sysimport.c && mv _sysimport.c sysimport.c
+\t6g unsafe.go
+\t./mksys sys >_sysimport.c &&\
+\t\t./mksys unsafe >>_sysimport.c &&\
+\t\tmv _sysimport.c sysimport.c
clean:
rm -f $(OFILES) *.6 enam.c 6.out a.out y.tab.h y.tab.c $(LIB) _sysimport.c
sysimport.c
の生成ルールが変更され、unsafe.go
が依存関係に追加され、mksys
が unsafe
パッケージも処理するようになりました。
src/cmd/gc/go.h
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -467,6 +467,7 @@ EXTERN Sym* pkgmyname; // my name for package
EXTERN Sym* pkgimportname; // package name from imported package
EXTERN int tptr; // either TPTR32 or TPTR64
extern char* sysimport;
+extern char* unsafeimport;
EXTERN char* filename; // name to uniqify names
EXTERN void (*dcladj)(Sym*); // declaration is being exported/packaged
@@ -535,7 +536,7 @@ int yyparse(void);\
int mainlex(int, char*[]);
void setfilename(char*);\
void importfile(Val*);\
-void cannedimports(void);\
+void cannedimports(char*, char*);\
void unimportfile();
int32 yylex(void);
void lexinit(void);
unsafeimport
変数の追加と、cannedimports
関数のシグネチャ変更。
src/cmd/gc/lex.c
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -218,6 +218,11 @@ importfile(Val *f)\
return;
}\
+\tif(strcmp(f->u.sval->s, \"unsafe\") == 0) {\
+\t\tcannedimports(\"unsafe.6\", unsafeimport);\
+\t\treturn;\
+\t}\
+\
if(!findpkg(f->u.sval))\
\tfatal(\"can\'t find import: %Z\", f->u.sval);\
imp = Bopen(namebuf, OREAD);\
@@ -277,11 +282,8 @@ unimportfile(void)\
}\
void
-cannedimports(void)\
+cannedimports(char *file, char *cp)\
{\
-\tchar *file;\
-\
-\tfile = \"sys.6\";\
lineno++; // if sys.6 is included on line 1,\
linehist(file, 0); // the debugger gets confused
@@ -290,7 +292,7 @@ cannedimports(void)\
curio.peekc = 0;\
curio.peekc1 = 0;\
curio.infile = file;\
-\tcurio.cp = sysimport;\
+\tcurio.cp = cp;\
pkgmyname = S;\
inimportsys = 1;
import "unsafe"
の処理ロジックが追加され、cannedimports
が汎用化されました。
src/cmd/gc/mksys.c
--- a/src/cmd/gc/mksys.c
+++ b/src/cmd/gc/mksys.c
@@ -13,15 +13,22 @@
int
main(int argc, char **argv)
{\
+\tchar *name;\
FILE *fin;\
-\tchar buf[1024], *p, *q;\
+\tchar buf[1024], initfunc[1024], *p, *q;\
\tif(argc != 2) {\
-\t\tfprintf(stderr, \"usage: mksys sys.6\\n\");
+\t\tfprintf(stderr, \"usage: sys sys\\n\");
+\t\tfprintf(stderr, \"in file $1.6 s/PACKAGE/$1/\\n\");
\t\texit(1);\
\t}\
-\tif((fin = fopen(argv[1], \"r\")) == NULL) {\
-\t\tfprintf(stderr, \"open %s: %s\\n\", argv[1], strerror(errno));
+\n+\tname = argv[1];\
+\tsnprintf(initfunc, sizeof(initfunc), \"init_%s_function\", name);\
+\n+\tsnprintf(buf, sizeof(buf), \"%s.6\", name);\
+\tif((fin = fopen(buf, \"r\")) == NULL) {\
+\t\tfprintf(stderr, \"open %s: %s\\n\", buf, strerror(errno));
\t\texit(1);\
\t}\
@@ -33,7 +40,7 @@ main(int argc, char **argv)\
\texit(1);\
begin:\
-\tprintf(\"char *sysimport = \\n\");
+\tprintf(\"char *%simport = \\n\", name);\
\t// process imports, stopping at $$ that closes them
\twhile(fgets(buf, sizeof buf, fin) != NULL) {\
@@ -45,17 +52,21 @@ begin:\
\t\tfor(p=buf; *p==\' \' || *p == \'\\t\'; p++)\
\t\t\t;\
-\t\t// cut out decl of init_sys_function - it doesn\'t exist
-\t\tif(strstr(buf, \"init_sys_function\"))
+\t\t// cut out decl of init_$1_function - it doesn\'t exist
+\t\tif(strstr(buf, initfunc))\
\t\t\tcontinue;\
-\t\t// sys.go claims to be in package SYS to avoid
-\t\t// conflicts during \"6g sys.go\". rename SYS to sys.\
-\t\tfor(q=p; *q; q++)\
-\t\t\tif(memcmp(q, \"SYS\", 3) == 0)\
-\t\t\t\tmemmove(q, \"sys\", 3);\
+\t\t// sys.go claims to be in package PACKAGE to avoid
+\t\t// conflicts during \"6g sys.go\". rename PACKAGE to $2.\
+\t\tprintf(\"\\t\\\"\");\
+\t\twhile(q = strstr(p, \"PACKAGE\")) {\
+\t\t\t*q = 0;\
+\t\t\tprintf(\"%s\", p);\t// up to the substitution
+\t\t\tprintf(\"%s\", name);\t// the sub name
+\t\t\tp = q+7;\t\t// continue with rest
+\t\t}\
-\t\tprintf(\"\\t\\\"%s\\\\n\\\"\\n\", p);\
+\t\tprintf(\"%s\\\\n\\\"\\n\", p);\
\t}\
\tfprintf(stderr, \"did not find end of imports\\n\");
\texit(1);\
mksys
ユーティリティが汎用化され、引数としてパッケージ名を受け取り、PACKAGE
プレースホルダーを置換するようになりました。
src/cmd/gc/sysimport.c
--- a/src/cmd/gc/sysimport.c
+++ b/src/cmd/gc/sysimport.c
@@ -79,3 +79,8 @@ char *sysimport =\
\"export func sys.semrelease (sema *int32)\\n\"\
\"\\n\"\
\"$$\\n\";
+char *unsafeimport =
+\t\"package unsafe\\n\"\
+\t\"export type unsafe.pointer *any\\n\"\
+\t\"\\n\"\
+\t\"$$\\n\";
unsafeimport
という新しいC文字列定数が追加され、unsafe
パッケージの初期定義(unsafe.pointer *any
)が含まれています。
src/cmd/gc/unsafe.go
--- /dev/null
+++ b/src/cmd/gc/unsafe.go
@@ -0,0 +1,8 @@
+// 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.
+
+
+package PACKAGE
+
+export type pointer *any;
新しく追加されたファイルで、unsafe
パッケージのGoソースコードとしての定義を含みます。
コアとなるコードの解説
このコミットの最も重要な変更は、Goコンパイラが unsafe
パッケージを「組み込み」で認識し、その定義を利用できるようにした点です。
-
unsafe.go
とsysimport.c
の連携:unsafe.go
はunsafe
パッケージのGo言語での定義(type pointer *any
)を提供します。mksys
ユーティリティは、このunsafe.go
を読み込み、その内容をC言語の文字列リテラルとしてsysimport.c
内のunsafeimport
変数に埋め込みます。- これにより、Goコンパイラ(C言語で書かれている部分)は、コンパイル時に
unsafe
パッケージの定義を直接参照できるようになります。これは、Goの標準ライブラリの一部がGoコンパイラ自体に「焼き付けられている」初期のGoの設計パターンを示しています。
-
import "unsafe"
の処理:- Goソースコード内で
import "unsafe"
が記述されると、コンパイラの字句解析器(lex.c
)がこれを検出し、cannedimports("unsafe.6", unsafeimport)
を呼び出します。 cannedimports
関数は、unsafeimport
に格納されたC文字列の内容を、あたかもunsafe.6
というファイルから読み込んだかのように処理します。これにより、unsafe.pointer
などの型がコンパイラのシンボルテーブルに登録され、Goコード内で利用可能になります。
- Goソースコード内で
-
mksys
の汎用化の意義:mksys
がsys
とunsafe
の両方を処理できるように汎用化されたことは、Goコンパイラのビルドシステムがより柔軟になったことを意味します。これにより、将来的に他の特別な組み込みパッケージを追加する際にも、同様のメカニズムを再利用できるようになります。package PACKAGE
の置換は、この汎用化を実現するための巧妙な方法です。
-
uintptr
とany
の関連:uintptr
は、ポインタの値を整数として扱うための型であり、unsafe.Pointer
との間で相互変換が可能です。このコミットでuptrint
からuintptr
への名称変更が行われたことは、unsafe
パッケージの導入と合わせて、低レベルメモリ操作の基盤が整備されつつあったことを示唆しています。any
型(interface{}
)に対する制約の追加は、unsafe.Pointer
が任意の型へのポインタを表現できるようになったことで、型安全性を維持するためのバランスを取る必要があったことを示しています。
これらの変更は、Go言語がその初期段階で、型安全性と低レベルな操作のバランスをどのように取ろうとしていたかを示す貴重な例です。
関連リンク
- Go言語の
unsafe
パッケージのドキュメント: https://pkg.go.dev/unsafe - Go言語の
uintptr
型のドキュメント: https://pkg.go.dev/builtin#uintptr
参考にした情報源リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語の初期の歴史に関する情報(一般的なGoの歴史サイトやブログ記事など)
- Yacc/Bisonに関する一般的な情報(パーサー生成の概念理解のため)