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

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

このコミットは、Go言語のARMアーキテクチャ向けアセンブラ(5a)とリンカ(5l)に、PLD (Preload Data) 命令のサポートを追加するものです。PLD命令は、プロセッサがメモリからデータを事前にキャッシュにロードするようヒントを与えるための命令であり、プログラムの実行性能向上に寄与します。

コミット

commit 2031252fb8182f8d1c5569034f20a952ec9e2de1
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Wed Apr 11 04:09:04 2012 +0800

    5a, 5l: add PLD (preload data) instruction
            Supported in ARMv5TE and above, excluding ARMv5TExP.
            For CL 5990066.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5999044

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

https://github.com/golang/go/commit/2031252fb8182f8d1c5569034f20a952ec9e2de1

元コミット内容

5a, 5l: add PLD (preload data) instruction
        Supported in ARMv5TE and above, excluding ARMv5TExP.
        For CL 5990066.

変更の背景

この変更の背景には、ARMアーキテクチャにおけるプログラムのパフォーマンス最適化があります。PLD命令は、CPUが将来必要とするであろうデータを事前にキャッシュに読み込むことで、メモリレイテンシを隠蔽し、結果としてプログラムの実行速度を向上させることを目的としています。特に、データアクセスパターンが予測可能なループ処理などで効果を発揮します。

Go言語は、様々なアーキテクチャをサポートしており、それぞれのアーキテクチャの特性を活かした最適化が重要です。ARMv5TE以降のARMプロセッサで利用可能なPLD命令をGoのアセンブラとリンカがサポートすることで、Goで書かれたプログラムがARMv5TE以降の環境でより効率的に動作するようになります。

前提知識の解説

ARMアーキテクチャと命令セット

ARM (Advanced RISC Machine) は、モバイルデバイスや組み込みシステムで広く利用されているRISC (Reduced Instruction Set Computer) アーキテクチャです。ARMプロセッサは、その低消費電力と高性能のバランスから、スマートフォン、タブレット、IoTデバイスなど、多岐にわたる分野で採用されています。

ARM命令セットは、Thumb、Thumb-2、ARMv8-A (AArch64) など、複数のバージョンが存在します。このコミットで言及されているARMv5TEは、ARMv5Tの拡張版であり、DSP (Digital Signal Processing) 拡張とJazelle DBX (Direct Bytecode eXecution) テクノロジーを導入しています。ARMv5TExPは、ARMv5TEから一部の機能を除外したプロファイルです。

キャッシュとプリフェッチ

現代のCPUは、メインメモリからのデータアクセス速度がCPUの処理速度に比べて非常に遅いため、キャッシュメモリを利用してこのギャップを埋めています。キャッシュは、メインメモリよりも高速な小容量のメモリであり、頻繁にアクセスされるデータを一時的に保持します。

キャッシュヒット: CPUが要求したデータがキャッシュ内に存在する場合。高速にデータにアクセスできる。 キャッシュミス: CPUが要求したデータがキャッシュ内に存在しない場合。メインメモリからデータを読み込む必要があり、性能が低下する。

プリフェッチ (Prefetching) は、CPUが将来必要となるであろうデータを予測し、事前にキャッシュに読み込んでおく技術です。これにより、データが必要になったときにすでにキャッシュに存在している可能性が高まり、キャッシュミスによる性能低下を抑えることができます。

PLD (Preload Data) 命令

PLD命令は、ARMアーキテクチャにおけるプリフェッチ命令の一つです。この命令は、指定されたアドレスのデータをキャッシュに読み込むようプロセッサにヒントを与えます。PLDはあくまで「ヒント」であり、プロセッサが必ずしもその命令に従うとは限りません。しかし、適切に利用することで、メモリレイテンシを隠蔽し、データ依存性の高い処理の性能を向上させることができます。

PLD命令の一般的な構文は以下の通りです。 PLD <address> <address>は、プリフェッチするデータのメモリ上のアドレスを指定します。

Go言語のツールチェイン

Go言語のコンパイラとリンカは、Goプログラムを様々なターゲットアーキテクチャ向けにビルドするために重要な役割を果たします。

  • 5a: ARMアー32ビットアーキテクチャ向けのアセンブラ。アセンブリコードをオブジェクトファイルに変換します。
  • 5l: ARM32ビットアーキテクチャ向けのリンカ。オブジェクトファイルを結合し、実行可能ファイルを生成します。

これらのツールは、Go言語のクロスコンパイル能力を支える基盤となっています。

技術的詳細

このコミットは、Go言語のARMアセンブラ(5a)とリンカ(5l)にPLD命令のサポートを追加するために、以下の主要な変更を行っています。

  1. アセンブラの字句解析器 (lex.c) と構文解析器 (a.y, y.tab.c, y.tab.h) の更新:

    • PLD命令を認識するための新しいトークン LTYPEPLD が追加されました。
    • lex.cPLDキーワードとそれに対応するトークンタイプ LTYPEPLD の定義が追加されました。
    • a.y (Yacc/Bisonの入力ファイル) に、PLD命令の構文規則が追加されました。これにより、アセンブラがPLD regのような形式の命令を正しく解析できるようになります。
    • y.tab.cy.tab.h は、a.y から自動生成されるファイルであり、新しいトークンと構文規則が反映されています。これらのファイルでは、LTYPEPLDの数値定義が追加され、関連するテーブル(yytranslate, yyprhs, yyrline, yytname, yytoknum, yyr1, yyr2, yydefact, yypact, yytable, yycheck, yystos)が更新されています。特に、YYLAST, YYNTOKENS, YYNRULES, YYNSTATES, YYMAXUTOKといったYacc/Bisonの内部定数が変更されており、これは新しい構文要素が追加されたことによるものです。
  2. リンカの命令定義 (5.out.h) の更新:

    • APLDという新しい命令タイプが追加されました。これは、リンカがPLD命令を内部的に識別するための定数です。
  3. リンカのアセンブリコード生成 (asm.c) の更新:

    • asm.ccase 95に、PLD命令の機械語コードを生成するロジックが追加されました。ARMのPLD命令は、通常、0xf5d0f000というベースオペコードに、プリフェッチするレジスタの情報をビットシフトしてOR演算することで生成されます。このコミットでは、p->from.reg << 16によってレジスタ番号を適切な位置に配置しています。
  4. リンカの命令テーブル (optab.c) の更新:

    • optab.c に、APLD命令のエントリが追加されました。このエントリは、PLD命令がレジスタをオペランドとして取り、機械語コードの生成時にcase 95のロジックを使用することを示しています。
  5. リンカのスパン処理 (span.c) の更新:

    • span.cbuildop関数内の命令タイプを処理するswitch文にAPLDが追加されました。これにより、リンカがPLD命令を正しく認識し、処理できるようになります。

これらの変更により、Go言語のARM向けツールチェインは、PLD命令をアセンブリコードとして受け入れ、それを適切な機械語コードに変換し、最終的な実行可能ファイルに含めることができるようになります。

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

src/cmd/5a/a.y

--- a/src/cmd/5a/a.y
+++ b/src/cmd/5a/a.y
@@ -52,7 +52,7 @@
 %token	<lval>\tLTYPE6 LTYPE7 LTYPE8 LTYPE9 LTYPEA
 %token	<lval>\tLTYPEB LTYPEC LTYPED LTYPEE LTYPEF
 %token	<lval>\tLTYPEG LTYPEH LTYPEI LTYPEJ LTYPEK
-%token	<lval>\tLTYPEL LTYPEM LTYPEN LTYPEBX
+%token	<lval>\tLTYPEL LTYPEM LTYPEN LTYPEBX LTYPEPLD
 %token	<lval>\tLCONST LSP LSB LFP LPC
 %token	<lval>\tLTYPEX LR LREG LF LFREG LC LCREG LPSR LFCR
 %token	<lval>\tLCOND LS LAT
@@ -301,6 +301,13 @@ inst:
 		$7.offset = $9;
 		outcode($1, $2, &$3, $5.reg, &$7);
 	}
+/*
+ * PLD
+ */
+|\tLTYPEPLD reg
+\t{
+\t\toutcode($1, Always, &$2, NREG, &nullgen);\
+	}
 /*
  * END
  */

src/cmd/5a/lex.c

--- a/src/cmd/5a/lex.c
+++ b/src/cmd/5a/lex.c
@@ -403,6 +403,8 @@ struct
 
 	"MCR",		LTYPEJ, 0,
 	"MRC",		LTYPEJ, 1,
+
+	"PLD",		LTYPEPLD, APLD,
 	0
 };

src/cmd/5l/5.out.h

--- a/src/cmd/5l/5.out.h
+++ b/src/cmd/5l/5.out.h
@@ -184,6 +184,8 @@ enum	as
 	ALDREXD,
 	ASTREXD,
 
+	APLD,
+
 	ALAST,
 };

src/cmd/5l/asm.c

--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -1429,6 +1429,9 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
 			break;
 		o2 = oshr(p->from.reg, 0, REGTMP, p->scond);
 		break;
+	case 95:	/* PLD reg */
+		o1 = 0xf5d0f000;
+		o1 |= p->from.reg << 16;
 	}
 	
 out[0] = o1;

src/cmd/5l/optab.c

--- a/src/cmd/5l/optab.c
+++ b/src/cmd/5l/optab.c
@@ -232,5 +232,7 @@ Optab	optab[] =
 	{ ALDREXD,	C_SOREG,C_NONE,	C_REG,		91, 4, 0 },
 	{ ASTREXD,	C_SOREG,C_REG,	C_REG,		92, 4, 0 },
 
+	{ APLD,		C_REG,	C_NONE,	C_NONE,		95, 4, 0 },
+
 	{ AXXX,		C_NONE,	C_NONE,	C_NONE,		 0, 4, 0 },
 };

src/cmd/5l/span.c

--- a/src/cmd/5l/span.c
+++ b/src/cmd/5l/span.c
@@ -843,6 +843,7 @@ buildop(void)
 		case ALDREXD:
 		case ASTREXD:
 		case ATST:
+		case APLD:
 			break;
 		}
 	}

コアとなるコードの解説

src/cmd/5a/a.y (Yacc/Bison文法定義ファイル)

このファイルは、アセンブラの構文規則を定義しています。

  • %token ... LTYPEPLDLTYPEPLDという新しいトークンを定義しています。これはPLD命令を表します。
  • inst: ... | LTYPEPLD reg { ... }inst(命令)の規則に、PLD命令の新しい形式を追加しています。これはPLDキーワードの後にレジスタが続く形式(例: PLD R0)を意味します。
  • outcode($1, Always, &$2, NREG, &nullgen);PLD命令が解析されたときに呼び出されるアクションです。outcode関数は、命令の機械語コードを生成するためのGo言語の内部関数です。
    • $1LTYPEPLDトークンの値(APLDに相当)。
    • Always:条件コード。PLDは通常無条件で実行されるため。
    • &$2reg(レジスタ)の値を参照渡し。PLD命令のオペランドとなるレジスタ情報が含まれます。
    • NREG:宛先レジスタがないことを示す。
    • &nullgen:追加のオペランドがないことを示す。

src/cmd/5a/lex.c (字句解析器)

このファイルは、アセンブリコードの文字列をトークンに分割する役割を担っています。

  • "PLD", LTYPEPLD, APLD,PLDという文字列が検出された場合に、LTYPEPLDトークンを返し、その内部値としてAPLD(リンカで定義される命令タイプ)を設定するように定義しています。

src/cmd/5l/5.out.h (リンカの出力ヘッダ)

このファイルは、リンカが内部的に使用する命令の列挙型を定義しています。

  • APLD,PLD命令を表す新しい定数APLDが追加されました。これにより、リンカはPLD命令を他のARM命令と同様に識別・処理できるようになります。

src/cmd/5l/asm.c (アセンブリコード生成)

このファイルは、Go言語の内部表現から実際の機械語コードを生成する部分です。

  • case 95: /* PLD reg */optab.cで定義されたPLD命令のオペコード番号95に対応するケースです。
  • o1 = 0xf5d0f000;:ARMのPLD命令のベースとなる機械語コードです。
  • o1 |= p->from.reg << 16;PLD命令のオペランドとして指定されたレジスタ番号(p->from.reg)を16ビット左シフトし、ベースオペコードにOR演算で組み込んでいます。これにより、最終的なPLD命令の機械語コードが完成します。

src/cmd/5l/optab.c (命令テーブル)

このファイルは、リンカがサポートする命令とその特性を定義するテーブルです。

  • { APLD, C_REG, C_NONE, C_NONE, 95, 4, 0 },APLD命令のエントリを追加しています。
    • APLD:命令タイプ。
    • C_REG:最初のオペランドがレジスタであることを示します。
    • C_NONE, C_NONE:2番目と3番目のオペランドがないことを示します。
    • 95:この命令に対応するasm.c内の処理のケース番号。
    • 4:命令のバイト長(ARM命令は通常4バイト)。
    • 0:追加情報(この場合は未使用)。

src/cmd/5l/span.c (スパン処理)

このファイルは、リンカが命令のサイズを計算し、ジャンプ命令のオフセットなどを調整する「スパン」処理に関連します。

  • case APLD:buildop関数内のswitch文にAPLDが追加されました。これにより、リンカはPLD命令を認識し、そのサイズを正しく考慮してコードレイアウトを決定できるようになります。PLD命令自体はジャンプ先を持たないため、特別な処理は不要でbreakで抜けています。

これらの変更により、Go言語のARM向けツールチェインは、PLD命令をアセンブリコードとして受け入れ、それを適切な機械語コードに変換し、最終的な実行可能ファイルに含めることができるようになります。

関連リンク

参考にした情報源リンク