[インデックス 14072] ファイルの概要
このコミットは、Go言語のツールチェインの一部であるcmd/8a
アセンブラにSSE2命令のサポートを追加するものです。具体的には、アセンブラの字句解析器(lexer)と構文解析器(parser)を更新し、新しいSSE2命令を認識して処理できるようにしています。
コミット
commit 5da37c0901bd420257bd0affe6dbe30ca957f55f
Author: Russ Cox <rsc@golang.org>
Date: Sun Oct 7 16:36:29 2012 -0400
cmd/8a: add SSE2 instructions
R=ken
CC=golang-dev
https://golang.org/cl/6614063
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5da37c0901bd420257bd0affe6dbe30ca957f55f
元コミット内容
cmd/8a: add SSE2 instructions
R=ken
CC=golang-dev
https://golang.org/cl/6614063
変更の背景
Go言語は、様々なアーキテクチャをサポートするために、それぞれのアセンブラを持っています。cmd/8a
は、x86アーキテクチャ(特に32ビット)向けのアセンブラです。SSE2(Streaming SIMD Extensions 2)は、IntelとAMDのプロセッサに搭載されているSIMD(Single Instruction, Multiple Data)命令セットの一種で、浮動小数点演算や整数演算を高速化するために使用されます。
このコミットの背景には、Go言語がより高性能な数値計算やマルチメディア処理を必要とするアプリケーションを効率的にサポートするために、基盤となるアセンブラが最新のCPU命令セットに対応する必要があったことが挙げられます。SSE2命令のサポートを追加することで、Goプログラムがこれらの命令を活用し、特定のワークロードにおいてパフォーマンスを向上させることが可能になります。
前提知識の解説
アセンブラとGo言語のcmd/8a
アセンブラは、アセンブリ言語で書かれたプログラムを機械語に変換するソフトウェアです。機械語はCPUが直接実行できる形式の命令です。Go言語のツールチェインには、各ターゲットアーキテクチャ向けのアセンブラが含まれており、cmd/8a
はその中でもx86(32ビット)アーキテクチャを担当しています。Go言語のコンパイラは、Goのソースコードを中間表現に変換し、最終的に各アーキテクチャのアセンブラが機械語に変換します。
SSE2 (Streaming SIMD Extensions 2)
SSE2は、Intel Pentium 4プロセッサで導入され、AMDのAthlon 64以降のプロセッサでもサポートされている命令セットです。主な特徴は以下の通りです。
- SIMD (Single Instruction, Multiple Data): 一つの命令で複数のデータを同時に処理する能力。これにより、データ並列性の高い処理(例: ベクトル演算、画像処理、音声処理)を高速化できます。
- 128ビットXMMレジスタ: 8つの128ビットXMMレジスタ(XMM0-XMM7)を導入し、単精度浮動小数点数(float)を4つ、倍精度浮動小数点数(double)を2つ、または様々なサイズの整数データを格納できます。
- 倍精度浮動小数点数演算: SSE2は、倍精度浮動小数点数(64ビット)のSIMD演算を初めてサポートしました。これにより、科学技術計算や金融計算など、高い精度が要求される分野での高速化が可能になりました。
- 整数SIMD演算の拡張: SSE2は、SSEで導入された整数SIMD命令をさらに拡張し、より多くの整数データ型と演算をサポートします。
Yacc (Yet Another Compiler Compiler) と Lex (Lexical Analyzer Generator)
YaccとLexは、コンパイラやインタプリタ、アセンブラなどの言語処理系を生成するためのツールです。
- Lex (字句解析器ジェネレータ): ソースコードをトークン(意味を持つ最小単位、例: キーワード、識別子、演算子)のストリームに分割する字句解析器(lexer)を生成します。Lexの入力は正規表現とそれに対応するアクションの集合です。
- Yacc (構文解析器ジェネレータ): トークンのストリームを入力として受け取り、言語の文法規則に従って構文木を構築する構文解析器(parser)を生成します。Yaccの入力はBNF(Backus-Naur Form)のような文法規則とそれに対応するアクションの集合です。
アセンブラの開発において、Lexはアセンブリ言語のニーモニック(例: MOV
, ADD
, CMPPS
)、レジスタ名(例: AX
, XMM0
)、定数などをトークン化し、Yaccはこれらのトークンを組み合わせて有効なアセンブリ命令の構造を認識します。
技術的詳細
このコミットは、cmd/8a
アセンブラがSSE2命令を正しく解析し、機械語に変換できるようにするための変更を含んでいます。
-
a.y
(Yacc文法ファイル) の変更:- 新しいトークン
LTYPEXC
が追加されています。これは、特定の種類のSSE2命令(例:CMPPS
,CMPPD
)を識別するために使用されます。これらの命令は、通常の算術命令とは異なるオペランドの構造を持つため、専用の型が割り当てられています。 - 新しい文法規則
spec9
が追加されています。これは、LTYPEXC
型の命令のオペランド構造を定義します。具体的には、reg ',' rem ',' con
という形式で、レジスタ、メモリまたはレジスタ、そして定数をオペランドとして取る命令に対応します。これはCMPPS
やCMPPD
のような比較命令でよく見られる形式です。
- 新しいトークン
-
lex.c
(Lex字句解析ファイル) の変更:- 多数の新しいSSE2命令のニーモニックが追加されています。これには、浮動小数点演算(
ADDPD
,ADDPS
,DIVPD
,MULPD
,SQRTPD
,SUBPD
など)、論理演算(ANDPD
,ANDPS
,XORPD
など)、データ転送(MOVAPD
,MOVUPS
,MOVSD
など)、変換(CVTPL2PD
,CVTPS2PL
など)、比較(CMPPD
,CMPPS
,COMISD
など)など、多岐にわたる命令が含まれます。 - これらのニーモニックは、
LTYPE3
または新しく追加されたLTYPEXC
というトークン型に関連付けられています。LTYPE3
は一般的な3オペランド命令(または2オペランドでソースとデスティネーションが異なる命令)に使用され、LTYPEXC
は前述の通り特定の比較命令に使用されます。
- 多数の新しいSSE2命令のニーモニックが追加されています。これには、浮動小数点演算(
-
y.tab.c
およびy.tab.h
の変更:- これらのファイルは、
a.y
とlex.c
からYaccとLexによって自動生成されるファイルです。したがって、a.y
とlex.c
の変更に伴い、これらのファイルも更新され、新しいトークン定義、文法規則、およびそれらを処理するためのC言語コードが反映されています。特に、y.tab.c
では、Bisonのバージョンが2.3から2.4.1に更新されており、これは生成されるコードの構造や内部的な処理に影響を与える可能性があります。
- これらのファイルは、
これらの変更により、cmd/8a
アセンブラは、SSE2命令を含むアセンブリコードを正しく解析し、Goコンパイラが生成する中間表現から最終的な機械語への変換パスを完了できるようになります。
コアとなるコードの変更箇所
src/cmd/8a/a.y
--- a/src/cmd/8a/a.y
+++ b/src/cmd/8a/a.y
@@ -53,7 +53,7 @@
%left '+' '/' '%'
%token <lval> LTYPE0 LTYPE1 LTYPE2 LTYPE3 LTYPE4
-%token <lval> LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPES LTYPEM LTYPEI LTYPEG
+%token <lval> LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPES LTYPEM LTYPEI LTYPEG LTYPEXC
%token <lval> LCONST LFP LPC LSB
%token <dval> LFCONST
@@ -63,7 +63,7 @@
%type <con2> con2
%type <gen> mem imm imm2 reg nam rel rem rim rom omem nmem
%type <gen2> nonnon nonrel nonrem rimnon rimrem remrim
-%type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8
+%type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9
%%
prog:
| prog
@@ -116,6 +116,7 @@ inst:
| LTYPEM spec6 { outcode($1, &$2); }
| LTYPEI spec7 { outcode($1, &$2); }
| LTYPEG spec8 { outcode($1, &$2); }
+|\tLTYPEXC spec9 { outcode($1, &$2); }
nonnon:
{
@@ -287,6 +288,14 @@ spec8: /* GLOBL */
$$.to = $5;
}
+spec9: /* CMPPS/CMPPD */
+ reg ',' rem ',' con
+ {
+ $$.from = $1;
+ $$.to = $3;
+ $$.to.offset = $5;
+ }
+
rem:
reg
| mem
src/cmd/8a/lex.c
--- a/src/cmd/8a/lex.c
+++ b/src/cmd/8a/lex.c
@@ -674,6 +674,114 @@ struct
"PREFETCHNTA", LTYPE2, APREFETCHNTA,
"UNDEF", LTYPE0, AUNDEF,
+ "ADDPD", LTYPE3, AADDPD,
+ "ADDPS", LTYPE3, AADDPS,
+ "ADDSD", LTYPE3, AADDSD,
+ "ADDSS", LTYPE3, AADDSS,
+ "ANDNPD", LTYPE3, AANDNPD,
+ "ANDNPS", LTYPE3, AANDNPS,
+ "ANDPD", LTYPE3, AANDPD,
+ "ANDPS", LTYPE3, AANDPS,
+ "CMPPD", LTYPEXC,ACMPPD,
+ "CMPPS", LTYPEXC,ACMPPS,
+ "CMPSD", LTYPEXC,ACMPSD,
+ "CMPSS", LTYPEXC,ACMPSS,
+ "COMISD", LTYPE3, ACOMISD,
+ "COMISS", LTYPE3, ACOMISS,
+ "CVTPL2PD", LTYPE3, ACVTPL2PD,
+ "CVTPL2PS", LTYPE3, ACVTPL2PS,
+ "CVTPD2PL", LTYPE3, ACVTPD2PL,
+ "CVTPD2PS", LTYPE3, ACVTPD2PS,
+ "CVTPS2PL", LTYPE3, ACVTPS2PL,
+ "CVTPS2PD", LTYPE3, ACVTPS2PD,
+ "CVTSD2SL", LTYPE3, ACVTSD2SL,
+ "CVTSD2SS", LTYPE3, ACVTSD2SS,
+ "CVTSL2SD", LTYPE3, ACVTSL2SD,
+ "CVTSL2SS", LTYPE3, ACVTSL2SS,
+ "CVTSS2SD", LTYPE3, ACVTSS2SD,
+ "CVTSS2SL", LTYPE3, ACVTSS2SL,
+ "CVTTPD2PL", LTYPE3, ACVTTPD2PL,
+ "CVTTPS2PL", LTYPE3, ACVTTPS2PL,
+ "CVTTSD2SL", LTYPE3, ACVTTSD2SL,
+ "CVTTSS2SL", LTYPE3, ACVTTSS2SL,
+ "DIVPD", LTYPE3, ADIVPD,
+ "DIVPS", LTYPE3, ADIVPS,
+ "DIVSD", LTYPE3, ADIVSD,
+ "DIVSS", LTYPE3, ADIVSS,
+ "MASKMOVOU", LTYPE3, AMASKMOVOU,
+ "MASKMOVDQU", LTYPE3, AMASKMOVOU, /* syn */
+ "MAXPD", LTYPE3, AMAXPD,
+ "MAXPS", LTYPE3, AMAXPS,
+ "MAXSD", LTYPE3, AMAXSD,
+ "MAXSS", LTYPE3, AMAXSS,
+ "MINPD", LTYPE3, AMINPD,
+ "MINPS", LTYPE3, AMINPS,
+ "MINSD", LTYPE3, AMINSD,
+ "MINSS", LTYPE3, AMINSS,
+ "MOVAPD", LTYPE3, AMOVAPD,
+ "MOVAPS", LTYPE3, AMOVAPS,
+ "MOVO", LTYPE3, AMOVO,
+ "MOVOA", LTYPE3, AMOVO, /* syn */
+ "MOVOU", LTYPE3, AMOVOU,
+ "MOVHLPS", LTYPE3, AMOVHLPS,
+ "MOVHPD", LTYPE3, AMOVHPD,
+ "MOVHPS", LTYPE3, AMOVHPS,
+ "MOVLHPS", LTYPE3, AMOVLHPS,
+ "MOVLPD", LTYPE3, AMOVLPD,
+ "MOVLPS", LTYPE3, AMOVLPS,
+ "MOVMSKPD", LTYPE3, AMOVMSKPD,
+ "MOVMSKPS", LTYPE3, AMOVMSKPS,
+ "MOVNTO", LTYPE3, AMOVNTO,
+ "MOVNTDQ", LTYPE3, AMOVNTO, /* syn */
+ "MOVNTPD", LTYPE3, AMOVNTPD,
+ "MOVNTPS", LTYPE3, AMOVNTPS,
+ "MOVSD", LTYPE3, AMOVSD,
+ "MOVSS", LTYPE3, AMOVSS,
+ "MOVUPD", LTYPE3, AMOVUPD,
+ "MOVUPS", LTYPE3, AMOVUPS,
+ "MULPD", LTYPE3, AMULPD,
+ "MULPS", LTYPE3, AMULPS,
+ "MULSD", LTYPE3, AMULSD,
+ "MULSS", LTYPE3, AMULSS,
+ "ORPD", LTYPE3, AORPD,
+ "ORPS", LTYPE3, AORPS,
+ "PADDQ", LTYPE3, APADDQ,
+ "PMAXSW", LTYPE3, APMAXSW,
+ "PMAXUB", LTYPE3, APMAXUB,
+ "PMINSW", LTYPE3, APMINSW,
+ "PMINUB", LTYPE3, APMINUB,
+ "PSADBW", LTYPE3, APSADBW,
+ "PSUBB", LTYPE3, APSUBB,
+ "PSUBL", LTYPE3, APSUBL,
+ "PSUBQ", LTYPE3, APSUBQ,
+ "PSUBSB", LTYPE3, APSUBSB,
+ "PSUBSW", LTYPE3, APSUBSW,
+ "PSUBUSB", LTYPE3, APSUBUSB,
+ "PSUBUSW", LTYPE3, APSUBUSW,
+ "PSUBW", LTYPE3, APSUBW,
+ "PUNPCKHQDQ", LTYPE3, APUNPCKHQDQ,
+ "PUNPCKLQDQ", LTYPE3, APUNPCKLQDQ,
+ "RCPPS", LTYPE3, ARCPPS,
+ "RCPSS", LTYPE3, ARCPSS,
+ "RSQRTPS", LTYPE3, ARSQRTPS,
+ "RSQRTSS", LTYPE3, ARSQRTSS,
+ "SQRTPD", LTYPE3, ASQRTPD,
+ "SQRTPS", LTYPE3, ASQRTPS,
+ "SQRTSD", LTYPE3, ASQRTSD,
+ "SQRTSS", LTYPE3, ASQRTSS,
+ "SUBPD", LTYPE3, ASUBPD,
+ "SUBPS", LTYPE3, ASUBPS,
+ "SUBSD", LTYPE3, ASUBSD,
+ "SUBSS", LTYPE3, ASUBSS,
+ "UCOMISD", LTYPE3, AUCOMISD,
+ "UCOMISS", LTYPE3, AUCOMISS,
+ "UNPCKHPD", LTYPE3, AUNPCKHPD,
+ "UNPCKHPS", LTYPE3, AUNPCKHPS,
+ "UNPCKLPD", LTYPE3, AUNPCKLPD,
+ "UNPCKLPS", LTYPE3, AUNPCKLPS,
+ "XORPD", LTYPE3, AXORPD,
+ "XORPS", LTYPE3, AXORPS,
+
0
};
コアとなるコードの解説
a.y
の変更点
%token ... LTYPEXC
: 新しいトークン型LTYPEXC
が定義されています。これは、CMPPS
やCMPPD
のような特定のSSE2比較命令を扱うために導入されました。これらの命令は、比較条件を即値として取るため、通常の命令とは異なる構文解析ルールが必要です。%type <gen2> ... spec9
: 新しい非終端記号spec9
が定義され、そのセマンティック値の型がgen2
(おそらく汎用的なオペランドペアを表す構造体)であることが示されています。| LTYPEXC spec9 { outcode($1, &$2); }
:inst
(命令)の文法規則に、LTYPEXC
トークンに続くspec9
ルールを処理する新しいプロダクションが追加されました。これにより、アセンブラはLTYPEXC
型の命令を認識し、spec9
で定義されたオペランド構造に従ってコードを生成します。spec9: reg ',' rem ',' con { ... }
:spec9
ルールの定義です。これは、reg
(レジスタ)、,
(カンマ)、rem
(レジスタまたはメモリ)、,
(カンマ)、con
(定数)という形式のオペランドシーケンスを期待します。このルールのアクションブロックでは、これらのオペランドからgen2
構造体($$
)のfrom
とto
フィールドを設定し、to
のオフセットに定数値を格納しています。これは、CMPPS
やCMPPD
命令が、ソースレジスタ、デスティネーションレジスタ/メモリ、そして比較条件を示す即値(定数)をオペランドとして取る典型的な形式を反映しています。
lex.c
の変更点
- SSE2命令の追加:
lex.c
のkeywords
構造体配列に、大量のSSE2命令のニーモニックが追加されています。各ニーモニックは、対応するトークン型(LTYPE3
またはLTYPEXC
)と内部的なアセンブラ命令コード(例:AADDPD
,ACMPPD
)に関連付けられています。LTYPE3
は、多くのSSE2命令が採用する一般的なオペランド形式(例:ADDPS XMM0, XMM1
)に対応します。LTYPEXC
は、a.y
で定義されたspec9
ルールと連携し、CMPPS
やCMPPD
のような特定の比較命令の構文解析を可能にします。
これらの変更により、cmd/8a
アセンブラは、SSE2命令を含むアセンブリ言語のソースコードを正確に字句解析し、構文解析ツリーを構築できるようになります。これにより、GoコンパイラがSSE2命令を生成する際に、cmd/8a
がそれらを正しく機械語に変換できるようになり、GoプログラムがSSE2の高速化機能を活用できるようになります。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/
- Go言語のツールチェインに関する情報: Go言語の公式ドキュメントやソースコードリポジトリで、
cmd/8a
やアセンブラに関する詳細な情報を探すことができます。 - Intel 64 and IA-32 Architectures Software Developer's Manuals: SSE2命令セットの詳細な仕様は、Intelの公式ドキュメントで確認できます。
- Volume 1: Basic Architecture
- Volume 2A/2B/2C: Instruction Set Reference, A-Z
- Volume 3A/3B/3C/3D: System Programming Guide
- GNU Bison: https://www.gnu.org/software/bison/
- Flex (Lexの代替): https://github.com/westes/flex
参考にした情報源リンク
- golang/go GitHubリポジトリ: https://github.com/golang/go (特に
src/cmd/8a
ディレクトリ) - Go CL 6614063: https://golang.org/cl/6614063 (元のコードレビューと変更の詳細)
- Wikipedia - Streaming SIMD Extensions 2 (SSE2): https://en.wikipedia.org/wiki/SSE2
- Wikipedia - Yacc: https://en.wikipedia.org/wiki/Yacc
- Wikipedia - Lex (software): https://en.wikipedia.org/wiki/Lex_(software)
- Bison Manual: https://www.gnu.org/software/bison/manual/
- Flex Manual: https://flex.sourceforge.io/manual/
- Intel® 64 and IA-32 Architectures Software Developer Manuals: https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html