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

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

このコミットは、Goコンパイラのcmd/8g(386アーキテクチャ向けコンパイラ)において、一部の欠落していた命令に対する記述を追加するものです。具体的には、GO386=387またはターゲットのi386 CPUがSSE2機能をサポートしていない場合に発行される命令に関する情報がsrc/cmd/8g/prog.cに追加されました。これにより、コンパイラがこれらの命令を正しく処理し、コード生成の精度と互換性が向上します。

コミット

commit bf58d209992395f8c30c8554b2d04dc4e4ce128f
Author: Lucio De Re <lucio.dere@gmail.com>
Date:   Thu Aug 29 14:41:01 2013 +0200

    cmd/8g: add descriptions for some missing instructions.
    
    These instructions are emitted when GO386=387 or the target
    i386 CPU does not have SSE2 capabilities.
    
    Fixes #6215.
    
    R=golang-dev, remyoudompheng
    CC=golang-dev
    https://golang.org/cl/12812045

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

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

元コミット内容

cmd/8g: いくつかの欠落していた命令の記述を追加。

これらの命令は、GO386=387が設定されている場合、またはターゲットのi386 CPUがSSE2機能をサポートしていない場合に発行されます。

Fixes #6215.

R=golang-dev, remyoudompheng CC=golang-dev https://golang.org/cl/12812045

変更の背景

Goコンパイラは、異なるCPUアーキテクチャや機能セットに対応するために、様々な命令セットをサポートしています。特に、浮動小数点演算の扱いにおいては、CPUがサポートする機能(例えばSSE2)によって生成されるアセンブリ命令が異なります。

このコミットの背景には、Goコンパイラがi386アーキテクチャ向けにコードを生成する際に、特定の条件下で発行されるアセンブリ命令(特にx87 FPU命令)に関する情報がprog.cファイルに不足していたという問題があります。

  • GO386=387: Goコンパイラにおいて、GO386環境変数は386システムでの浮動小数点呼び出し規約を制御します。GO386=387に設定すると、Goコンパイラは浮動小数点演算にx87浮動小数点ユニット(FPU)を使用するように指示されます。これは、古い32ビットx86アーキテクチャでのパフォーマンスと精度に影響を与える可能性があります。Go 1.16以降ではGO386=387のサポートは廃止され、SSE2サポートがi386ターゲットの必須要件となっていますが、このコミットが作成された2013年当時はまだ有効な設定でした。
  • i386 CPUのSSE2機能の欠如: SSE2(Streaming SIMD Extensions 2)は、Intel Pentium 4以降のプロセッサで導入されたSIMD(Single Instruction, Multiple Data)命令セットです。浮動小数点演算の高速化に寄与します。古いi386 CPUの中にはSSE2をサポートしていないものがあり、その場合、コンパイラはSSE2命令ではなく、x87 FPU命令などの代替命令を生成する必要があります。

これらの条件下でGoコンパイラが正しく動作し、適切なアセンブリ命令を生成するためには、コンパイラ内部の命令記述(prog.c内のprogtable)に、これらの命令に関する正確な情報が定義されている必要があります。このコミットは、その不足を解消し、コンパイラの堅牢性と互換性を高めることを目的としています。

前提知識の解説

Goコンパイラのcmd/8gprog.c

  • cmd/8g: これはGoの初期のコンパイラで、x86 (386) アーキテクチャ専用でした。Goのソースコードを.8オブジェクトファイルにコンパイルしていました。現在のgo tool compileコマンドが標準になる前の命名規則(8gは386、6gはamd64、5gはarmなど)の一部です。
  • src/cmd/8g/prog.c: このC言語ファイルは、Goコンパイラのバックエンドの一部であり、386アーキテクチャ向けのアセンブリ命令に関するメタデータを定義しています。具体的には、各アセンブリ命令のオペランドのサイズ、読み書きの特性、フラグへの影響などを記述したProgInfo構造体の配列progtableが含まれています。コンパイラはコード生成時にこのテーブルを参照し、命令のセマンティクスを理解します。

x86アセンブリ命令と浮動小数点演算

x86アーキテクチャには、浮動小数点演算を行うための2つの主要な方法があります。

  1. x87 FPU (Floating-Point Unit): これは、Intel 8087コプロセッサから続く伝統的な浮動小数点演算ユニットです。スタックベースのレジスタ(ST(0)からST(7))を使用して浮動小数点数を操作します。このコミットで追加された命令の多くは、このx87 FPUに関連するものです。
  2. SSE/SSE2 (Streaming SIMD Extensions): Pentium IIIでSSEが導入され、Pentium 4でSSE2が導入されました。これらはSIMD命令セットであり、複数の浮動小数点数を同時に処理できるレジスタ(XMMレジスタ)を使用します。現代のほとんどのCPUはSSE2以降をサポートしており、GoコンパイラもデフォルトでSSE2を使用します。

このコミットは、主にSSE2が利用できない環境(またはGO386=387が設定されている環境)で必要となるx87 FPU命令の記述を補完しています。

ProgInfo構造体とフラグ

src/cmd/8g/prog.c内のprogtableは、ProgInfo構造体の配列です。この構造体は、各アセンブリ命令の特性を定義するビットフラグの組み合わせで構成されています。

  • SizeD / SizeF / SizeW / SizeL / SizeB: オペランドのサイズを示します(Double, Float, Word, Long, Byte)。
  • LeftRead / RightRead: 左オペランドまたは右オペランドが読み取られることを示します。
  • LeftWrite / RightWrite: 左オペランドまたは右オペランドが書き込まれることを示します。
  • LeftRdwr / RightRdwr: 左オペランドまたは右オペランドが読み書きされることを示します。
  • LeftAddr / RightAddr: 左オペランドまたは右オペランドがアドレスであることを示します。
  • ShiftCX: シフト命令でCXレジスタが使用されることを示します。
  • SetCarry: キャリーフラグが設定されることを示します。
  • OK: 命令が有効であることを示します。
  • AX: AXレジスタが関与することを示します。

コミットで追加された(または修正された)命令

  • AFLDCW: FLDCW (Load FPU Control Word) 命令に対応。FPU制御ワードをメモリからロードします。
  • AFSTCW: FSTCW (Store FPU Control Word) 命令に対応。FPU制御ワードをメモリにストアします。
  • AFSTSW: FSTSW (Store FPU Status Word) 命令に対応。FPUステータスワードをメモリまたはAXレジスタにストアします。これはx87 FPUのステータスフラグを読み取るために使用されます。
  • AFADDD / AFADDDP / AFADDF: 浮動小数点加算命令。Dはdouble、Fはfloat、Pはpopを意味します。
  • AFCHS: FCHS (Change Sign) 命令に対応。FPUスタックトップの符号を反転させます。
  • AFDIVD / AFDIVDP / AFDIVF: 浮動小数点除算命令。
  • AFDIVRDP / AFDIVRF / AFDIVRD: 浮動小数点逆除算命令。RはReverseを意味し、オペランドの順序が逆になります。Pはpopを意味します。これらの命令は、標準のx86アセンブリ命令としては直接的な対応が見つかりにくいですが、Goコンパイラ内部での抽象化された命令名である可能性があります。例えば、FDIVRFIDIVRといった標準命令に対応するGoコンパイラ内部の表現かもしれません。
  • AFXCHD: FXCH (Exchange Register Contents) 命令に対応。FPUスタックレジスタの内容を交換します。Dはdoubleを意味する可能性がありますが、FXCHは通常オペランドサイズを指定しません。これもGoコンパイラ内部の抽象化された命令名である可能性があります。
  • AFSUBD / AFSUBDP / AFSUBF: 浮動小数点減算命令。
  • ARORL / ARORW: ビットローテート命令。
  • ASAHF: SAHF (Store AH into Flags) 命令に対応。AHレジスタの内容をEFLAGSレジスタの特定のフラグにストアします。これは古いコードや特定の仮想化シナリオでフラグを操作するために使用されます。

技術的詳細

このコミットは、src/cmd/8g/prog.cファイル内のprogtable配列に、いくつかのx87 FPU命令および関連する命令のProgInfo記述を追加または修正しています。progtableは、Goコンパイラのバックエンドがアセンブリ命令の特性を理解し、適切なコードを生成するために使用する重要なデータ構造です。

具体的には、以下の命令に対してProgInfoが追加または更新されました。

  • AFSTSW: FSTSW命令は、FPUステータスワードをメモリまたはAXレジスタにストアします。この命令は、FPUの現在の状態(例えば、演算結果のフラグ)をプログラムで検査するために重要です。追加された記述{SizeW | RightAddr | RightWrite}は、この命令がワードサイズ(SizeW)のデータを右オペランド(通常はメモリまたはレジスタ)に書き込む(RightWrite)ことを示しています。RightAddrは、右オペランドがアドレスであることを示唆しています。
  • AFDIVRDP, AFDIVRF, AFDIVRD: これらの命令は、浮動小数点逆除算に関連するものです。AFDIVRDPSizeD | LeftAddr | RightRdwrAFDIVRFSizeF | LeftAddr | RightRdwrAFDIVRDSizeD | LeftAddr | RightRdwrと記述されています。これらは、それぞれdouble、float、doubleのサイズで、左オペランドがアドレスであり、右オペランドが読み書きされることを示しています。これらの命令は、標準のx86命令セットにおけるFDIVR(浮動小数点逆除算)やそのバリアントに対応すると考えられます。
  • AFXCHD: FXCH命令は、FPUスタックレジスタの内容を交換します。追加された記述{SizeD | LeftRdwr | RightRdwr}は、doubleサイズ(SizeD)のデータが左オペランドと右オペランドの両方で読み書きされる(LeftRdwr, RightRdwr)ことを示しています。これは、FXCHが2つのFPUスタックレジスタ間で値を交換する動作を正確に反映しています。
  • ASAHF: SAHF命令は、AHレジスタの内容をEFLAGSレジスタの特定のフラグにストアします。追加された記述{OK, AX, AX}は、この命令が有効(OK)であり、AXレジスタが関与する(AX)ことを示しています。AX, AXは、オペランドがAXレジスタであることを示唆している可能性がありますが、SAHFは通常明示的なオペランドを取りません。これは、コンパイラ内部でのAXレジスタの使用方法を反映していると考えられます。

これらの記述の追加により、Goコンパイラは、GO386=387が設定されている場合や、ターゲットのi386 CPUがSSE2をサポートしていない場合に、これらのx87 FPU命令を正しく解釈し、適切なコードを生成できるようになります。これにより、コンパイラの互換性と堅牢性が向上し、より幅広い環境でGoプログラムが正しく動作するようになります。

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

src/cmd/8g/prog.cファイルにおいて、progtable配列に以下の変更が加えられました。

--- a/src/cmd/8g/prog.c
+++ b/src/cmd/8g/prog.c
@@ -95,8 +95,10 @@ static ProgInfo progtable[ALAST] = {
 	[ADIVSD]=\t{SizeD | LeftRead | RightRdwr},\
 	[ADIVSS]=\t{SizeF | LeftRead | RightRdwr},\
 	
-	[AFLDCW]=\t{SizeW | LeftAddr },
-	[AFSTCW]=\t{SizeW | RightAddr },
+	[AFLDCW]=\t{SizeW | LeftAddr},\
+	[AFSTCW]=\t{SizeW | RightAddr},\
+
+	[AFSTSW]=\t{SizeW | RightAddr | RightWrite},\
 
 	[AFADDD]=\t{SizeD | LeftAddr | RightRdwr},\
 	[AFADDDP]=\t{SizeD | LeftAddr | RightRdwr},\
@@ -111,9 +113,15 @@ static ProgInfo progtable[ALAST] = {\
 
 	[AFCHS]=\t{SizeD | RightRdwr}, // also SizeF
 
-	[AFDIVD]=\t{SizeD | LeftAddr | RightRdwr},\
 	[AFDIVDP]=\t{SizeD | LeftAddr | RightRdwr},\
 	[AFDIVF]=\t{SizeF | LeftAddr | RightRdwr},\
+	[AFDIVD]=\t{SizeD | LeftAddr | RightRdwr},\
+
+	[AFDIVRDP]=\t{SizeD | LeftAddr | RightRdwr},\
+	[AFDIVRF]=\t{SizeF | LeftAddr | RightRdwr},\
+	[AFDIVRD]=\t{SizeD | LeftAddr | RightRdwr},\
+
+	[AFXCHD]=\t{SizeD | LeftRdwr | RightRdwr},\
 
 	[AFSUBD]=\t{SizeD | LeftAddr | RightRdwr},\
 	[AFSUBDP]=\t{SizeD | LeftAddr | RightRdwr},\
@@ -235,6 +243,8 @@ static ProgInfo progtable[ALAST] = {\
 	[ARORL]=\t{SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},\
 	[ARORW]=\t{SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},\
 
+	[ASAHF]=\t{OK, AX, AX},\
+
 	[ASALB]=\t{SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},\
 	[ASALL]=\t{SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},\
 	[ASALW]=\t{SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},\

コアとなるコードの解説

変更は、progtableという静的なProgInfo構造体の配列に対するものです。この配列は、Goコンパイラが各アセンブリ命令の特性を理解するために使用します。各エントリは、命令のオペランドのサイズ、読み書きの動作、フラグへの影響などをビットフラグの組み合わせで定義しています。

具体的な変更点は以下の通りです。

  1. AFLDCWAFSTCW の修正:

    -	[AFLDCW]=\t{SizeW | LeftAddr },
    -	[AFSTCW]=\t{SizeW | RightAddr },
    +	[AFLDCW]=\t{SizeW | LeftAddr},\
    +	[AFSTCW]=\t{SizeW | RightAddr},\
    

    これは、行末のカンマの追加と、インデントの調整のみで、実質的な意味の変更はありません。

  2. AFSTSW の追加:

    +	[AFSTSW]=\t{SizeW | RightAddr | RightWrite},\
    

    AFSTSWは、FPUステータスワードをストアする命令です。

    • SizeW: オペランドがワードサイズ(16ビット)であることを示します。
    • RightAddr: 右オペランドがアドレス(メモリ位置)であることを示します。
    • RightWrite: 右オペランドにデータが書き込まれることを示します。 これにより、コンパイラはFSTSW命令がワードサイズのデータを指定されたアドレスに書き込むことを認識します。
  3. 浮動小数点逆除算命令の追加:

    -	[AFDIVD]=\t{SizeD | LeftAddr | RightRdwr},\
     	[AFDIVDP]=\t{SizeD | LeftAddr | RightRdwr},\
     	[AFDIVF]=\t{SizeF | LeftAddr | RightRdwr},\
    +	[AFDIVD]=\t{SizeD | LeftAddr | RightRdwr},\
    +
    +	[AFDIVRDP]=\t{SizeD | LeftAddr | RightRdwr},\
    +	[AFDIVRF]=\t{SizeF | LeftAddr | RightRdwr},\
    +	[AFDIVRD]=\t{SizeD | LeftAddr | RightRdwr},\
    
    • AFDIVDが重複して追加されていますが、これは元々存在していた行が削除され、同じ内容で再追加されたものです。
    • AFDIVRDP, AFDIVRF, AFDIVRDは、浮動小数点逆除算命令に対応します。
      • SizeD / SizeF: オペランドがdouble(64ビット)またはfloat(32ビット)サイズであることを示します。
      • LeftAddr: 左オペランドがアドレスであることを示します。
      • RightRdwr: 右オペランドが読み書きされることを示します。 これらの追加により、コンパイラはこれらの逆除算命令の特性を正確に把握し、適切なコード生成が可能になります。
  4. AFXCHD の追加:

    +	[AFXCHD]=\t{SizeD | LeftRdwr | RightRdwr},\
    

    AFXCHDは、FPUスタックレジスタの内容を交換する命令(FXCH)に対応します。

    • SizeD: オペランドがdoubleサイズであることを示します。
    • LeftRdwr / RightRdwr: 左オペランドと右オペランドの両方が読み書きされることを示します。 これは、FXCH命令が2つのFPUスタックレジスタ間で値を交換する動作を正確に反映しています。
  5. ASAHF の追加:

    +	[ASAHF]=\t{OK, AX, AX},\
    

    ASAHFは、AHレジスタの内容をEFLAGSレジスタの特定のフラグにストアする命令(SAHF)に対応します。

    • OK: 命令が有効であることを示します。
    • AX, AX: この命令がAXレジスタと関連していることを示します。SAHFは明示的なオペランドを取りませんが、AHレジスタ(AXレジスタの下位8ビット)を使用するため、このように記述されていると考えられます。

これらの変更は、Goコンパイラがi386アーキテクチャ、特にSSE2非対応環境やGO386=387設定下で、x87 FPU命令をより正確に処理できるようにするためのものです。これにより、コンパイラが生成するアセンブリコードの正確性と互換性が向上します。

関連リンク

参考にした情報源リンク