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

[インデックス 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という特殊なリンカモデルの条件付き適用にあります。

  1. linkarchinit()関数の導入:

    • 以前は、各アーキテクチャ固有のコンパイラ(5g, 6g, 8g)のgalign.cファイル内で、thelinkarchというグローバル変数に適切なLinkArch構造体へのポインタを直接割り当てていました。
    • このコミットでは、linkarchinit()という新しいvoid関数が導入され、この関数内でthelinkarchの初期化を行うように変更されました。
    • cmd/5g/galign.ccmd/8g/galign.cでは、linkarchinit()は空の関数として定義されています。これは、これらのアーキテクチャ(ARMと386)では、デフォルトのLinkArchlinkarmlink386)で十分であり、特別な条件付きロジックが不要なためです。
    • 一方、cmd/6g/galign.cでは、linkarchinit()内に特別なロジックが追加されています。
  2. amd64p32リンカモデルの条件付き適用 (cmd/6g):

    • cmd/6gはAMD64アーキテクチャをターゲットとしますが、NaCl環境ではamd64p32という特殊なモデルを使用する必要がありました。
    • cmd/6g/galign.clinkarchinit()関数内で、strcmp(getgoarch(), "amd64p32") == 0という条件チェックが追加されました。
    • この条件が真(つまり、GOARCHamd64p32である)の場合、thelinkarchはデフォルトの&linkamd64ではなく、&linkamd64p32に設定されます。これにより、コンパイラはamd64p32のリンカモデル(32ビットポインタなど)に従ってコードを生成するようになります。
  3. cmd/gc/lex.cにおけるlinkarchinit()の呼び出し:

    • Goコンパイラのメインエントリポイントであるcmd/gc/lex.cmain関数内で、linkarchinit()が呼び出されるようになりました。
    • これにより、リンカアーキテクチャの初期化が、コンパイラの起動シーケンスの早い段階で、かつ一元的に行われるようになります。
    • また、GOARCHの検証ロジックがlinkarchinit()の呼び出しの前に移動され、fatalエラーを発生させる条件が変更されています。以前はstrncmpthestring(現在のアーキテクチャ文字列)との比較を行っていましたが、linkarchinitの導入により、thestringamd64p32のようなサフィックスを持つアーキテクチャを適切に扱えるように、GOARCHの検証ロジックが調整されました。具体的には、GOARCHthestringで始まることを確認するロジックが、linkarchinit()の呼び出しの直前に移動し、linkarchinit()がアーキテクチャ固有の初期化を行うようになりました。

この変更により、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/5gcmd/8g: これらのファイルでは、linkarchinit() は空の関数として実装されています。これは、ARM (5g) と 386 (8g) アーキテクチャでは、デフォルトのリンカアーキテクチャ(linkarm および link386)で十分であり、特別な条件付きロジックが不要なためです。
  • cmd/6g: src/cmd/6g/galign.clinkarchinit() 関数には、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 が適切に設定され、検証されることが保証されます。また、コメントにあるように、GOARCHthestring(例: "amd64")またはそのサフィックス(例: "amd64p32")であることを許可するようになっています。これにより、amd64p32 のような特殊なアーキテクチャ名も適切に処理できるようになりました。

これらの変更により、Goコンパイラは異なるリンカアーキテクチャ、特に amd64p32 のような特殊なケースを、よりモジュール化された方法で扱うことができるようになりました。

関連リンク

参考にした情報源リンク