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

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

このコミットは、Go言語のリンカである cmd/6loptab.c ファイルに対する変更です。optab.c は、Goコンパイラおよびリンカが使用する命令のオペランド型定義テーブルを格納しています。具体的には、各命令がどのような種類のオペランド(レジスタ、メモリ、即値など)を取ることができるかを定義しています。

コミット

commit 26d43a0f22e4d19350bd5243253ce76018651861
Author: Russ Cox <rsc@golang.org>
Date:   Wed Jun 5 10:38:52 2013 -0400

    cmd/6l: accept NOP of $x+10(SP) and of X0
    
    Needed to link code compiled with 6c -N.
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/10043044

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

https://github.com/golang/go/commit/26d43a0f22e4d19350bd5243253ce76018651861

元コミット内容

cmd/6l: $x+10(SP) および X0NOP を受け入れるようにする。 6c -N でコンパイルされたコードをリンクするために必要。

変更の背景

この変更の背景には、Goコンパイラ 6c の特定のコンパイルオプション -N が関係しています。6c -N オプションは、Goの標準ライブラリの一部をコンパイルする際に使用されることがあり、通常はデバッグ情報や最適化を抑制するために使われます。このオプションが有効な場合、コンパイラは特定の形式の NOP (No Operation) 命令を生成することがありました。

しかし、当時のリンカ 6l は、これらの特定の形式の NOP 命令(具体的には、スタックポインタからのオフセットを持つアドレス指定 $x+10(SP) や、特定のレジスタ X0 をオペランドとする NOP)を正しく認識し、処理することができませんでした。その結果、6c -N でコンパイルされたコードをリンクしようとすると、リンカエラーが発生し、実行可能ファイルを生成できない問題がありました。

このコミットは、リンカ 6l がこれらの新しい NOP オペランドの形式を認識し、適切に処理できるようにすることで、6c -N でコンパイルされたコードのリンクを可能にすることを目的としています。

前提知識の解説

Goコンパイラ (6c) とリンカ (6l)

  • 6c: Go言語のソースコードをアセンブリコード(またはオブジェクトファイル)にコンパイルするツールです。Goのツールチェーンの一部であり、go build コマンドの内部で利用されます。6c6 は、当時主流だったAMD64アーキテクチャ(x86-64)を指します。
  • 6l: 6c によって生成されたオブジェクトファイルや、他のライブラリを結合して実行可能なバイナリを生成するリンカです。リンカは、異なるオブジェクトファイル間の参照を解決し、最終的な実行可能ファイルを構築します。

NOP (No Operation) 命令

NOP は「No Operation」の略で、CPUに対して何もしないことを指示する命令です。通常、プログラムの実行フローには影響を与えませんが、以下のような目的で使用されます。

  • アライメント: コードのアライメントを調整し、パフォーマンスを向上させるため。
  • パディング: 命令のサイズを調整するため。
  • デバッグ: ブレークポイントを設定したり、コードの実行を一時的に停止させたりするため。
  • コードの置き換え: 将来的に命令を挿入するためのプレースホルダーとして。

アセンブリ表記 $x+10(SP)X0

Goのアセンブリ言語は、Plan 9アセンブラの構文に基づいています。

  • SP (Stack Pointer): スタックの現在のトップを指すレジスタです。
  • $x+10(SP): これは「スタックポインタ SP が指すアドレスから x+10 バイトオフセットしたメモリ位置」を意味します。$ は即値ではなく、アドレス指定であることを示唆しています。Goのアセンブリでは、$offset(SP) の形式でスタック上のローカル変数や引数にアクセスします。この場合、x はシンボルまたは定数で、10 はオフセットです。
  • X0: これは特定のレジスタを指します。Goのアセンブリでは、AX, BX, CX, DX などの汎用レジスタの他に、X0, X1 などの浮動小数点レジスタやSIMDレジスタが使用されることがあります。この文脈では、X0 は特定のレジスタ(おそらく浮動小数点レジスタまたはSIMDレジスタ)を指していると考えられます。

optab.c とオペランド型定義

optab.c ファイルは、Goのコンパイラおよびリンカが命令を処理するために必要なオペランドの型定義テーブルを含んでいます。このテーブルは、各命令がどのような種類のオペランド(レジスタ、メモリ、即値など)を取ることができるかをリンカに伝えます。

  • ynop テーブル: NOP 命令のオペランドの組み合わせを定義するテーブルです。
  • Ynone: オペランドがないことを示します。
  • Yml: メモリ参照(Memory Location)オペランドを示します。
  • Yrf: 汎用レジスタ(Register File)オペランドを示します。
  • Yiauto: スタック上の自動変数($offset(SP) の形式)を示すオペランドです。
  • Yxr: 拡張レジスタ(eXtended Register)、例えば浮動小数点レジスタやSIMDレジスタを示すオペランドです。
  • Zpseudo: 疑似命令(リンカが特別に処理する命令)であることを示します。
  • 最後の数値 (0または1): これは、そのオペランドの組み合わせが命令のサイズに影響を与えるかどうかを示すフラグである可能性があります。0 はサイズに影響しない、1 は影響する、といった意味合いが考えられます。

技術的詳細

このコミットの技術的な核心は、リンカ 6lNOP 命令の新しいオペランド形式を正しく解釈できるように、optab.c 内の ynop テーブルを拡張することにあります。

Goのコンパイラ 6c は、特定の状況下(特に -N オプションが有効な場合)で、NOP 命令にスタックオフセット付きのアドレス指定($x+10(SP))や、特定のレジスタ(X0)をオペランドとして付加した形式を生成するようになりました。

リンカは、オブジェクトファイルを読み込む際に、各命令とそのオペランドの型を認識し、それに基づいて命令のサイズを計算したり、アドレスを解決したりします。optab.cynop テーブルは、NOP 命令がどのようなオペランドの組み合わせを許容するかをリンカに指示します。

変更前は、ynop テーブルには Yiauto (スタック上の自動変数) や Yxr (拡張レジスタ) をオペランドとする NOP のエントリがありませんでした。そのため、6c -N が生成するこれらの形式の NOP 命令に遭遇すると、リンカはそれを未知の命令形式と判断し、エラーを報告していました。

このコミットでは、ynop テーブルに以下の新しいエントリが追加されました。

  • Ynone, Yiauto, Zpseudo, 0, : オペランドなしとスタック上の自動変数 ($x+10(SP) に対応) の組み合わせ。
  • Ynone, Yxr, Zpseudo, 0, : オペランドなしと拡張レジスタ (X0 に対応) の組み合わせ。
  • Yiauto, Ynone, Zpseudo, 0, : スタック上の自動変数とオペランドなしの組み合わせ。
  • Yxr, Ynone, Zpseudo, 1, : 拡張レジスタとオペランドなしの組み合わせ。

これらのエントリを追加することで、リンカは $x+10(SP)X0 をオペランドとする NOP 命令を、有効な命令形式として認識できるようになります。これにより、6c -N でコンパイルされたコードがリンカによって正常に処理され、実行可能ファイルが生成されるようになります。

特に注目すべきは、Yxr, Ynone, Zpseudo, 1, の最後の値が 1 である点です。これは、この特定の NOP 形式が命令のサイズに影響を与える可能性があることを示唆しています。他の新しいエントリが 0 であることから、この Yxr を含む NOP は、他の NOP とは異なるサイズを持つ可能性があるか、またはリンカが特別な処理を行う必要があることを示しています。

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

src/cmd/6l/optab.c ファイルの ynop 配列が変更されています。

--- a/src/cmd/6l/optab.c
+++ b/src/cmd/6l/optab.c
@@ -42,11 +42,15 @@ uchar	ytext[] =
 };
 uchar	ynop[] =
 {
-	Ynone,	Ynone,	Zpseudo,1,
-	Ynone,	Yml,\tZpseudo,1,
-	Ynone,	Yrf,\tZpseudo,1,
-	Yml,	Ynone,\tZpseudo,1,
-	Yrf,	Ynone,\tZpseudo,1,
+	Ynone,	Ynone,	Zpseudo,0,
+	Ynone,	Yiauto,	Zpseudo,0,
+	Ynone,	Yml,	Zpseudo,0,
+	Ynone,	Yrf,	Zpseudo,0,
+	Ynone,	Yxr,	Zpseudo,0,
+	Yiauto,	Ynone,	Zpseudo,0,
+	Yml,	Ynone,	Zpseudo,0,
+	Yrf,	Ynone,	Zpseudo,0,
+	Yxr,	Ynone,	Zpseudo,1,
 	0
 };
 uchar	yxorb[] =

コアとなるコードの解説

変更は ynop 配列内で行われています。この配列は、NOP 命令が取りうるオペランドの組み合わせを定義しています。各行は4つの要素からなるタプルで、それぞれ「ソースオペランドの型」「デスティネーションオペランドの型」「命令の種類(疑似命令など)」「フラグ」を表しています。

元のコードでは、NOP は主に Ynone (オペランドなし)、Yml (メモリ参照)、Yrf (汎用レジスタ) の組み合わせしかサポートしていませんでした。

追加された行は以下の通りです。

  1. Ynone, Yiauto, Zpseudo, 0,
    • これは、ソースオペランドなし (Ynone) で、デスティネーションオペランドがスタック上の自動変数 (Yiauto、つまり $x+10(SP) の形式) である NOP をリンカが認識できるようにします。
  2. Ynone, Yxr, Zpseudo, 0,
    • これは、ソースオペランドなし (Ynone) で、デスティネーションオペランドが拡張レジスタ (Yxr、つまり X0 の形式) である NOP をリンカが認識できるようにします。
  3. Yiauto, Ynone, Zpseudo, 0,
    • これは、ソースオペランドがスタック上の自動変数 (Yiauto) で、デスティネーションオペランドなし (Ynone) である NOP をリンカが認識できるようにします。
  4. Yxr, Ynone, Zpseudo, 1,
    • これは、ソースオペランドが拡張レジスタ (Yxr) で、デスティネーションオペランドなし (Ynone) である NOP をリンカが認識できるようにします。最後の 1 は、この特定の組み合わせが命令のサイズに影響を与える可能性があることを示唆しています。

また、既存のいくつかのエントリの最後のフラグが 1 から 0 に変更されています。これは、これらの NOP 形式が命令のサイズに影響を与えないことを明示している可能性があります。

これらの変更により、6c -N コンパイラが生成する特定の NOP 命令形式がリンカによって正しく解釈され、リンクプロセスが正常に完了するようになります。

関連リンク

参考にした情報源リンク

  • Go Assembly Language (Plan 9 style): https://go.dev/doc/asm
  • Go Toolchain Documentation (for 6c, 6l context): https://go.dev/doc/cmd
  • Understanding NOP instructions: General computer architecture knowledge.
  • Stack Pointer (SP) and addressing modes: General assembly language knowledge.
  • Go source code for cmd/6l/optab.c (for context on Ynone, Yml, Yrf, Zpseudo etc.): https://github.com/golang/go/blob/master/src/cmd/6l/optab.c (Note: This link points to the current master, not the exact historical version, but provides general context.)
  • Discussion on 6c -N (if available, often found in Go issue tracker or mailing lists).