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

[インデックス 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命令を正しく解析し、機械語に変換できるようにするための変更を含んでいます。

  1. a.y (Yacc文法ファイル) の変更:

    • 新しいトークンLTYPEXCが追加されています。これは、特定の種類のSSE2命令(例: CMPPS, CMPPD)を識別するために使用されます。これらの命令は、通常の算術命令とは異なるオペランドの構造を持つため、専用の型が割り当てられています。
    • 新しい文法規則spec9が追加されています。これは、LTYPEXC型の命令のオペランド構造を定義します。具体的には、reg ',' rem ',' conという形式で、レジスタ、メモリまたはレジスタ、そして定数をオペランドとして取る命令に対応します。これはCMPPSCMPPDのような比較命令でよく見られる形式です。
  2. 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は前述の通り特定の比較命令に使用されます。
  3. y.tab.c および y.tab.h の変更:

    • これらのファイルは、a.ylex.cからYaccとLexによって自動生成されるファイルです。したがって、a.ylex.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が定義されています。これは、CMPPSCMPPDのような特定の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構造体($$)のfromtoフィールドを設定し、toのオフセットに定数値を格納しています。これは、CMPPSCMPPD命令が、ソースレジスタ、デスティネーションレジスタ/メモリ、そして比較条件を示す即値(定数)をオペランドとして取る典型的な形式を反映しています。

lex.c の変更点

  • SSE2命令の追加: lex.ckeywords構造体配列に、大量のSSE2命令のニーモニックが追加されています。各ニーモニックは、対応するトークン型(LTYPE3またはLTYPEXC)と内部的なアセンブラ命令コード(例: AADDPD, ACMPPD)に関連付けられています。
    • LTYPE3は、多くのSSE2命令が採用する一般的なオペランド形式(例: ADDPS XMM0, XMM1)に対応します。
    • LTYPEXCは、a.yで定義されたspec9ルールと連携し、CMPPSCMPPDのような特定の比較命令の構文解析を可能にします。

これらの変更により、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

参考にした情報源リンク