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

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

このコミットは、Go言語のツールチェイン(特にcmd/6acmd/8acmd/6lcmd/8l)にAES(Advanced Encryption Standard)関連の命令を追加するものです。これにより、AESハッシュ処理におけるパフォーマンス向上が期待されます。具体的には、アセンブラとリンカが新しいAES命令を認識し、適切に処理できるようになります。

コミット

  • コミットハッシュ: 297bb12809c81810f3c7fbb7794be124b76d291a
  • Author: Keith Randall khr@golang.org
  • Date: Thu Mar 7 12:54:00 2013 -0800

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

https://github.com/golang/go/commit/297bb12809c81810f3c7fbb7794be124b76d291a

元コミット内容

cmd/6a, cmd/8a, cmd/6l, cmd/8l: add AES instructions

Instructions for use in AES hashing. See CL#7543043

R=rsc
CC=golang-dev
https://golang.org/cl/7548043

変更の背景

この変更の主な背景は、Go言語でAESハッシュ処理を行う際のパフォーマンスを向上させることです。当時のGo言語の標準ライブラリやアプリケーションがAES処理を行う際、ソフトウェア実装に依存している場合、ハードウェアが提供するAES命令セットを活用することで大幅な高速化が見込めます。IntelおよびAMDの最新のCPUには、AES暗号化・復号化をハードウェアレベルでサポートする専用の命令セット(AES-NI: Advanced Encryption Standard New Instructions)が搭載されています。これらの命令をGoのツールチェインが認識し、利用できるようにすることで、暗号処理の効率が向上し、全体的なアプリケーションのパフォーマンスが改善されます。

コミットメッセージに記載されているCL#7543043は、この変更に関連するGoの変更リスト(Change List)を示しており、通常、より詳細な議論や背景情報が含まれています。

前提知識の解説

Go言語のツールチェイン

Go言語のビルドプロセスは、主に以下のツールで構成されます。

  • cmd/6a (または cmd/8a, cmd/5a など): アセンブラ。Goのアセンブリ言語(Plan 9アセンブリ)で書かれたソースコードをオブジェクトファイルに変換します。6aはAMD64(x86-64)アーキテクチャ用、8aはx86(32-bit)アーキテクチャ用です。
  • cmd/6l (または cmd/8l, cmd/5l など): リンカ。アセンブラによって生成されたオブジェクトファイルやGoのコンパイラが生成したオブジェクトファイルを結合し、実行可能ファイルを生成します。6lはAMD64用、8lはx86用です。
  • cmd/6g (または cmd/8g, cmd/5g など): コンパイラ。Go言語のソースコードをオブジェクトファイルに変換します。

これらのツールは、Goのソースコードを最終的な実行可能ファイルに変換する上で不可欠な役割を担っています。

x86/x86-64アセンブリ言語と命令セット

x86およびx86-64は、IntelおよびAMDのCPUで広く使用されている命令セットアーキテクチャです。アセンブリ言語は、これらのCPUが直接実行できる機械語命令を人間が読める形式で表現したものです。

AES-NI (Advanced Encryption Standard New Instructions)

AES-NIは、Intelが開発し、その後のAMDプロセッサにも採用された、AES暗号化アルゴリズムを高速化するための命令セット拡張です。これらの命令は、AESの各ラウンドで必要な複雑な計算(ShiftRows, SubBytes, MixColumns, AddRoundKeyなど)を単一のCPUサイクルで実行できるように設計されており、ソフトウェア実装に比べて大幅なパフォーマンス向上を実現します。

このコミットで追加される主なAES命令には以下のようなものがあります。

  • AESENC: AES暗号化の1ラウンドを実行します。
  • PINSRD / PINSRQ: 汎用レジスタまたはメモリからXMMレジスタの指定された位置に32ビット(Dword)または64ビット(Qword)のデータを挿入します。これは、AES処理でデータをXMMレジスタにロードする際に使用されます。
  • PSHUFB: バイト単位のシャッフル操作を実行します。これは、AESのSubBytesステップやその他のデータ操作で利用されることがあります。

Bison (Yacc)

src/cmd/8a/a.ysrc/cmd/8a/y.tab.cは、Bison(GNU版Yacc)というパーサジェネレータによって生成されたファイルです。a.yはアセンブラの文法定義ファイル(Yaccの入力ファイル)であり、y.tab.cはその文法定義から生成されたC言語のソースファイルです。アセンブラがアセンブリコードを解析する際に、このパーサが使用されます。新しい命令を追加するには、この文法定義を更新し、パーサを再生成する必要があります。

技術的詳細

このコミットは、Go言語のAMD64(cmd/6a, cmd/6l)およびx86(cmd/8a, cmd/8l)ツールチェインにAES命令のサポートを追加します。変更は主にアセンブラ(6a, 8a)とリンカ(6l, 8l)の以下の部分に及びます。

  1. アセンブラの字句解析器 (lex.c):

    • 新しいAES命令(PINSRD, PINSRQ, PSHUFB, AESENCなど)のニーモニック(命令名)を認識するように、字句解析器のテーブルが更新されます。これにより、アセンブラがこれらの命令を正しくトークン化できるようになります。
    • 例: src/cmd/6a/lex.c および src/cmd/8a/lex.cPINSRD, PINSRQ, PSHUFB, AESENC, CPUID, MOVQ などのエントリが追加されています。
  2. アセンブラの構文解析器 (a.y, y.tab.c):

    • src/cmd/8a/a.y (Yaccの文法定義ファイル) が更新され、新しい命令のオペランドの組み合わせを定義するルールが追加されます。特に、PINSRDのような命令は即値、メモリ、レジスタの組み合わせを取るため、それに対応するspec10という新しいルールが追加されています。
    • src/cmd/8a/y.tab.c は、a.yの変更に伴ってBisonによって再生成されたファイルです。このファイルには、新しいトークンタイプ(LTYPEXなど)や、新しい文法ルールに対応するC言語のコードが含まれています。変更点には、Bisonのバージョン情報や内部テーブル(yytranslate, yyprhs, yyrhs, yyrline, yytname, yytoknum, yyr1, yyr2, yydefact, yypact, yypgoto, yytable, yycheck, yystosなど)の更新が含まれます。これらのテーブルは、パーサが入力アセンブリコードを解析する際の状態遷移やルール適用を制御します。
  3. リンカの命令定義 (optab.c):

    • src/cmd/6l/optab.c および src/cmd/8l/optab.c は、リンカが各命令をどのようにエンコードするかを定義するテーブルです。新しいAES命令に対応するエントリが追加され、それぞれの命令のオペランドタイプ、プレフィックス(Pe, Pm, Pq, Pf2, Pf3, Pq3など)、オペコードバイトが定義されます。
    • 例えば、APINSRDAPINSRQAPSHUFBなどの命令が追加され、それらの命令が使用するオペランドのパターン(ymshufb, yinsrなど)も定義されています。
  4. リンカのヘッダファイル (l.h, 6.out.h, 8.out.h):

    • src/cmd/6l/l.h および src/cmd/8l/l.h は、リンカ内部で使用される定数や構造体の定義が含まれるヘッダファイルです。新しいオペランドタイプ(Zm2_rなど)や、命令のエンコーディングに関連するプレフィックスの定義(Pq, Pf2, Pf3のコメント更新、Pq3の追加)が更新されています。
    • src/cmd/6l/6.out.h および src/cmd/8l/8.out.h は、Goのアセンブラとリンカが共通で使用する命令コードの列挙型定義です。新しいAES命令に対応する列挙型メンバー(APINSRD, APINSRQ, APSHUFB, AAESENCなど)が追加されています。
  5. リンカのスパン処理 (span.c):

    • src/cmd/6l/span.c および src/cmd/8l/span.c は、リンカが命令の長さを計算し、ジャンプ命令のオフセットを調整する「スパン処理」に関連するファイルです。新しい命令プレフィックス(Pq3)の処理が追加され、命令のエンコーディングロジックが更新されています。

これらの変更により、GoのアセンブラはAES命令を含むアセンブリコードを正しく解析し、リンカはそれらを適切な機械語に変換して実行可能ファイルを生成できるようになります。これにより、GoプログラムからハードウェアAES命令を直接利用することが可能となり、暗号処理のパフォーマンスが向上します。

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

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

  • src/cmd/6a/lex.c: AMD64アセンブラの字句解析器。新しい命令のニーモニックを追加。
  • src/cmd/6l/6.out.h: AMD64リンカの命令コード定義。新しい命令の列挙型を追加。
  • src/cmd/6l/l.h: AMD64リンカの共通ヘッダ。新しいオペランドタイプやプレフィックス定義を追加/更新。
  • src/cmd/6l/optab.c: AMD64リンカの命令エンコーディングテーブル。新しい命令のエンコーディングルールを追加。
  • src/cmd/6l/span.c: AMD64リンカのスパン処理。新しいプレフィックスの処理を追加。
  • src/cmd/8a/a.y: x86アセンブラの文法定義(Yacc入力)。新しい命令の構文ルールを追加。
  • src/cmd/8a/lex.c: x86アセンブラの字句解析器。新しい命令のニーモニックを追加。
  • src/cmd/8a/y.tab.c: src/cmd/8a/a.yからBisonによって生成されたx86アセンブラのパーサ。文法変更に伴い再生成。
  • src/cmd/8a/y.tab.h: src/cmd/8a/a.yからBisonによって生成されたx86アセンブラのパーサのヘッダ。文法変更に伴い再生成。
  • src/cmd/8l/8.out.h: x86リンカの命令コード定義。新しい命令の列挙型を追加。
  • src/cmd/8l/l.h: x86リンカの共通ヘッダ。新しいオペランドタイプやプレフィックス定義を追加/更新。
  • src/cmd/8l/optab.c: x86リンカの命令エンコーディングテーブル。新しい命令のエンコーディングルールを追加。
  • src/cmd/8l/span.c: x86リンカのスパン処理。新しいプレフィックスの処理を追加。

コアとなるコードの解説

src/cmd/6a/lex.c および src/cmd/8a/lex.c の変更

これらのファイルでは、アセンブラが認識する命令のリストに、PINSRD, PINSRQ, PSHUFB, AESENC, CPUID, MOVQ などの新しい命令が追加されています。これにより、Goのアセンブリコード内でこれらの命令を直接記述できるようになります。

// src/cmd/6a/lex.c (抜粋)
 	"PINSRW",	LTYPEX,	APINSRW,
+	"PINSRD",	LTYPEX,	APINSRD,
+	"PINSRQ",	LTYPEX,	APINSRQ,
 	"PMADDWL",	LTYPE3,	APMADDWL,
 // ...
 	"PSHUFW",	LTYPEX, APSHUFW,
+	"PSHUFB",	LTYPEM, APSHUFB,

LTYPEXLTYPEMは、命令のオペランドの型を示す内部的な分類です。

src/cmd/8a/a.y の変更

このファイルはアセンブラの文法を定義しており、新しい命令の構文ルールが追加されています。特に注目すべきは、spec10という新しいルールが追加され、PINSRDのような命令がimm, rem, regというオペランドの組み合わせを取ることを定義している点です。

// src/cmd/8a/a.y (抜粋)
%token	<lval>\tLTYPEX LCONST LFP LPC LSB
// ...
%type	<gen2>\tspec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9 spec10
%%
// ...
inst:
// ...
|\tLTYPEXC spec9	{ outcode($1, &$2); }
+|\tLTYPEX spec10	{ outcode($1, &$2); }

// ...
spec10:	/* PINSRD */
	imm ',' rem ',' reg
	{
		$$.from = $3;
		$$.to = $5;
		if($1.type != D_CONST)
			yyerror("illegal constant");
		$$.to.offset = $1.offset;
	}

LTYPEXトークンがspec10ルールにマッピングされ、PINSRD命令の構文が定義されています。immは即値、remはメモリまたはレジスタ、regはレジスタを表します。

src/cmd/6l/optab.c および src/cmd/8l/optab.c の変更

これらのファイルは、リンカがアセンブリ命令を機械語に変換するためのオペコードテーブルを定義しています。新しいAES命令に対応するエントリが追加され、それぞれの命令のオペランドの型、プレフィックス、オペコードバイトが指定されています。

// src/cmd/6l/optab.c (抜粋)
 	{ APINSRW,	yextrw,	Pq, 0xc4,(00) },
+	{ APINSRD,	yinsr,	Pq, 0x3a, 0x22, (00) },
+	{ APINSRQ,	yinsr,	Pq3, 0x3a, 0x22, (00) },
 	{ APMADDWL,	ymm,	Py, 0xf5,Pe,0xf5 },
// ...
 	{ APSHUFW,	ymshuf,	Pm, 0x70,(00) },
+	{ APSHUFB,	ymshufb,Pq, 0x38, 0x00 },

APINSRD, APINSRQ, APSHUFBなどの命令が追加され、それぞれに対応するオペランドパターン(yinsr, ymshufb)とエンコーディング情報(Pq, Pq3, 0x3a, 0x22, 0x38, 0x00など)が定義されています。Pq3は新しいプレフィックスの組み合わせを示します。

src/cmd/6l/l.h および src/cmd/8l/l.h の変更

これらのヘッダファイルでは、リンカが使用する内部的な定数やフラグが更新されています。特に、新しいオペランドタイプZm2_rが追加され、Pq, Pf2, Pf3のコメントがより詳細になり、Pq3という新しいプレフィックスが定義されています。

// src/cmd/6l/l.h (抜粋)
 	Zm_r,
+	Zm2_r,
 	Zm_r_xm,
// ...
 	Pq		= 0xff,	/* both escapes: 66 0f */
 	Pb		= 0xfe,	/* byte operands */
 	Pf2		= 0xf2,	/* xmm escape 1: f2 0f */
 	Pf3		= 0xf3,	/* xmm escape 2: f3 0f */
+	Pq3		= 0x67, /* xmm escape 3: 66 48 0f */
 	Pw		= 0x48,	/* Rex.w */

Pq3は、66 48 0fというバイト列で構成されるプレフィックスを示しており、これは特定の命令(例: PINSRQ)で使用される拡張エンコーディングです。

src/cmd/6l/span.c および src/cmd/8l/span.c の変更

これらのファイルでは、リンカが命令の長さを計算し、ジャンプ命令のオフセットを調整するロジックが更新されています。新しいプレフィックスPq3の処理が追加され、リンカがこれらの命令を正しく処理できるようになっています。

// src/cmd/6l/span.c (抜粋)
 	case Pq:	/* 16 bit escape and opcode escape */
 		*andptr++ = Pe;
 		*andptr++ = Pm;
 		break;
+	case Pq3:	/* 16 bit escape, Rex.w, and opcode escape */
+		*andptr++ = Pe;
+		*andptr++ = Pw;
+		*andptr++ = Pm;
+		break;

このコードは、Pq3プレフィックスが検出された場合に、対応するバイト列(Pe, Pw, Pm)を生成するようにリンカに指示します。

これらの変更は、Go言語のツールチェインがハードウェアのAES命令を完全にサポートするための基盤を構築するものであり、Goで書かれた暗号関連のアプリケーションのパフォーマンスに大きな影響を与えます。

関連リンク

参考にした情報源リンク