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

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

このコミットは、Goコンパイラのcmd/cccmd/5ccmd/6ccmd/8cツールチェーンにlinkarchinit関数を導入し、特にnacl/amd64p32アーキテクチャのサポートを追加するものです。これにより、Goコンパイラが特定の環境(Google Native Clientのamd64p32)向けにビルドされる際に、適切なリンカモデル(amd64p32)を動的に選択できるようになります。

コミット

  • コミットハッシュ: d4896fb8766f8b7ac37ba421fbfbffb0f56ee8d4
  • Author: Dave Cheney dave@cheney.net
  • Date: Fri Mar 7 09:55:59 2014 +1100

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/d4896fb8766f8b7ac37ba421fbfbffb0f56ee8d4

元コミット内容

cmd/cc, cmd/5c, cmd/6c, cmd/8c: introduce linkarchinit and add amd64p32 support

Replaces CL 70000043.

Introduce linkarchinit() from cmd/ld.

For cmd/6c, switch to the amd64p32 linker model if we are building under nacl/amd64p32.

LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/72010043

変更の背景

このコミットの主な背景は、Go言語がGoogle Native Client (NaCl) プラットフォームをサポートする取り組みの一環として、amd64p32という特定のアーキテクチャに対応する必要があったことです。

Goコンパイラは、異なるターゲットアーキテクチャ(例: amd64, 386)やオペレーティングシステム(例: linux, windows, nacl)向けにコードを生成します。それぞれの組み合わせには、特定のリンカモデルやデータ構造のサイズ(ポインタのサイズなど)が関連付けられています。

nacl/amd64p32は、64ビットの命令セットを使用しながらも、ポインタやint/uint型が32ビットであるという特殊なモデルです。これは、64ビットのレジスタや演算能力を活用しつつ、メモリフットプリントを削減したい場合に有効でした。このコミット以前は、Goコンパイラがnacl/amd64p32環境でビルドされる際に、適切なリンカモデルを動的に選択するメカニズムが不足していました。

linkarchinit関数の導入は、コンパイラの初期化段階で現在のターゲットアーキテクチャ(GOARCH環境変数で指定される)をチェックし、必要に応じてamd64p32リンカモデルに切り替えるための汎用的な方法を提供します。これにより、Goコンパイラがnacl/amd64p32環境で正しく動作し、効率的なコードを生成できるようになります。

なお、Go言語におけるNaClサポートは、Go 1.14以降で廃止されています。これは、NaCl自体の利用が減少したことや、WebAssemblyなどの新しい技術が登場したことによるものです。しかし、このコミットは当時のGo言語のクロスコンパイル戦略と、特定のニッチな環境への対応を示す重要な歴史的変更点と言えます。

前提知識の解説

このコミットを理解するためには、以下の概念について知っておく必要があります。

  1. Goコンパイラのツールチェーン (cmd/cc, cmd/5c, cmd/6c, cmd/8c):

    • Go言語の初期のコンパイラは、Plan 9 Cコンパイラをベースにしていました。
    • cmd/ccはC言語のフロントエンドで、GoのコンパイラがCコードを処理する際に使用されました。
    • cmd/5cはARMアーキテクチャ(GOARCH=arm)向けのCコンパイラです。
    • cmd/6cはAMD64アーキテクチャ(GOARCH=amd64)向けのCコンパイラです。
    • cmd/8cは386アーキテクチャ(GOARCH=386)向けのCコンパイラです。
    • これらのツールは、Goのソースコードをアセンブリに変換する過程で重要な役割を果たしていました。
  2. Google Native Client (NaCl):

    • Googleが開発したサンドボックス技術で、ウェブブラウザ内でコンパイルされたC/C++コードを安全かつ高速に実行することを目的としていました。
    • ウェブアプリケーションがネイティブコードを実行できるようにすることで、パフォーマンスと機能の向上を目指しました。
    • Go言語は、かつてNaClプラットフォームへの実験的なサポートを提供していました。
  3. amd64p32アーキテクチャ:

    • amd64p32は、NaCl環境でGoがサポートしていた特定のアーキテクチャの一つです。
    • amd64は64ビットのx86命令セットを指します。
    • p32は「32ビットポインタ」を意味します。つまり、64ビットの命令セットを使用するものの、アドレス空間は4GBに制限され、ポインタやint/uint型は32ビットサイズになります。
    • このモデルは、64ビットのレジスタと演算能力の恩恵を受けつつ、ポインタサイズが小さくなることでメモリ使用量を削減できるという利点がありました。
  4. LinkArch構造体:

    • Goコンパイラの内部で、特定のアーキテクチャのリンカに関する情報(例: ポインタサイズ、アラインメント要件など)をカプセル化するために使用される構造体です。
    • linkamd64は標準のAMD64アーキテクチャのリンカ設定を、linkamd64p32amd64p32アーキテクチャのリンカ設定をそれぞれ表します。
  5. getgoarch()関数:

    • Goのビルドシステムが設定するGOARCH環境変数の値を取得する関数です。これにより、現在のターゲットアーキテクチャをプログラム的に判別できます。

技術的詳細

このコミットの技術的な核心は、Goコンパイラの初期化プロセスにlinkarchinit()という新しい関数を導入し、これを通じてnacl/amd64p32という特殊なリンカモデルを動的に選択できるようにした点にあります。

以前のGoコンパイラでは、thelinkarchというグローバル変数が、コンパイルターゲットのアーキテクチャに応じたLinkArch構造体へのポインタとして静的に初期化されていました(例: src/cmd/6c/txt.cではlinkamd64src/cmd/8c/txt.cではlink386)。しかし、amd64p32のような、同じamd64命令セットを使用しながらもポインタサイズが異なるような特殊なケースに対応するためには、より柔軟なメカニズムが必要でした。

linkarchinit()関数は、コンパイラのmain関数内でcinit()(コンパイラの初期化処理)の前に呼び出されるように設計されています。この関数は、getgoarch()を呼び出して現在のGOARCH環境変数の値を取得し、それが"amd64p32"であるかどうかをチェックします。もし"amd64p32"であれば、thelinkarchグローバル変数を&linkamd64p32に設定し直します。これにより、以降のコンパイルおよびリンク処理でamd64p32の特性(特に32ビットポインタ)が正しく考慮されるようになります。

また、src/cmd/cc/lex.cmain関数内では、amd64p32の場合にewidth[TIND](ポインタの幅)を4バイトに設定する既存のロジックが、getgoarch()の戻り値ではなく、より汎用的なp変数(getgoarch()の結果を格納)を使用するように変更されています。これは、コードの一貫性を保ち、linkarchinit()が設定するthelinkarchと連携して、ポインタサイズに関する設定が正しく行われるようにするためです。

src/cmd/8c/txt.c(386コンパイラ)では、linkarchinit()が導入されていますが、その実装は空です。これは、amd64p32が386アーキテクチャとは直接関係ないため、386コンパイラでは特別なリンカモデルの切り替えが不要であることを示しています。しかし、将来的な拡張性やコードの統一性のために、関数自体は定義されています。

この変更により、Goコンパイラは、ビルド環境のGOARCH設定に基づいて、適切なリンカモデルを動的に選択できるようになり、nacl/amd64p32のような特殊なターゲット環境への対応が強化されました。

コアとなるコードの変更箇所

diff --git a/src/cmd/6c/txt.c b/src/cmd/6c/txt.c
index 61b333c1cb..f308aff08c 100644
--- a/src/cmd/6c/txt.c
+++ b/src/cmd/6c/txt.c
@@ -30,11 +30,18 @@
 
 #include "gc.h"
 
-LinkArch	*thelinkarch = &linkamd64;
-
 int thechar = '6';
 char *thestring = "amd64";
 
+LinkArch	*thelinkarch = &linkamd64;
+
+void
+linkarchinit(void)
+{
+	if(strcmp(getgoarch(), "amd64p32") == 0)
+		thelinkarch = &linkamd64p32;
+}
+
 void
 ginit(void)
 {
diff --git a/src/cmd/8c/txt.c b/src/cmd/8c/txt.c
index 8a38955459..1b6c2e6d96 100644
--- a/src/cmd/8c/txt.c
+++ b/src/cmd/8c/txt.c
@@ -30,11 +30,17 @@
 
 #include "gc.h"
 
-LinkArch	*thelinkarch = &link386;
-
 int thechar = '8';
 char *thestring = "386";
 
+LinkArch	*thelinkarch = &link386;
+
+void
+linkarchinit(void)
+{
+}
+
 void
 ginit(void)
 {
diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h
index 726364ce94..c8aac12530 100644
--- a/src/cmd/cc/cc.h
+++ b/src/cmd/cc/cc.h
@@ -795,6 +795,7 @@ int32	exreg(Type*);
 int32	align(int32, Type*, int, int32*);
 int32	maxround(int32, int32);
 int	hasdotdotdot(void);
+void    linkarchinit(void);
 
 extern	schar	ewidth[];
 
diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c
index f6025d2807..a8ece212f0 100644
--- a/src/cmd/cc/lex.c
+++ b/src/cmd/cc/lex.c
@@ -124,7 +124,7 @@ main(int argc, char *argv[])
 	p = getgoarch();
 	if(strncmp(p, thestring, strlen(thestring)) != 0)
 		sysfatal("cannot use %cc with GOARCH=%s", thechar, p);
-	if(strcmp(getgoarch(), "amd64p32") == 0) // must be before cinit
+	if(strcmp(p, "amd64p32") == 0) // must be before cinit
 		ewidth[TIND] = 4;
 		
 	nacl = strcmp(getgoos(), "nacl") == 0;
@@ -133,6 +133,7 @@ main(int argc, char *argv[])
 
 	quotefmtinstall(); // before cinit, which overrides %Q
 
+	linkarchinit();
 	ctxt = linknew(thelinkarch);\n
 	ctxt->diag = yyerror;\n
 	ctxt->bso = &bstdout;\n

コアとなるコードの解説

このコミットでは、主に以下のファイルが変更されています。

  1. src/cmd/6c/txt.c:

    • LinkArch *thelinkarch = &linkamd64; の宣言が、linkarchinit関数の定義の前に移動されました。これは、linkarchinitthelinkarchを変更できるようにするためです。
    • linkarchinit()関数が新しく追加されました。この関数は、getgoarch()で現在のターゲットアーキテクチャを取得し、それが"amd64p32"である場合に、グローバル変数thelinkarch&linkamd64p32に設定します。これにより、amd64p32環境でのコンパイル時に適切なリンカモデルが選択されるようになります。
  2. src/cmd/8c/txt.c:

    • src/cmd/6c/txt.cと同様に、LinkArch *thelinkarch = &link386; の宣言が移動されました。
    • linkarchinit()関数が追加されましたが、その実装は空です。これは、amd64p32が386アーキテクチャとは直接関係ないため、386コンパイラでは特別なリンカモデルの切り替えが不要であることを示しています。しかし、関数シグネチャを統一し、将来的な拡張性を考慮して定義されています。
  3. src/cmd/cc/cc.h:

    • linkarchinit()関数のプロトタイプ宣言void linkarchinit(void);が追加されました。これにより、他のコンパイラソースファイルからこの関数を呼び出すことができるようになります。
  4. src/cmd/cc/lex.c:

    • main関数内で、ewidth[TIND] = 4;を設定する条件がstrcmp(getgoarch(), "amd64p32") == 0からstrcmp(p, "amd64p32") == 0に変更されました。ここでpgetgoarch()の戻り値を格納するローカル変数です。これは機能的な変更ではなく、コードの一貫性を高めるためのリファクタリングです。
    • 最も重要な変更は、linkarchinit();の呼び出しがmain関数内に追加されたことです。この呼び出しは、ctxt = linknew(thelinkarch);(リンカコンテキストの初期化)の直前に行われます。これにより、リンカコンテキストが作成される前にthelinkarchが正しく設定され、amd64p32環境でのポインタサイズなどの特性が適切に反映されることが保証されます。

これらの変更により、Goコンパイラは、nacl/amd64p32のような特殊なターゲットアーキテクチャに対して、より正確で効率的なコードを生成できるようになりました。

関連リンク

参考にした情報源リンク