[インデックス 18794] ファイルの概要
このコミットは、Goコンパイラのバックエンド(cmd/gc
)および特定アーキテクチャ向けコンパイラ(cmd/5g
, cmd/6g
, cmd/8g
)におけるリンカアーキテクチャの初期化メカニズムの導入と、amd64p32
アーキテクチャのサポート追加に関するものです。具体的には、src/cmd/5g/galign.c
, src/cmd/6g/galign.c
, src/cmd/8g/galign.c
, src/cmd/gc/go.h
, src/cmd/gc/lex.c
の5つのファイルが変更されています。
コミット
commit e31a1ce109e0ddd0efba95fc724a424a27824d
Author: Dave Cheney <dave@cheney.net>
Date: Fri Mar 7 15:33:44 2014 +1100
cmd/gc, cmd/5g, cmd/6g, cmd/8g: introduce linkarchinit and add amd64p32 support
Replaces CL 70000043.
Introduce linkarchinit() from cmd/ld.
For cmd/6g, switch to the amd64p32 linker model if we are building under nacl/amd64p32.
LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/71330045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e31a1ce109e0ddd0efba95fc724a8b424a27824d
元コミット内容
cmd/gc, cmd/5g, cmd/6g, cmd/8g: introduce linkarchinit and add amd64p32 support
Replaces CL 70000043.
Introduce linkarchinit() from cmd/ld.
For cmd/6g, switch to the amd64p32 linker model if we are building under nacl/amd64p32.
LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/71330045
変更の背景
このコミットの主な背景は、GoコンパイラがGoogle Native Client (NaCl) 環境におけるamd64p32
アーキテクチャを適切にサポートするための基盤を整備することにあります。
Goコンパイラは、異なるターゲットアーキテクチャ(例: ARM, AMD64, 386)向けにコードを生成するために、それぞれのアーキテクチャに特化したリンカモデルを持っています。これまでは、リンカアーキテクチャの初期化が各コンパイラのメイン関数内で直接行われていたり、特定のアーキテクチャに依存する形で散在していた可能性があります。
amd64p32
は、64ビットの命令セット(amd64
と同様)を使用しながらも、ポインタやint
/uint
型が32ビット幅であるという特殊なリンカモデルを指します。これは、特にGoogle Native Client (NaCl) のようなサンドボックス環境で、メモリ効率とセキュリティを両立させるために採用されることがありました。NaClは、ウェブブラウザ内でネイティブコードを安全に実行するための技術でしたが、現在はWebAssemblyに置き換えられ、GoにおけるNaClサポートもGo 1.14以降で廃止されています。
このコミットは、linkarchinit()
という共通の初期化関数を導入することで、リンカアーキテクチャの選択と初期化ロジックを一元化し、特にnacl/amd64p32
のような特殊なターゲットに対するサポートをよりクリーンに統合することを目的としています。これにより、コンパイラのコードベースの保守性と拡張性が向上します。
前提知識の解説
このコミットを理解するためには、以下の概念について知っておく必要があります。
- Goコンパイラ (
cmd/gc
): Go言語の公式コンパイラです。Goのソースコードを機械語に変換する主要なツールチェーンの一部です。 - アーキテクチャ固有のコンパイラ (
cmd/5g
,cmd/6g
,cmd/8g
):cmd/5g
: ARMアーキテクチャ向けのGoコンパイラバックエンド。cmd/6g
: AMD64 (x86-64) アーキテクチャ向けのGoコンパイラバックエンド。cmd/8g
: 386 (x86) アーキテクチャ向けのGoコンパイラバックエンド。 これらは、Goコンパイラのフロントエンド(cmd/gc
)が生成した中間表現を受け取り、それぞれのターゲットアーキテクチャの機械語に変換する役割を担っていました。
LinkArch
: Goコンパイラ内部で使用される構造体で、特定のリンカアーキテクチャに関する情報(例: ポインタサイズ、アライメント規則など)をカプセル化しています。コンパイラがコードを生成する際に、このLinkArch
の情報に基づいて適切な命令やデータ配置を決定します。amd64p32
:GOARCH
(Go Architecture)の一つで、64ビットの命令セット(amd64
)を使用しますが、ポインタとint
/uint
型が32ビット幅であるという特殊なモデルです。これは、64ビット環境のレジスタや命令を活用しつつ、メモリ使用量を抑えたい場合や、特定のABI(Application Binary Interface)に準拠する必要がある場合に使用されました。特にGoogle Native Client (NaCl) 環境で利用されました。- Google Native Client (NaCl): Googleが開発した、ウェブブラウザ内でネイティブコードを安全に実行するためのサンドボックス技術です。Goはかつて
GOOS=nacl
としてNaCl環境をサポートしていました。NaClは、WebAssemblyの登場によりその役割を終え、Go 1.14以降ではサポートが廃止されています。このコミットが行われた2014年時点では、NaClはまだ活発に開発・利用されていました。 getgoarch()
: Goコンパイラ内部の関数で、現在のビルドターゲットのアーキテクチャ(GOARCH
環境変数で指定される値)を取得します。strcmp()
: C言語の標準ライブラリ関数で、2つの文字列を比較します。
技術的詳細
このコミットの技術的な核心は、リンカアーキテクチャの初期化ロジックの抽象化と、amd64p32
という特殊なリンカモデルの条件付き適用にあります。
-
linkarchinit()
関数の導入:- 以前は、各アーキテクチャ固有のコンパイラ(
5g
,6g
,8g
)のgalign.c
ファイル内で、thelinkarch
というグローバル変数に適切なLinkArch
構造体へのポインタを直接割り当てていました。 - このコミットでは、
linkarchinit()
という新しいvoid
関数が導入され、この関数内でthelinkarch
の初期化を行うように変更されました。 cmd/5g/galign.c
とcmd/8g/galign.c
では、linkarchinit()
は空の関数として定義されています。これは、これらのアーキテクチャ(ARMと386)では、デフォルトのLinkArch
(linkarm
とlink386
)で十分であり、特別な条件付きロジックが不要なためです。- 一方、
cmd/6g/galign.c
では、linkarchinit()
内に特別なロジックが追加されています。
- 以前は、各アーキテクチャ固有のコンパイラ(
-
amd64p32
リンカモデルの条件付き適用 (cmd/6g
):cmd/6g
はAMD64アーキテクチャをターゲットとしますが、NaCl環境ではamd64p32
という特殊なモデルを使用する必要がありました。cmd/6g/galign.c
のlinkarchinit()
関数内で、strcmp(getgoarch(), "amd64p32") == 0
という条件チェックが追加されました。- この条件が真(つまり、
GOARCH
がamd64p32
である)の場合、thelinkarch
はデフォルトの&linkamd64
ではなく、&linkamd64p32
に設定されます。これにより、コンパイラはamd64p32
のリンカモデル(32ビットポインタなど)に従ってコードを生成するようになります。
-
cmd/gc/lex.c
におけるlinkarchinit()
の呼び出し:- Goコンパイラのメインエントリポイントである
cmd/gc/lex.c
のmain
関数内で、linkarchinit()
が呼び出されるようになりました。 - これにより、リンカアーキテクチャの初期化が、コンパイラの起動シーケンスの早い段階で、かつ一元的に行われるようになります。
- また、
GOARCH
の検証ロジックがlinkarchinit()
の呼び出しの前に移動され、fatal
エラーを発生させる条件が変更されています。以前はstrncmp
でthestring
(現在のアーキテクチャ文字列)との比較を行っていましたが、linkarchinit
の導入により、thestring
がamd64p32
のようなサフィックスを持つアーキテクチャを適切に扱えるように、GOARCH
の検証ロジックが調整されました。具体的には、GOARCH
がthestring
で始まることを確認するロジックが、linkarchinit()
の呼び出しの直前に移動し、linkarchinit()
がアーキテクチャ固有の初期化を行うようになりました。
- Goコンパイラのメインエントリポイントである
この変更により、Goコンパイラはamd64p32
のような特殊なリンカモデルを、より構造化された方法でサポートできるようになり、将来的な新しいアーキテクチャやリンカモデルの追加が容易になりました。
コアとなるコードの変更箇所
src/cmd/5g/galign.c
, src/cmd/8g/galign.c
--- a/src/cmd/5g/galign.c
+++ b/src/cmd/5g/galign.c
@@ -10,6 +10,11 @@ int thechar = '5';
char* thestring = "arm";
LinkArch* thelinkarch = &linkarm;
+void
+linkarchinit(void)
+{
+}
+
vlong MAXWIDTH = (1LL<<32) - 1;
/*
src/cmd/8g/galign.c
も同様の変更。
src/cmd/6g/galign.c
--- a/src/cmd/6g/galign.c
+++ b/src/cmd/6g/galign.c
@@ -10,6 +10,13 @@ int thechar = '6';
char* thestring = "amd64";
LinkArch* thelinkarch = &linkamd64;
+void
+linkarchinit(void)
+{
+ if(strcmp(getgoarch(), "amd64p32") == 0)
+ thelinkarch = &linkamd64p32;
+}
+
vlong MAXWIDTH = 1LL<<50;
int addptr = AADDQ;
src/cmd/gc/go.h
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -1505,6 +1505,7 @@ Prog* gjmp(Prog*);
void gused(Node*);
void movelarge(NodeList*);
int isfat(Type*);
+void linkarchinit(void);
void liveness(Node*, Prog*, Sym*, Sym*, Sym*);
void markautoused(Prog*);
Plist* newplist(void);
src/cmd/gc/lex.c
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -212,7 +212,14 @@ main(int argc, char *argv[])
#ifdef PLAN9
notify(catcher);
#endif
+ // Allow GOARCH=thestring or GOARCH=thestringsuffix,
+ // but not other values.
+ p = getgoarch();
+ if(strncmp(p, thestring, strlen(thestring)) != 0)
+ fatal("cannot use %cg with GOARCH=%s", thechar, p);
+ goarch = p;
+ linkarchinit();
ctxt = linknew(thelinkarch);
ctxt->diag = yyerror;
ctxt->bso = &bstdout;
@@ -259,13 +266,6 @@ main(int argc, char *argv[])
goroot = getgoroot();
goos = getgoos();
- // Allow GOARCH=thestring or GOARCH=thestringsuffix,
- // but not other values.
- p = getgoarch();
- if(strncmp(p, thestring, strlen(thestring)) != 0)
- fatal("cannot use %cg with GOARCH=%s", thechar, p);
- goarch = p;
-
nacl = strcmp(goos, "nacl") == 0;
if(nacl)
flag_largemodel = 1;
コアとなるコードの解説
galign.c
ファイル群の変更
linkarchinit()
関数の追加:src/cmd/5g/galign.c
,src/cmd/6g/galign.c
,src/cmd/8g/galign.c
の各ファイルにlinkarchinit()
という新しい関数が追加されました。この関数は、リンカアーキテクチャの初期化を担当します。cmd/5g
とcmd/8g
: これらのファイルでは、linkarchinit()
は空の関数として実装されています。これは、ARM (5g) と 386 (8g) アーキテクチャでは、デフォルトのリンカアーキテクチャ(linkarm
およびlink386
)で十分であり、特別な条件付きロジックが不要なためです。cmd/6g
:src/cmd/6g/galign.c
のlinkarchinit()
関数には、amd64p32
のサポートに関する重要なロジックが含まれています。
このコードは、現在のvoid linkarchinit(void) { if(strcmp(getgoarch(), "amd64p32") == 0) thelinkarch = &linkamd64p32; }
GOARCH
(Goのターゲットアーキテクチャ)が"amd64p32"
であるかどうかをstrcmp
関数でチェックします。もしそうであれば、グローバル変数thelinkarch
を&linkamd64p32
に設定します。これにより、amd64p32
リンカモデル(32ビットポインタなど)が有効になり、コンパイラはそれに応じたコードを生成するようになります。
src/cmd/gc/go.h
の変更
linkarchinit()
関数のプロトタイプ宣言が追加されました。これにより、他のファイル(特にlex.c
)からこの関数を呼び出すことができるようになります。
src/cmd/gc/lex.c
の変更
linkarchinit()
の呼び出し:main
関数内でlinkarchinit()
が呼び出されるようになりました。これにより、リンカアーキテクチャの初期化が、コンパイラの起動シーケンスの早い段階で、かつ一元的に行われるようになります。GOARCH
検証ロジックの移動と調整: 以前はmain
関数の後半にあったGOARCH
の検証ロジックが、linkarchinit()
の呼び出しの直前に移動しました。
この変更により、p = getgoarch(); if(strncmp(p, thestring, strlen(thestring)) != 0) fatal("cannot use %cg with GOARCH=%s", thechar, p); goarch = p;
linkarchinit()
が実行される前にGOARCH
が適切に設定され、検証されることが保証されます。また、コメントにあるように、GOARCH
がthestring
(例: "amd64")またはそのサフィックス(例: "amd64p32")であることを許可するようになっています。これにより、amd64p32
のような特殊なアーキテクチャ名も適切に処理できるようになりました。
これらの変更により、Goコンパイラは異なるリンカアーキテクチャ、特に amd64p32
のような特殊なケースを、よりモジュール化された方法で扱うことができるようになりました。
関連リンク
- Go Gerrit Change-ID: https://golang.org/cl/71330045
参考にした情報源リンク
- amd64p32 Go nacl: https://stackoverflow.com/questions/29999999/what-is-amd64p32-in-go
- Google Native Client (NaCl) - Wikipedia: https://en.wikipedia.org/wiki/Google_Native_Client
- Go 1.14 Release Notes (NaCl deprecation): https://go.dev/doc/go1.14#nacl
- Go 1.5 Release Notes (nacl/amd64p32 support): https://go.dev/doc/go1.5#nacl
- Go 1.13 Release Notes (nacl/amd64p32 support): https://go.dev/doc/go1.13#nacl