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

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

このコミットは、Goコンパイラのバックエンドの一部であるsrc/cmd/6g/gsubr.cファイルに対する変更です。このファイルは、Go言語の抽象構文木(AST)の操作を、ターゲットアーキテクチャ(この場合は6g、つまりamd64アーキテクチャ)のアセンブリ命令に変換する役割を担っています。

コミット

added and, or, xor opcodes

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

https://github.com/golang/go/commit/3f2d542817717400e28df072fa2ac20d648ad5be

元コミット内容

commit 3f2d542817717400e28df072fa2ac20d648ad5be
Author: Ken Thompson <ken@golang.org>
Date:   Mon Jun 16 17:04:39 2008 -0700

    added and, or, xor opcodes
    
    SVN=123031

変更の背景

このコミットは、Go言語の初期開発段階における重要な機能追加の一つです。Goコンパイラが、論理AND (&)、論理OR (|)、論理XOR (^)といったビット演算子を、ターゲットアーキテクチャの適切なアセンブリ命令に正しく変換できるようにするための変更です。

Go言語は、その設計当初から効率的な実行を重視しており、コンパイラが生成する機械語コードの品質は非常に重要です。ビット演算は、低レベルのプログラミングや、データ構造の操作、暗号化、ネットワークプロトコルなど、様々な場面で頻繁に使用されます。これらの演算がコンパイラによって適切にアセンブリ命令にマッピングされない場合、Goプログラムのパフォーマンスに直接的な影響を与えたり、そもそもこれらの演算が利用できなかったりする可能性があります。

このコミットが行われた2008年6月は、Go言語がまだ一般に公開される前の初期段階であり、コンパイラの基本的な機能が次々と実装されていた時期です。この変更は、Go言語がビット演算をネイティブにサポートし、効率的なコードを生成するための基盤を固める一環として行われました。

前提知識の解説

このコミットを理解するためには、以下の概念についての知識が必要です。

  • コンパイラのバックエンド: コンパイラは通常、フロントエンド、最適化、バックエンドの3つの主要なフェーズに分かれています。
    • フロントエンド: ソースコードを解析し、抽象構文木(AST)などの中間表現を生成します。
    • 最適化: 中間表現を変換し、より効率的なコードを生成します。
    • バックエンド: 最適化された中間表現を、特定のCPUアーキテクチャの機械語(アセンブリ命令)に変換します。src/cmd/6g/gsubr.cはこのバックエンドの一部です。
  • 抽象構文木 (AST): プログラムのソースコードの抽象的な構造を木構造で表現したものです。コンパイラはASTを操作してコードを分析・変換します。
  • アセンブリ命令 (Opcodes): CPUが直接実行できる低レベルの命令です。各CPUアーキテクチャには独自のアセンブリ命令セットがあります。
    • AANDB, AANDW, AANDL, AANDQ: それぞれ8ビット、16ビット、32ビット、64ビットのAND演算を行うアセンブリ命令です。Goコンパイラの内部では、これらの命令が生成されます。
    • AORB, AORW, AORL, AORQ: 同様に、OR演算を行うアセンブリ命令です。
    • AXORB, AXORW, AXORL, AXORQ: 同様に、XOR演算を行うアセンブリ命令です。
    • 末尾のB, W, L, Qはそれぞれバイト (Byte, 8bit)、ワード (Word, 16bit)、ロング (Long, 32bit)、クアッド (Quad, 64bit) を示し、オペランドのサイズを表します。
  • Goコンパイラの内部表現: Goコンパイラは、Go言語の演算子を内部的なオペレーションコード(OAND, OOR, OXORなど)として表現します。これらの内部オペレーションコードは、optoas関数によって特定のアセンブリ命令にマッピングされます。
  • 型システム: Go言語は静的型付け言語であり、各変数や式には型があります。コンパイラは、演算の対象となるデータの型(例: TINT8 (8ビット整数), TUINT64 (64ビット符号なし整数), TPTR32 (32ビットポインタ))に基づいて、適切なアセンブリ命令を選択する必要があります。
  • CASEマクロ: このコミットで使われているCASEマクロは、Goコンパイラの内部で、特定のオペレーションコードと型の組み合わせを簡潔に記述するために使用されるものです。例えば、CASE(OAND, TINT8)は「AND演算で、かつ8ビット整数型の場合」という条件を表します。

技術的詳細

このコミットの核心は、Goコンパイラのsrc/cmd/6g/gsubr.cファイル内のoptoas関数に、ビット演算子(AND, OR, XOR)に対応する新しいケースを追加したことです。

optoas関数は、Go言語の内部的なオペレーションコード(OAND, OOR, OXORなど)と、そのオペレーションが適用されるデータの型(TINT8, TUINT16, TPTR64など)の組み合わせを受け取り、それに対応するx86-64アセンブリ命令(AANDB, AORW, AXORQなど)を返します。

追加されたコードは、各ビット演算子に対して、以下の型の組み合わせを網羅しています。

  • 8ビット整数: TINT8, TUINT8
  • 16ビット整数: TINT16, TUINT16
  • 32ビット整数: TINT32, TUINT32, TPTR32 (32ビットポインタも32ビット整数として扱われる)
  • 64ビット整数: TINT64, TUINT64, TPTR64 (64ビットポインタも64ビット整数として扱われる)

これにより、Goコンパイラは、Goソースコード内のビット演算子を、オペランドのサイズと型に応じて、適切なバイト、ワード、ロング、またはクアッドのアセンブリ命令に正確に変換できるようになりました。例えば、int8型の変数に対するAND演算はAANDBに、int64型の変数に対するXOR演算はAXORQに変換されます。

この変更により、Go言語で記述されたビット演算が、CPUのネイティブなビット演算命令に直接マッピングされるため、非常に効率的に実行されることが保証されます。これは、Go言語がシステムプログラミングや高性能コンピューティングの分野で利用される上で不可欠な機能です。

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

変更はsrc/cmd/6g/gsubr.cファイルのoptoas関数内、既存のANEGQ(符号反転)のケースの直後に追加されています。

--- a/src/cmd/6g/gsubr.c
+++ b/src/cmd/6g/gsubr.c
@@ -1360,6 +1360,72 @@ optoas(int op, Type *t)
 		a = ANEGQ;
 		break;
 
+	case CASE(OAND, TINT8):
+	case CASE(OAND, TUINT8):
+		a = AANDB;
+		break;
+
+	case CASE(OAND, TINT16):
+	case CASE(OAND, TUINT16):
+		a = AANDW;
+		break;
+
+	case CASE(OAND, TINT32):
+	case CASE(OAND, TUINT32):
+	case CASE(OAND, TPTR32):
+		a = AANDL;
+		break;
+
+	case CASE(OAND, TINT64):
+	case CASE(OAND, TUINT64):
+	case CASE(OAND, TPTR64):
+		a = AANDQ;
+		break;
+
+	case CASE(OOR, TINT8):
+	case CASE(OOR, TUINT8):
+		a = AORB;
+		break;
+
+	case CASE(OOR, TINT16):
+	case CASE(OOR, TUINT16):
+		a = AORW;
+		break;
+
+	case CASE(OOR, TINT32):
+	case CASE(OOR, TUINT32):
+	case CASE(OOR, TPTR32):
+		a = AORL;
+		break;
+
+	case CASE(OOR, TINT64):
+	case CASE(OOR, TUINT64):
+	case CASE(OOR, TPTR64):
+		a = AORQ;
+		break;
+
+	case CASE(OXOR, TINT8):
+	case CASE(OXOR, TUINT8):
+		a = AXORB;
+		break;
+
+	case CASE(OXOR, TINT16):
+	case CASE(OXOR, TUINT16):
+		a = AXORW;
+		break;
+
+	case CASE(OXOR, TINT32):
+	case CASE(OXOR, TUINT32):
+	case CASE(OXOR, TPTR32):
+		a = AXORL;
+		break;
+
+	case CASE(OXOR, TINT64):
+	case CASE(OXOR, TUINT64):
+	case CASE(OXOR, TPTR64):
+		a = AXORQ;
+		break;
+
 	case CASE(OLSH, TINT8):
 	case CASE(OLSH, TUINT8):
 		a = ASHLB;

コアとなるコードの解説

このコードは、C言語のswitch文とcaseラベルを使用して、Goコンパイラの内部オペレーションコードと型の組み合わせを処理しています。

  • CASE(OAND, TINT8): これは、Go言語の内部表現でAND演算子(OAND)が、8ビット符号付き整数型(TINT8)に適用される場合のケースです。
  • CASE(OAND, TUINT8): 同様に、8ビット符号なし整数型(TUINT8)に適用される場合のケースです。
  • これらのcaseにマッチした場合、変数aAANDBが代入されます。AANDBは、x86-64アーキテクチャにおける8ビットのANDアセンブリ命令を表します。

同様のパターンが、OR演算子(OOR)とXOR演算子(OXOR)に対しても、それぞれの型サイズ(8ビット、16ビット、32ビット、64ビット)と対応するアセンブリ命令(AORBAORQAXORBAXORQ)で繰り返されています。

特筆すべきは、32ビットおよび64ビットのポインタ型(TPTR32, TPTR64)が、それぞれ32ビットおよび64ビットの整数型と同じアセンブリ命令にマッピングされている点です。これは、ポインタが本質的にメモリアドレスを表す整数値であるため、ビット演算が整数と同様に適用できることを意味します。

この変更により、Goコンパイラは、Go言語のビット演算子を、ターゲットアーキテクチャの効率的なネイティブ命令に変換するための完全なサポートを獲得しました。

関連リンク

  • Go言語のソースコードリポジトリ: https://github.com/golang/go
  • Go言語のコンパイラに関するドキュメント(公式ドキュメントやGoの内部構造に関する記事などを参照すると良いでしょう)

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • x86-64アセンブリ命令セットリファレンス
  • コンパイラ設計に関する一般的な書籍やオンラインリソース
  • Go言語のコンパイラソースコード(特にsrc/cmd/6g/ディレクトリ内の他のファイル)
  • Go言語の初期開発に関するメーリングリストや設計ドキュメント(もし公開されていれば)
  • Go言語の型システムに関する情報
  • Go言語の内部表現に関する情報(AST、オペレーションコードなど)
  • Go言語のコンパイラがどのようにアセンブリコードを生成するかについての解説記事