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

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

このコミットは、GoコンパイラのARMアーキテクチャ向けバックエンドであるcmd/5gにおいて、xtramodesと呼ばれる最適化を有効にするものです。具体的には、ARMプロセッサのポストインクリメントアドレッシングモードとシフトオフセットアドレッシングモードを活用することで、生成されるアセンブリコードの効率を向上させます。

コミット

commit 3f26c5e1248d53d1e46566e40bb1a21ab6ed9f05
Author: Dave Cheney <dave@cheney.net>
Date:   Sun Nov 11 07:51:20 2012 +1100

    cmd/5g: enable xtramodes optimisation
    
    xtramodes’ C_PBIT optimisation transforms:
    
    MOVW          0(R3),R1
    ADD           $4,R3,R3
    
    into:
    
    MOVW.P        4(R3),R1
    
    and the AADD optimisation tranforms:
    
    ADD          R0,R1
    MOVBU        0(R1),R0
    
    into:
    
    MOVBU        R0<<0(R1),R0
    
    5g does not appear to generate sequences that
    can be transformed by xtramodes’ AMOVW.
    
    R=remyoudompheng, rsc
    CC=golang-dev
    https://golang.org/cl/6817085

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

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

元コミット内容

このコミットは、src/cmd/5g/peep.cファイルに対して行われた変更です。主な変更点は、以前コメントアウトされていたxtramodes最適化のコードブロックを有効にし、関連する関数に詳細なコメントを追加したことです。これにより、5gコンパイラが特定の命令シーケンスをより効率的なARMアセンブリ命令に変換できるようになります。

変更の背景

Goコンパイラは、異なるアーキテクチャ向けにコードを生成するためのバックエンドを持っています。cmd/5gはARMアーキテクチャ向けのバックエンドです。コンパイラが生成するアセンブリコードの効率は、プログラムの実行速度に直結します。ARMプロセッサには、特定の命令シーケンスをよりコンパクトで高速な単一命令に変換できる特殊なアドレッシングモード(ポストインクリメントやシフトオフセット)が存在します。

このコミットの背景には、これらのARM固有の最適化を活用し、5gコンパイラが生成するコードの品質とパフォーマンスを向上させるという目的があります。以前はこれらの最適化がコードベースに存在しながらも無効化されていたため、それを有効にすることで、より効率的な機械語コードの生成を目指しました。

前提知識の解説

Goコンパイラとcmd/5g

Go言語のコンパイラは、ソースコードを機械語に変換する役割を担います。cmd/5gは、Goコンパイラの一部であり、ARMアーキテクチャ(ARMv5, ARMv6, ARMv7など)向けの機械語コードを生成するバックエンドです。Goコンパイラは、フロントエンドでGoのソースコードを中間表現に変換し、その後、各アーキテクチャ固有のバックエンドがその中間表現をターゲットアーキテクチャの機械語に変換します。

アセンブリ言語と最適化

アセンブリ言語は、CPUが直接実行できる機械語命令を人間が読める形式で記述したものです。コンパイラの最適化とは、生成されるアセンブリコードのサイズを小さくしたり、実行速度を速くしたりするプロセスを指します。これには、冗長な命令の削除、命令の並べ替え、より効率的な命令への変換などが含まれます。

ARMアーキテクチャの特殊なアドレッシングモード

ARMアーキテクチャは、その効率的な命令セットで知られています。特に、以下のような特殊なアドレッシングモードは、メモリ操作と算術演算を組み合わせることで、コードのコンパクトさと実行速度を向上させます。

  1. ポストインクリメントアドレッシングモード (Post-indexed Addressing Mode):

    • これは、メモリから値をロードまたはストアした後、ベースレジスタの値を自動的に更新するモードです。
    • 例: LDR R1, [R3], #4 は、R3が指すアドレスからR1に値をロードし、その後R3の値を4だけインクリメントします。
    • このコミットで言及されているMOVW.P 4(R3),R1は、MOVW命令(ワード移動)にポストインクリメントの特性(.Pサフィックス)を付加したもので、R3が指すアドレスから4バイトオフセットした場所からR1に値をロードし、その後R3を4だけインクリメントするという一連の操作を単一の命令で実現します。これは、配列や構造体の連続した要素にアクセスする際に非常に効率的です。
  2. シフトオフセットアドレッシングモード (Shifted Register Addressing Mode):

    • これは、ベースレジスタの値に、別のレジスタの値をシフトした結果をオフセットとして加算してアドレスを計算するモードです。
    • 例: LDR R0, [R1, R2, LSL #2] は、R1の値にR2を左に2ビットシフトした値(R2 * 4)を加算したアドレスからR0に値をロードします。これは、配列のインデックスアクセス(array[i])を効率的に行うために使用されます。
    • このコミットで言及されているMOVBU R0<<0(R1),R0は、MOVBU命令(符号なしバイト移動)にシフトオフセットの特性を付加したもので、R1の値にR0を0ビットシフトした値(つまりR0そのまま)を加算したアドレスからR0にバイトをロードするという操作を単一の命令で実現します。これは、バイト配列へのアクセスなどで利用されます。

peep.cとピーフホール最適化

peep.cファイルは、Goコンパイラのバックエンドにおける「ピーフホール最適化 (Peephole Optimization)」を担当する部分です。ピーフホール最適化とは、コンパイラが生成したアセンブリコードの小さな「窓(peephole)」を覗き込み、特定の命令シーケンスをより効率的な別の命令シーケンスに置き換える最適化手法です。これは、コード生成の最終段階で行われることが多く、ターゲットアーキテクチャの特性を最大限に活用するために重要です。

技術的詳細

このコミットは、src/cmd/5g/peep.c内のpeep関数に焦点を当てています。peep関数は、生成されたアセンブリ命令列を走査し、最適化の機会を探します。

具体的には、以下の2つの主要な最適化パターンをxtramodes関数を通じて有効にします。

  1. C_PBIT 最適化 (Post-indexed Addressing Mode):

    • 元の命令シーケンス:
      MOVW          0(R3),R1  ; R3が指すアドレスからR1にワードをロード
      ADD           $4,R3,R3  ; R3に4を加算(R3をインクリメント)
      
    • 最適化後の命令:
      MOVW.P        4(R3),R1  ; R3が指すアドレスから4バイトオフセットした場所からR1にワードをロードし、R3を4だけインクリメント
      
    • この最適化は、メモリからのロードとポインタのインクリメントという2つの独立した操作を、ARMのポストインクリメントアドレッシングモードを持つ単一のMOVW.P命令に統合します。これにより、命令数が減少し、パイプラインの効率が向上し、結果として実行速度が向上します。
  2. AADD 最適化 (Shifted Register Addressing Mode):

    • 元の命令シーケンス:
      ADD          R0,R1      ; R1にR0を加算
      MOVBU        0(R1),R0   ; R1が指すアドレスからR0に符号なしバイトをロード
      
    • 最適化後の命令:
      MOVBU        R0<<0(R1),R0 ; R1にR0を0ビットシフトした値(R0そのまま)を加算したアドレスからR0に符号なしバイトをロード
      
    • この最適化は、ベースレジスタにオフセットレジスタの値をシフトして加算する操作を、ARMのシフトオフセットアドレッシングモードを持つ単一のMOVBU命令に統合します。これは、特に配列のインデックスアクセスにおいて、アドレス計算とメモリロードを効率的に行うために利用されます。

コミットメッセージには「5g does not appear to generate sequences that can be transformed by xtramodes' AMOVW」と記載されており、これはAMOVW(ワード移動)命令に関しては、5gコンパイラがxtramodesが最適化できるような特定のパターンを生成しないことを示唆しています。しかし、AMOVB(バイト移動)やAMOVBU(符号なしバイト移動)に関しては、この最適化が適用される可能性があります。

また、xtramodes関数内の変更として、if(p1->scond & C_SBIT) break;が追加されています。これは、ADD.SADC(キャリー付き加算)のような、ステータスフラグを設定するADD命令のシーケンスを最適化の対象から除外するためのものです。ステータスフラグは条件分岐などに利用されるため、これらの命令を最適化によって変更すると、プログラムの動作が変わってしまう可能性があるため、安全のために除外されています。

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

変更は主にsrc/cmd/5g/peep.cファイルに集中しています。

--- a/src/cmd/5g/peep.c
+++ b/src/cmd/5g/peep.c
@@ -49,7 +49,6 @@ peep(void)
  	int t;
  
  	p1 = nil;
- 	USED(p1);		// ... in unreachable code...
  /*
   * complete R structure
   */
@@ -120,7 +119,7 @@ loop1:
  			}
  			break;
  
-#ifdef	NOTDEF
+#ifdef NOTDEF
  		if(p->scond == C_SCOND_NONE)
  		if(regtyp(&p->to))
  		if(isdconst(&p->from)) {
@@ -175,22 +174,21 @@ loop1:
  			break;
  		}
  	}
-#ifdef	NOTDEF
-
-//	for(r=firstr; r!=R; r=r->link) {
-//		p = r->prog;
-//		switch(p->as) {
-//		case AMOVW:
-//		case AMOVB:
-//		case AMOVBU:
-//			if(p->from.type == D_OREG && p->from.offset == 0)
-//				xtramodes(r, &p->from);
-//			else
-//			if(p->to.type == D_OREG && p->to.offset == 0)
-//				xtramodes(r, &p->to);
-//			else
-//				continue;
-//			break;
-
-
+\n	for(r=firstr; r!=R; r=r->link) {\n+\t\tp = r->prog;\n+\t\tswitch(p->as) {\n+\t\tcase AMOVW:\n+\t\tcase AMOVB:\n+\t\tcase AMOVBU:\n+\t\t\tif(p->from.type == D_OREG && p->from.offset == 0)\n+\t\t\t\txtramodes(r, &p->from);\n+\t\t\telse\n+\t\t\tif(p->to.type == D_OREG && p->to.offset == 0)\n+\t\t\t\txtramodes(r, &p->to);\n+\t\t\telse\n+\t\t\t\tcontinue;\n+\t\t\tbreak;\n //		case ACMP:\n //			/*\n //			 * elide CMP $0,x if calculation of x can set condition codes\n@@ -258,13 +256,17 @@ loop1:\n //			r2->prog->as = t;\n //			excise(r);\n //			continue;\n-//		}\n-//	}\n+\t\t}\n+\t}\n \n-\tpredicate();\n-#endif\n+//	predicate();\n }\n \n+/*\n+ * uniqp returns a "unique" predecessor to instruction r.\n+ * If the instruction is the first one or has multiple\n+ * predecessors due to jump, R is returned.\n+ */\n Reg*\n uniqp(Reg *r)\n {\n@@ -737,6 +739,11 @@ shiftprop(Reg *r)\n  	return 1;\n }\n \n+/*\n+ * findpre returns the last instruction mentioning v\n+ * before r. It must be a set, and there must be\n+ * a unique path from that instruction to r.\n+ */\n Reg*\n findpre(Reg *r, Adr *v)\n {\n@@ -757,6 +764,10 @@ findpre(Reg *r, Adr *v)\n  	return R;\n }\n \n+/*\n+ * findinc finds ADD instructions with a constant\n+ * argument which falls within the immed_12 range.\n+ */\n Reg*\n findinc(Reg *r, Reg *r2, Adr *v)\n {\n@@ -847,6 +858,19 @@ finduse(Reg *r, Adr *v)\n  	return findu1(r, v);\n }\n \n+/*\n+ * xtramodes enables the ARM post increment and\n+ * shift offset addressing modes to transform\n+ *   MOVW   0(R3),R1\n+ *   ADD    $4,R3,R3\n+ * into\n+ *   MOVW.P 4(R3),R1\n+ * and \n+ *   ADD    R0,R1\n+ *   MOVBU  0(R1),R0\n+ * into \n+ *   MOVBU  R0<<0(R1),R0\n+ */\n int\n xtramodes(Reg *r, Adr *a)\n {\n@@ -855,8 +879,6 @@ xtramodes(Reg *r, Adr *a)\n  	Adr v;\n  \n  	p = r->prog;\n-\tif(debug['h'] && p->as == AMOVB && p->from.type == D_OREG)	/* byte load */\n-\t\treturn 0;\n  	v = *a;\n  	v.type = D_REG;\n  	r1 = findpre(r, &v);\n@@ -865,6 +887,9 @@ xtramodes(Reg *r, Adr *a)\n  		if(p1->to.type == D_REG && p1->to.reg == v.reg)\n  		switch(p1->as) {\n  		case AADD:\n+\t\t\tif(p1->scond & C_SBIT)\n+\t\t\t\t// avoid altering ADD.S/ADC sequences.\n+\t\t\t\tbreak;\n  		if(p1->from.type == D_REG ||\n  		   (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 &&\n  		    (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) ||\n```

## コアとなるコードの解説

1.  **`#ifdef NOTDEF` の解除**:
    *   以前は`#ifdef NOTDEF`で囲まれていたコードブロックが解除され、`peep`関数内で実行されるようになりました。このブロックは、アセンブリ命令を走査し、`AMOVW`, `AMOVB`, `AMOVBU`(ワード、バイト、符号なしバイトの移動命令)のパターンを探します。
    *   これらの命令が`D_OREG`(オフセットレジスタ)タイプのアドレスを持ち、オフセットが0の場合(例: `0(R3)`)、`xtramodes`関数が呼び出されます。これは、メモリからのロード/ストア操作とレジスタの更新を組み合わせる最適化の候補となります。

2.  **`xtramodes`関数の変更**:
    *   `if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG) return 0;`という行が削除されました。これは、デバッグモードでの特定のバイトロード操作に対する早期リターンを無効にし、より多くのケースで`xtramodes`最適化が適用されるようにします。
    *   `AADD`ケース内に`if(p1->scond & C_SBIT) break;`が追加されました。
        *   `p1->scond`は命令の条件コードフラグを示し、`C_SBIT`はSビット(ステータスフラグ設定ビット)がセットされていることを意味します。
        *   `ADD.S`や`ADC`のような命令は、演算結果に基づいてCPUのステータスフラグ(ゼロフラグ、キャリーフラグなど)を設定します。これらのフラグは後続の条件分岐命令などに影響を与えるため、最適化によってこれらの命令の動作を変更すると、プログラムのロジックが壊れる可能性があります。
        *   この変更により、ステータスフラグを設定する`ADD`命令のシーケンスは`xtramodes`最適化の対象から除外され、安全性が確保されます。

3.  **関数コメントの追加**:
    *   `uniqup`, `findpre`, `findinc`, `xtramodes`といった補助関数に詳細なコメントが追加されました。これにより、これらの関数の目的と動作が明確になり、コードの可読性と保守性が向上します。特に`xtramodes`関数のコメントは、このコミットが実現する2つの主要な最適化パターン(C_PBITとAADD)を具体的に説明しています。

これらの変更により、`5g`コンパイラはARMアーキテクチャの特性をより深く理解し、より効率的な機械語コードを生成できるようになりました。これは、GoプログラムのARMデバイス上でのパフォーマンス向上に貢献します。

## 関連リンク

*   Go言語の公式リポジトリ: [https://github.com/golang/go](https://github.com/golang/go)
*   このコミットのGerrit Change-ID: [https://golang.org/cl/6817085](https://golang.org/cl/6817085)

## 参考にした情報源リンク

*   ARM Architecture Reference Manual (ARM ARM): ARMプロセッサの命令セットとアドレッシングモードに関する詳細な情報源。
*   Go Compiler Internals: Goコンパイラの内部構造と動作に関するドキュメントや記事。
*   Peephole Optimization: コンパイラ最適化手法としてのピーフホール最適化に関する一般的な情報。
*   Dave CheneyのブログやGo関連の発表: コミットの著者であるDave CheneyはGoコミュニティで著名な開発者であり、彼のブログや発表はGoの内部実装に関する貴重な情報源となることがあります。
*   GoのIssue Tracker (Go Bug Tracker): 過去のGoのバグ報告や機能要求、議論の履歴。