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

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

このコミットは、Go言語のランタイムとコンパイラ(cmd/6g, cmd/8g)におけるレジスタの使用方法の変更と、runtime·gogocall関数の引数追加に関するものです。具体的には、間接呼び出しブロック(indirect call block)に使用するレジスタをAXからDXに変更し、runtime·gogocallにコンテキスト引数r0を追加しています。

コミット

commit 6066fdcf38bbf92bd551f74a6db4cb72306ed493
Author: Russ Cox <rsc@golang.org>
Date:   Fri Feb 22 10:47:54 2013 -0500

    cmd/6g, cmd/8g: switch to DX for indirect call block
    runtime: add context argument to gogocall
    
    Too many other things use AX, and at least one
    (stack zeroing) cannot be moved onto a different
    register. Use the less special DX instead.
    
    Preparation for step 2 of http://golang.org/s/go11func.
    Nothing interesting here, just split out so that we can
    see it's correct before moving on.
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/7395050

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

https://github.com/golang/go/commit/6066fdcf38bbf92bd551f74a6db4cb72306ed493

元コミット内容

cmd/6g, cmd/8g: switch to DX for indirect call block runtime: add context argument to gogocall

AXレジスタは多くの処理で使用されており、特にスタックのゼロクリアなど、他のレジスタに移動できない処理が存在するため、より汎用的なDXレジスタを代わりに使用するように変更しました。

これは、http://golang.org/s/go11funcのステップ2に向けた準備作業です。このコミット自体は特筆すべき変更を含んでいませんが、次のステップに進む前にこの変更が正しいことを確認するために分割されました。

変更の背景

このコミットの主な背景は、Goランタイムにおけるレジスタの競合問題と、将来的な関数呼び出し規約の変更(http://golang.org/s/go11funcで言及されているもの)への準備です。

x86アーキテクチャ(32ビットの386と64ビットのamd64)では、AXレジスタは関数からの戻り値や、特定の算術演算で暗黙的に使用されるなど、非常に多くの用途で利用されます。そのため、AXレジスタを間接呼び出しブロックのような重要な処理に固定的に使用すると、他の処理との間でレジスタの競合が発生しやすくなります。特に、スタックのゼロクリアのような低レベルの操作では、特定のレジスタの使用が必須となる場合があり、そのレジスタが他の用途で占有されていると、コードの複雑化やパフォーマンスの低下を招く可能性があります。

このコミットでは、間接呼び出しブロックにAXではなくDXレジスタを使用することで、AXレジスタの競合を緩和し、より柔軟なレジスタ割り当てを可能にしています。これは、Goランタイムの効率性と堅牢性を向上させるための、低レベルながらも重要な最適化です。

また、コミットメッセージに記載されているhttp://golang.org/s/go11funcは、Go 1.1における関数呼び出し規約の変更に関する提案を指しています。この変更は、Goの関数呼び出しのオーバーヘッドを削減し、特にクロージャやインターフェースメソッドの呼び出し性能を向上させることを目的としていました。このコミットは、その大規模な変更の一部として、レジスタ使用の準備段階として行われたものです。

前提知識の解説

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

  1. レジスタ (Registers): CPU内部にある高速な記憶領域で、CPUが現在処理しているデータや命令のアドレスを一時的に保持します。x86アーキテクチャには汎用レジスタ(AX, BX, CX, DX, SI, DI, BP, SPなど)や特殊レジスタ(PC/IPSPなど)があります。

    • AX (Accumulator Register): 算術演算の主要なレジスタとして使われることが多い。関数からの戻り値も通常ここに格納される。
    • DX (Data Register): AXと組み合わせて乗除算に使われたり、汎用データレジスタとして使われたりする。AXに比べて用途が限定的で、競合が少ない傾向がある。
    • BX (Base Register): メモリのアドレス計算に使われることが多い。
    • CX (Count Register): ループカウンタやシフト操作の回数などに使われることが多い。
    • SI (Source Index Register), DI (Destination Index Register): 文字列操作やデータ転送のソース/デスティネーションアドレスに使われることが多い。
    • SP (Stack Pointer Register): スタックの最上位アドレスを指す。
    • PC/IP (Program Counter/Instruction Pointer): 次に実行される命令のアドレスを指す。
  2. アセンブリ言語 (Assembly Language): CPUが直接理解できる機械語に1対1で対応する低レベルプログラミング言語です。レジスタの操作、メモリへのアクセス、ジャンプ命令などを直接記述します。Goランタイムの低レベル部分は、パフォーマンス最適化のためにアセンブリ言語で記述されることがあります。

    • MOVL, MOVQ, MOVW: データを移動する命令。Lは32ビット(Long)、Qは64ビット(Quad)、Wは16ビット(Word)を意味する。
    • JMP: 無条件ジャンプ。
    • TEXT: 関数の開始を宣言するディレクティブ。
    • SB: Static Base。グローバルシンボルや外部シンボルへの参照に使われる。
    • SP: Stack Pointer。現在のスタックフレームのベースからのオフセットを示す。
    • g(CX): Goのg(goroutine構造体)へのポインタをCXレジスタから取得する。Goランタイムでは、現在のgoroutineの情報をgレジスタ(TLS: Thread Local Storage経由でアクセスされる)に保持することが一般的です。
  3. Goランタイム (Go Runtime): Goプログラムの実行を管理する部分です。ガベージコレクション、スケジューラ(goroutineの管理)、メモリ割り当て、システムコールなど、Go言語の並行処理やメモリ管理の基盤を提供します。ランタイムの一部は、パフォーマンスが重要なためアセンブリ言語で実装されています。

  4. Gobuf構造体: Goランタイムで使用されるコンテキスト(実行状態)を保存するための構造体です。主にgoroutineの切り替え(コンテキストスイッチ)時に、現在のgoroutineのスタックポインタ(SP)、プログラムカウンタ(PC)、現在のg(goroutine)へのポインタなどを保存・復元するために使用されます。

  5. 間接呼び出し (Indirect Call): 呼び出す関数のアドレスが、コンパイル時ではなく実行時に決定される呼び出し方法です。関数ポインタやインターフェースメソッドの呼び出しなどで使用されます。

  6. go11func: これは、Go 1.1リリースで導入された関数呼び出し規約の変更に関する提案を指します。この変更は、Goの関数呼び出しのオーバーヘッドを削減し、特にクロージャやインターフェースメソッドの呼び出し性能を向上させることを目的としていました。具体的な内容は、関数呼び出し時のスタックフレームの構造やレジスタの使用方法の見直しを含みます。このコミットは、その大規模な変更の一部として、レジスタ使用の準備段階として行われたものです。

技術的詳細

このコミットは、主に以下の2つの技術的変更を含んでいます。

  1. 間接呼び出しブロックにおけるレジスタの変更 (AX -> DX または DI): src/cmd/6g/ggen.csrc/cmd/8g/ggen.cは、それぞれ32ビット(6g)と64ビット(8g)のGoコンパイラのコード生成部分です。これらのファイルでは、ginscall関数内で間接呼び出しを行う際に、関数ポインタを格納するレジスタをD_AXAXレジスタ)からD_DXDXレジスタ)に変更しています。 また、src/cmd/8l/pass.cでは、スタックフレームサイズを保存するレジスタがD_DXからD_DIに変更されています。これは、DXレジスタも他の用途で使用される可能性があるため、さらに競合の少ないDIレジスタに移動したことを示唆しています。

    この変更の理由は、コミットメッセージにある通り「Too many other things use AX, and at least one (stack zeroing) cannot be moved onto a different register. Use the less special DX instead.」(AXレジスタはあまりにも多くの場所で使われており、少なくとも1つ(スタックのゼロクリア)は別のレジスタに移動できない。代わりに、より特殊性の低いDXを使う。)というものです。これにより、AXレジスタの競合が緩和され、コンパイラがより効率的にレジスタを割り当てられるようになります。

  2. runtime·gogocall関数へのコンテキスト引数 (uintptr r0) の追加: src/pkg/runtime/asm_386.ssrc/pkg/runtime/asm_amd64.ssrc/pkg/runtime/asm_arm.sは、それぞれ32ビットx86、64ビットx86、ARMアーキテクチャ向けのアセンブリコードです。これらのファイルでは、runtime·gogocallおよびruntime·gogocallfn関数のシグネチャが変更され、新たにuintptr r0というコンテキスト引数が追加されています。

    • runtime·gogocall(Gobuf*, void (*fn)(void)) から runtime·gogocall(Gobuf*, void (*fn)(void), uintptr r0)
    • このr0引数は、gogocallが呼び出す関数に渡される追加のコンテキスト情報として機能します。アセンブリコードでは、このr0DXレジスタ(386/amd64)またはR0レジスタ(ARM)にロードされ、m_cret(BX)m構造体のcretフィールド)に保存されています。
    • m_cretは、m(machine/thread)構造体の一部であり、おそらく関数呼び出しのコンテキストや戻り値を一時的に保持するためのフィールドです。
    • runtime/runtime.hの関数プロトタイプもこれに合わせて更新されています。
    • src/pkg/runtime/stack.cruntime·newstack関数では、runtime·gogocallの呼び出し時にm->cretが新しい引数として渡されるようになっています。

    この変更は、http://golang.org/s/go11funcで示唆されている関数呼び出し規約の変更と密接に関連しています。新しい呼び出し規約では、関数に渡される引数やコンテキストの管理方法が変更される可能性があり、このr0引数の追加はそのための準備と考えられます。特に、スタックの拡張や縮小(morestack/lessstack)といったランタイムの低レベルな処理において、追加のコンテキスト情報が必要になる場合があります。

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

このコミットで変更された主要なファイルとコードの変更点は以下の通りです。

  • src/cmd/6g/ggen.c および src/cmd/8g/ggen.c:

    • ginscall関数内で、間接呼び出しに使用するレジスタをD_AXからD_DXに変更。
      --- a/src/cmd/6g/ggen.c
      +++ b/src/cmd/6g/ggen.c
      @@ -77,7 +77,7 @@ ginscall(Node *f, int proc)\
       			tgins(AUNDEF, N, N);\
       		break;\
       	}\
      -	tnodreg(&reg, types[tptr], D_AX);\
      +	tnodreg(&reg, types[tptr], D_DX);\
       	tnodreg(&r1, types[tptr], D_BX);\
       	tgmove(f, &reg);\
       	reg.op = OINDREG;
      
  • src/cmd/8l/pass.c:

    • スタックフレームサイズを保存するレジスタをD_DXからD_DIに変更。
      --- a/src/cmd/8l/pass.c
      +++ b/src/cmd/8l/pass.c
      @@ -539,9 +539,9 @@ dostkoff(void)\
       			tq = p;\
       		}\
       
      -		tp = appendp(p);	// save frame size in DX
      +		tp = appendp(p);	// save frame size in DI
       		tp->as = AMOVL;\
      -		tp->to.type = D_DX;\
      +		tp->to.type = D_DI;\
       		tp->from.type = D_CONST;
       
       		// If we ask for more stack, we'll get a minimum of StackMin bytes.
      
  • src/pkg/runtime/asm_386.s (32-bit x86 アセンブリ):

    • runtime·gogocallおよびruntime·gogocallfnのシグネチャ変更と、DXレジスタへのコンテキスト引数r0のロード、gobuf_gのロード先レジスタをDXからDIへ変更、g(CX)へのストア元レジスタをDXからDIへ変更。
    • runtime·morestackm_cret(BX)DXを保存する行を追加。
    • runtime·jmpdeferAXからDXへのレジスタ変更。
      --- a/src/pkg/runtime/asm_386.s
      +++ b/src/pkg/runtime/asm_386.s
      @@ -134,16 +134,17 @@ TEXT runtime·gogo(SB), 7, $0
       	MOVL	gobuf_pc(BX), BX
       	JMP	BX
       
      -// void gogocall(Gobuf*, void (*fn)(void))
      +// void gogocall(Gobuf*, void (*fn)(void), uintptr r0)
       // restore state from Gobuf but then call fn.
       // (call fn, returning to state in Gobuf)
       TEXT runtime·gogocall(SB), 7, $0
      +	MOVL	12(SP), DX	// context
       	MOVL	8(SP), AX		// fn
       	MOVL	4(SP), BX		// gobuf
      -	MOVL	gobuf_g(BX), DX
      +	MOVL	gobuf_g(BX), DI
       	get_tls(CX)
      -	MOVL	DX, g(CX)
      -	MOVL	0(DX), CX		// make sure g != nil
      +	MOVL	DI, g(CX)
      +	MOVL	0(DI), CX		// make sure g != nil
       	MOVL	gobuf_sp(BX), SP	// restore SP
       	MOVL	gobuf_pc(BX), BX
       	PUSHL	BX
      @@ -154,16 +155,16 @@ TEXT runtime·gogocall(SB), 7, $0
       // restore state from Gobuf but then call fn.
       // (call fn, returning to state in Gobuf)
       TEXT runtime·gogocallfn(SB), 7, $0
      -	MOVL	8(SP), AX		// fn
      +	MOVL	8(SP), DX		// fn
       	MOVL	4(SP), BX		// gobuf
      -	MOVL	gobuf_g(BX), DX
      +	MOVL	gobuf_g(BX), DI
       	get_tls(CX)
      -	MOVL	DX, g(CX)
      -	MOVL	0(DX), CX		// make sure g != nil
      +	MOVL	DI, g(CX)
      +	MOVL	0(DI), CX		// make sure g != nil
       	MOVL	gobuf_sp(BX), SP	// restore SP
       	MOVL	gobuf_pc(BX), BX
       	PUSHL	BX
      -	MOVL	0(AX), BX
      +	MOVL	0(DX), BX
       	JMP	BX
       	POPL	BX	// not reached
       
      @@ -209,11 +210,13 @@ TEXT runtime·morestack(SB),7,$0
       	CMPL	g(CX), SI
       	JNE	2(PC)
       	INT	$3
      +	
      +	MOVL	DX, m_cret(BX)
       
      -	// frame size in DX
      +	// frame size in DI
       	// arg size in AX
       	// Save in m.
      -	MOVL	DX, m_moreframesize(BX)
      +	MOVL	DI, m_moreframesize(BX)
       	MOVL	AX, m_moreargsize(BX)
       
       	// Called from f.
      @@ -441,11 +444,11 @@ TEXT runtime·atomicstore64(SB), 7, $0
       // 2. sub 5 bytes from the callers return
       // 3. jmp to the argument
       TEXT runtime·jmpdefer(SB), 7, $0
      -	MOVL	4(SP), AX	// fn
      +	MOVL	4(SP), DX	// fn
       	MOVL	8(SP), BX	// caller sp
       	LEAL	-4(BX), SP	// caller sp after CALL
       	SUBL	$5, (SP)	// return to CALL again
      -	MOVL	0(AX), BX
      +	MOVL	0(DX), BX
       	JMP	BX	// but first run the deferred function
       
       // Dummy function to use in saved gobuf.PC,
      
  • src/pkg/runtime/asm_amd64.s (64-bit x86 アセンブリ):

    • runtime·gogocallおよびruntime·gogocallfnのシグネチャ変更と、DXレジスタへのコンテキスト引数r0のロード、gobuf_gのロード先レジスタをDXからDIへ変更、g(CX)へのストア元レジスタをDXからAXへ変更。
    • runtime·morestackm_cret(BX)DXを保存する行を追加。
    • runtime·jmpdeferAXからDXへのレジスタ変更。
      --- a/src/pkg/runtime/asm_amd64.s
      +++ b/src/pkg/runtime/asm_amd64.s
      @@ -121,16 +121,17 @@ TEXT runtime·gogo(SB), 7, $0
       	MOVQ	gobuf_pc(BX), BX
       	JMP	BX
       
      -// void gogocall(Gobuf*, void (*fn)(void))
      +// void gogocall(Gobuf*, void (*fn)(void), uintptr r0)
       // restore state from Gobuf but then call fn.
       // (call fn, returning to state in Gobuf)
       TEXT runtime·gogocall(SB), 7, $0
      +	MOVQ	24(SP), DX	// context
       	MOVQ	16(SP), AX		// fn
       	MOVQ	8(SP), BX		// gobuf
      -	MOVQ	gobuf_g(BX), DX
      +	MOVQ	gobuf_g(BX), DI
       	get_tls(CX)
      -	MOVQ	DX, g(CX)
      -	MOVQ	0(DX), CX	// make sure g != nil
      +	MOVQ	DI, g(CX)
      +	MOVQ	0(DI), CX	// make sure g != nil
       	MOVQ	gobuf_sp(BX), SP	// restore SP
       	MOVQ	gobuf_pc(BX), BX
       	PUSHQ	BX
      @@ -141,16 +142,16 @@ TEXT runtime·gogocall(SB), 7, $0
       // restore state from Gobuf but then call fn.
       // (call fn, returning to state in Gobuf)
       TEXT runtime·gogocallfn(SB), 7, $0
      -	MOVQ	16(SP), AX		// fn
      +	MOVQ	16(SP), DX		// fn
       	MOVQ	8(SP), BX		// gobuf
      -	MOVQ	gobuf_g(BX), DX
      +	MOVQ	gobuf_g(BX), AX
       	get_tls(CX)
      -	MOVQ	DX, g(CX)
      -	MOVQ	0(DX), CX	// make sure g != nil
      +	MOVQ	AX, g(CX)
      +	MOVQ	0(AX), CX	// make sure g != nil
       	MOVQ	gobuf_sp(BX), SP	// restore SP
       	MOVQ	gobuf_pc(BX), BX
       	PUSHQ	BX
      -	MOVQ	0(AX), BX
      +	MOVQ	0(DX), BX
       	JMP	BX
       	POPQ	BX	// not reached
       
      @@ -195,6 +196,8 @@ TEXT runtime·morestack(SB),7,$0
       	CMPQ	g(CX), SI
       	JNE	2(PC)
       	INT	$3
      +	
      +	MOVQ	DX, m_cret(BX)
       
       	// Called from f.
       	// Set m->morebuf to f's caller.
      @@ -471,11 +474,11 @@ TEXT runtime·atomicstore64(SB), 7, $0
       // 2. sub 5 bytes from the callers return
       // 3. jmp to the argument
       TEXT runtime·jmpdefer(SB), 7, $0
      -	MOVQ	8(SP), AX	// fn
      +	MOVQ	8(SP), DX	// fn
       	MOVQ	16(SP), BX	// caller sp
       	LEAQ	-8(BX), SP	// caller sp after CALL
       	SUBQ	$5, (SP)	// return to CALL again
      -	MOVQ	0(AX), BX
      +	MOVQ	0(DX), BX
       	JMP	BX	// but first run the deferred function
       
       // Dummy function to use in saved gobuf.PC,
      
  • src/pkg/runtime/asm_arm.s (ARM アセンブリ):

    • runtime·gogocallのシグネチャ変更と、R0レジスタへのコンテキスト引数r0のロード。
    • runtime·morestackm_cret(m)R0を保存する行を追加。
      --- a/src/pkg/runtime/asm_arm.s
      +++ b/src/pkg/runtime/asm_arm.s
      @@ -112,19 +112,19 @@ TEXT runtime·gogo(SB), 7, $-4
       	MOVW	gobuf_sp(R1), SP	// restore SP
       	MOVW	gobuf_pc(R1), PC
       
      -// void gogocall(Gobuf*, void (*fn)(void))
      +// void gogocall(Gobuf*, void (*fn)(void), uintptr r0)
       // restore state from Gobuf but then call fn.
       // (call fn, returning to state in Gobuf)
       // using frame size $-4 means do not save LR on stack.
       TEXT runtime·gogocall(SB), 7, $-4
       	MOVW	0(FP), R3		// gobuf
       	MOVW	4(FP), R1		// fn
      -	MOVW	8(FP), R2		// fp offset
       	MOVW	gobuf_g(R3), g
       	MOVW	0(g), R0		// make sure g != nil
       	MOVW	cgo_save_gm(SB), R0
       	CMP 	$0, R0 // if in Cgo, we have to save g and m
       	BL.NE	(R0) // this call will clobber R0
      +	MOVW	8(FP), R0	// context
       	MOVW	gobuf_sp(R3), SP	// restore SP
       	MOVW	gobuf_pc(R3), LR
       	MOVW	R1, PC
      @@ -136,7 +136,6 @@ TEXT runtime·gogocall(SB), 7, $-4
       TEXT runtime·gogocallfn(SB), 7, $-4
       	MOVW	0(FP), R3		// gobuf
       	MOVW	4(FP), R1		// fn
      -	MOVW	8(FP), R2		// fp offset
       	MOVW	gobuf_g(R3), g
       	MOVW	0(g), R0		// make sure g != nil
       	MOVW	cgo_save_gm(SB), R0
      @@ -189,6 +188,7 @@ TEXT runtime·morestack(SB),7,$-4
       	BL.EQ	runtime·abort(SB)
       
       	// Save in m.
      +	MOVW	R0, m_cret(m) // function context
       	MOVW	R1, m_moreframesize(m)
       	MOVW	R2, m_moreargsize(m)
       
      
  • src/pkg/runtime/runtime.h:

    • runtime·gogocall関数のプロトタイプ宣言を更新。
      --- a/src/pkg/runtime/runtime.h
      +++ b/src/pkg/runtime/runtime.h
      @@ -615,7 +615,7 @@ int32	runtime·charntorune(int32*, uint8*, int32);
       #define FLUSH(x)	USED(x)
       
       void	runtime·gogo(Gobuf*, uintptr);
      -void	runtime·gogocall(Gobuf*, void(*)(void));
      +void	runtime·gogocall(Gobuf*, void(*)(void), uintptr);
       void	runtime·gogocallfn(Gobuf*, FuncVal*);
       void	runtime·gosave(Gobuf*);
       void	runtime·lessstack(void);
      
  • src/pkg/runtime/stack.c:

    • runtime·newstack関数内でruntime·gogocallを呼び出す際に、m->cretを新しい引数として渡すように変更。
      --- a/src/pkg/runtime/stack.c
      +++ b/src/pkg/runtime/stack.c
      @@ -276,7 +276,7 @@ runtime·newstack(void)\
       	if(reflectcall)\
       		runtime·gogocallfn(&label, (FuncVal*)m->morepc);\
       	else\
      -		runtime·gogocall(&label, m->morepc);\
      +		runtime·gogocall(&label, m->morepc, m->cret);\
       
       	*(int32*)345 = 123;	// never return
       }
      

コアとなるコードの解説

このコミットの核となる変更は、Goランタイムが関数呼び出しのコンテキストをどのように管理し、レジスタをどのように利用するかという点にあります。

  1. レジスタの再割り当て: コンパイラ(6g, 8g)が生成するコードにおいて、間接呼び出しのアドレスを保持するレジスタがAXからDX(またはDI)に変更されました。これは、AXレジスタがGoランタイムの他の重要な部分(特にスタックのゼロクリア)で不可欠であり、その使用を減らすことでレジスタの競合を避け、より効率的なコード生成を可能にするためです。低レベルのアセンブリコードでは、レジスタの選択はパフォーマンスに直接影響するため、このような変更は重要です。

  2. runtime·gogocallへのコンテキスト引数追加: runtime·gogocallは、Goランタイムがgoroutineのコンテキストを切り替える際に使用する低レベルの関数です。この関数にuintptr r0という新しい引数が追加されたことは、Goランタイムが関数呼び出しの際に、より多くのコンテキスト情報を渡す必要があることを示唆しています。

    • gogocallは、Gobuf構造体から保存された状態(スタックポインタ、プログラムカウンタなど)を復元し、指定された関数fnを呼び出します。
    • 追加されたr0引数は、アセンブリコード内でDX(x86)またはR0(ARM)レジスタにロードされ、最終的にm構造体のcretフィールドに保存されます。
    • m構造体は、GoランタイムにおけるOSスレッド(M: Machine)の情報を保持しており、cretフィールドは「call return context」のような意味合いを持つと考えられます。これは、関数呼び出しの戻り値や、呼び出し元のコンテキストに関する追加情報がここに格納されることを示唆しています。
    • runtime·newstack関数(スタックの拡張が必要な場合に呼び出される)がgogocallを呼び出す際に、m->cretを渡すようになったことから、スタックの管理や関数呼び出しの際に、このcretが重要な役割を果たすことがわかります。

これらの変更は、Go 1.1で導入された新しい関数呼び出し規約(go11func)の準備段階として行われました。この規約変更は、Goの関数呼び出しのオーバーヘッドを削減し、特にクロージャやインターフェースメソッドの呼び出し性能を向上させることを目的としていました。レジスタの効率的な利用と、関数呼び出しコンテキストのより柔軟な管理は、これらの目標達成に不可欠な要素です。

関連リンク

  • Go 1.1 Release Notes: Go 1.1のリリースノートには、このコミットが関連する関数呼び出し規約の変更やパフォーマンス改善に関する情報が含まれている可能性があります。
  • Go Assembly Language: Goのアセンブリ言語に関する公式ドキュメントやチュートリアル。
  • Go Runtime Source Code: Goランタイムのソースコードは、これらの低レベルな変更の全体像を理解する上で不可欠です。

参考にした情報源リンク

  • http://golang.org/s/go11func: このURLは、Go 1.1における関数呼び出し規約の変更に関する提案ドキュメントを指しています。このコミットの背景を深く理解するために最も重要な情報源です。
  • x86 Instruction Set Reference: x86アーキテクチャのレジスタと命令に関する詳細な情報。
    • Intel 64 and IA-32 Architectures Software Developer's Manuals (Intelの公式ドキュメント)
  • ARM Architecture Reference Manual: ARMアーキテクチャのレジスタと命令に関する詳細な情報。
    • ARM Architecture Reference Manual (ARMの公式ドキュメント)
  • Goのソースコード: 特にsrc/cmd/, src/pkg/runtime/以下のファイル。
  • GoのIssue Tracker: 関連するバグ報告や機能リクエスト。
  • GoのCode Reviewシステム (Gerrit): コミットメッセージに記載されているhttps://golang.org/cl/7395050は、GoのGerritコードレビューシステムへのリンクです。ここには、この変更に関する議論や追加のコンテキストが含まれている可能性があります。
  • Goのメーリングリスト (golang-dev): 開発者間の議論。

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

このコミットは、Go言語のランタイムとコンパイラ(cmd/6g, cmd/8g)におけるレジスタの使用方法の変更と、runtime·gogocall関数の引数追加に関するものです。具体的には、間接呼び出しブロック(indirect call block)に使用するレジスタをAXからDXに変更し、runtime·gogocallにコンテキスト引数r0を追加しています。

コミット

commit 6066fdcf38bbf92bd551f74a6db4cb72306ed493
Author: Russ Cox <rsc@golang.org>
Date:   Fri Feb 22 10:47:54 2013 -0500

    cmd/6g, cmd/8g: switch to DX for indirect call block
    runtime: add context argument to gogocall
    
    Too many other things use AX, and at least one
    (stack zeroing) cannot be moved onto a different
    register. Use the less special DX instead.
    
    Preparation for step 2 of http://golang.org/s/go11func.
    Nothing interesting here, just split out so that we can
    see it's correct before moving on.
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/7395050

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

https://github.com/golang/go/commit/6066fdcf38bbf92bd551f74a6db4cb72306ed493

元コミット内容

cmd/6g, cmd/8g: switch to DX for indirect call block runtime: add context argument to gogocall

AXレジスタは多くの処理で使用されており、特にスタックのゼロクリアなど、他のレジスタに移動できない処理が存在するため、より汎用的なDXレジスタを代わりに使用するように変更しました。

これは、http://golang.org/s/go11funcのステップ2に向けた準備作業です。このコミット自体は特筆すべき変更を含んでいませんが、次のステップに進む前にこの変更が正しいことを確認するために分割されました。

変更の背景

このコミットの主な背景は、Goランタイムにおけるレジスタの競合問題と、将来的な関数呼び出し規約の変更(http://golang.org/s/go11funcで言及されているもの)への準備です。

x86アーキテクチャ(32ビットの386と64ビットのamd64)では、AXレジスタは関数からの戻り値や、特定の算術演算で暗黙的に使用されるなど、非常に多くの用途で利用されます。そのため、AXレジスタを間接呼び出しブロックのような重要な処理に固定的に使用すると、他の処理との間でレジスタの競合が発生しやすくなります。特に、スタックのゼロクリアのような低レベルの操作では、特定のレジスタの使用が必須となる場合があり、そのレジスタが他の用途で占有されていると、コードの複雑化やパフォーマンスの低下を招く可能性があります。

このコミットでは、間接呼び出しブロックにAXではなくDXレジスタを使用することで、AXレジスタの競合を緩和し、より柔軟なレジスタ割り当てを可能にしています。これは、Goランタイムの効率性と堅牢性を向上させるための、低レベルながらも重要な最適化です。

また、コミットメッセージに記載されているhttp://golang.org/s/go11funcは、Go 1.1における関数呼び出し規約の変更に関する提案を指しています。この変更は、Goの関数呼び出しのオーバーヘッドを削減し、特にクロージャやインターフェースメソッドの呼び出し性能を向上させることを目的としていました。このコミットは、その大規模な変更の一部として、レジスタ使用の準備段階として行われたものです。

前提知識の解説

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

  1. レジスタ (Registers): CPU内部にある高速な記憶領域で、CPUが現在処理しているデータや命令のアドレスを一時的に保持します。x86アーキテクチャには汎用レジスタ(AX, BX, CX, DX, SI, DI, BP, SPなど)や特殊レジスタ(PC/IPSPなど)があります。

    • AX (Accumulator Register): 算術演算の主要なレジスタとして使われることが多い。関数からの戻り値も通常ここに格納される。
    • DX (Data Register): AXと組み合わせて乗除算に使われたり、汎用データレジスタとして使われたりする。AXに比べて用途が限定的で、競合が少ない傾向がある。
    • BX (Base Register): メモリのアドレス計算に使われることが多い。
    • CX (Count Register): ループカウンタやシフト操作の回数などに使われることが多い。
    • SI (Source Index Register), DI (Destination Index Register): 文字列操作やデータ転送のソース/デスティネーションアドレスに使われることが多い。
    • SP (Stack Pointer Register): スタックの最上位アドレスを指す。
    • PC/IP (Program Counter/Instruction Pointer): 次に実行される命令のアドレスを指す。
  2. アセンブリ言語 (Assembly Language): CPUが直接理解できる機械語に1対1で対応する低レベルプログラミング言語です。レジスタの操作、メモリへのアクセス、ジャンプ命令などを直接記述します。Goランタイムの低レベル部分は、パフォーマンス最適化のためにアセンブリ言語で記述されることがあります。

    • MOVL, MOVQ, MOVW: データを移動する命令。Lは32ビット(Long)、Qは64ビット(Quad)、Wは16ビット(Word)を意味する。
    • JMP: 無条件ジャンプ。
    • TEXT: 関数の開始を宣言するディレクティブ。
    • SB: Static Base。グローバルシンボルや外部シンボルへの参照に使われる。
    • SP: Stack Pointer。現在のスタックフレームのベースからのオフセットを示す。
    • g(CX): Goのg(goroutine構造体)へのポインタをCXレジスタから取得する。Goランタイムでは、現在のgoroutineの情報をgレジスタ(TLS: Thread Local Storage経由でアクセスされる)に保持することが一般的です。
  3. Goランタイム (Go Runtime): Goプログラムの実行を管理する部分です。ガベージコレクション、スケジューラ(goroutineの管理)、メモリ割り当て、システムコールなど、Go言語の並行処理やメモリ管理の基盤を提供します。ランタイムの一部は、パフォーマンスが重要なためアセンブリ言語で実装されています。

  4. Gobuf構造体: Goランタイムで使用されるコンテキスト(実行状態)を保存するための構造体です。主にgoroutineの切り替え(コンテキストスイッチ)時に、現在のgoroutineのスタックポインタ(SP)、プログラムカウンタ(PC)、現在のg(goroutine)へのポインタなどを保存・復元するために使用されます。

  5. 間接呼び出し (Indirect Call): 呼び出す関数のアドレスが、コンパイル時ではなく実行時に決定される呼び出し方法です。関数ポインタやインターフェースメソッドの呼び出しなどで使用されます。

  6. go11func: これは、Go 1.1リリースで導入された関数呼び出し規約の変更に関する提案を指します。この変更は、Goの関数呼び出しのオーバーヘッドを削減し、特にクロージャやインターフェースメソッドの呼び出し性能を向上させることを目的としていました。具体的な内容は、関数呼び出し時のスタックフレームの構造やレジスタの使用方法の見直しを含みます。このコミットは、その大規模な変更の一部として、レジスタ使用の準備段階として行われたものです。

技術的詳細

このコミットは、主に以下の2つの技術的変更を含んでいます。

  1. 間接呼び出しブロックにおけるレジスタの変更 (AX -> DX または DI): src/cmd/6g/ggen.csrc/cmd/8g/ggen.cは、それぞれ32ビット(6g)と64ビット(8g)のGoコンパイラのコード生成部分です。これらのファイルでは、ginscall関数内で間接呼び出しを行う際に、関数ポインタを格納するレジスタをD_AXAXレジスタ)からD_DXDXレジスタ)に変更しています。 また、src/cmd/8l/pass.cでは、スタックフレームサイズを保存するレジスタがD_DXからD_DIに変更されています。これは、DXレジスタも他の用途で使用される可能性があるため、さらに競合の少ないDIレジスタに移動したことを示唆しています。

    この変更の理由は、コミットメッセージにある通り「Too many other things use AX, and at least one (stack zeroing) cannot be moved onto a different register. Use the less special DX instead.」(AXレジスタはあまりにも多くの場所で使われており、少なくとも1つ(スタックのゼロクリア)は別のレジスタに移動できない。代わりに、より特殊性の低いDXを使う。)というものです。これにより、AXレジスタの競合が緩和され、コンパイラがより効率的にレジスタを割り当てられるようになります。

  2. runtime·gogocall関数へのコンテキスト引数 (uintptr r0) の追加: src/pkg/runtime/asm_386.ssrc/pkg/runtime/asm_amd64.ssrc/pkg/runtime/asm_arm.sは、それぞれ32ビットx86、64ビットx86、ARMアーキテクチャ向けのアセンブリコードです。これらのファイルでは、runtime·gogocallおよびruntime·gogocallfn関数のシグネチャが変更され、新たにuintptr r0というコンテキスト引数が追加されています。

    • runtime·gogocall(Gobuf*, void (*fn)(void)) から runtime·gogocall(Gobuf*, void (*fn)(void), uintptr r0)
    • このr0引数は、gogocallが呼び出す関数に渡される追加のコンテキスト情報として機能します。アセンブリコードでは、このr0DXレジスタ(386/amd64)またはR0レジスタ(ARM)にロードされ、m_cret(BX)m構造体のcretフィールド)に保存されています。
    • m_cretは、m(machine/thread)構造体の一部であり、おそらく関数呼び出しのコンテキストや戻り値を一時的に保持するためのフィールドです。
    • runtime/runtime.hの関数プロトタイプもこれに合わせて更新されています。
    • src/pkg/runtime/stack.cruntime·newstack関数では、runtime·gogocallの呼び出し時にm->cretが新しい引数として渡されるようになっています。

    この変更は、http://golang.org/s/go11funcで示唆されている関数呼び出し規約の変更と密接に関連しています。新しい呼び出し規約では、関数に渡される引数やコンテキストの管理方法が変更される可能性があり、このr0引数の追加はそのための準備と考えられます。特に、スタックの拡張や縮小(morestack/lessstack)といったランタイムの低レベルな処理において、追加のコンテキスト情報が必要になる場合があります。

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

このコミットで変更された主要なファイルとコードの変更点は以下の通りです。

  • src/cmd/6g/ggen.c および src/cmd/8g/ggen.c:

    • ginscall関数内で、間接呼び出しに使用するレジスタをD_AXからD_DXに変更。
      --- a/src/cmd/6g/ggen.c
      +++ b/src/cmd/6g/ggen.c
      @@ -77,7 +77,7 @@ ginscall(Node *f, int proc)\
       			tgins(AUNDEF, N, N);\
       		break;\
       	}\
      -	tnodreg(&reg, types[tptr], D_AX);\
      +	tnodreg(&reg, types[tptr], D_DX);\
       	tnodreg(&r1, types[tptr], D_BX);\
       	tgmove(f, &reg);\
       	reg.op = OINDREG;
      
  • src/cmd/8l/pass.c:

    • スタックフレームサイズを保存するレジスタをD_DXからD_DIに変更。
      --- a/src/cmd/8l/pass.c
      +++ b/src/cmd/8l/pass.c
      @@ -539,9 +539,9 @@ dostkoff(void)\
       			tq = p;\
       		}\
       
      -		tp = appendp(p);	// save frame size in DX
      +		tp = appendp(p);	// save frame size in DI
       		tp->as = AMOVL;\
      -		tp->to.type = D_DX;\
      +		tp->to.type = D_DI;\
       		tp->from.type = D_CONST;
       
       		// If we ask for more stack, we'll get a minimum of StackMin bytes.
      
  • src/pkg/runtime/asm_386.s (32-bit x86 アセンブリ):

    • runtime·gogocallおよびruntime·gogocallfnのシグネチャ変更と、DXレジスタへのコンテキスト引数r0のロード、gobuf_gのロード先レジスタをDXからDIへ変更、g(CX)へのストア元レジスタをDXからDIへ変更。
    • runtime·morestackm_cret(BX)DXを保存する行を追加。
    • runtime·jmpdeferAXからDXへのレジスタ変更。
      --- a/src/pkg/runtime/asm_386.s
      +++ b/src/pkg/runtime/asm_386.s
      @@ -134,16 +134,17 @@ TEXT runtime·gogo(SB), 7, $0
       	MOVL	gobuf_pc(BX), BX
       	JMP	BX
       
      -// void gogocall(Gobuf*, void (*fn)(void))
      +// void gogocall(Gobuf*, void (*fn)(void), uintptr r0)
       // restore state from Gobuf but then call fn.
       // (call fn, returning to state in Gobuf)
       TEXT runtime·gogocall(SB), 7, $0
      +	MOVL	12(SP), DX	// context
       	MOVL	8(SP), AX		// fn
       	MOVL	4(SP), BX		// gobuf
      -	MOVL	gobuf_g(BX), DX
      +	MOVL	gobuf_g(BX), DI
       	get_tls(CX)
      -	MOVL	DX, g(CX)
      -	MOVL	0(DX), CX		// make sure g != nil
      +	MOVL	DI, g(CX)
      +	MOVL	0(DI), CX		// make sure g != nil
       	MOVL	gobuf_sp(BX), SP	// restore SP
       	MOVL	gobuf_pc(BX), BX
       	PUSHL	BX
      @@ -154,16 +155,16 @@ TEXT runtime·gogocall(SB), 7, $0
       // restore state from Gobuf but then call fn.
       // (call fn, returning to state in Gobuf)
       TEXT runtime·gogocallfn(SB), 7, $0
      -	MOVL	8(SP), AX		// fn
      +	MOVL	8(SP), DX		// fn
       	MOVL	4(SP), BX		// gobuf
      -	MOVL	gobuf_g(BX), DX
      +	MOVL	gobuf_g(BX), DI
       	get_tls(CX)
      -	MOVL	DX, g(CX)
      -	MOVL	0(DX), CX		// make sure g != nil
      +	MOVL	DI, g(CX)
      +	MOVL	0(DI), CX		// make sure g != nil
       	MOVL	gobuf_sp(BX), SP	// restore SP
       	MOVL	gobuf_pc(BX), BX
       	PUSHL	BX
      -	MOVL	0(AX), BX
      +	MOVL	0(DX), BX
       	JMP	BX
       	POPL	BX	// not reached
       
      @@ -209,11 +210,13 @@ TEXT runtime·morestack(SB),7,$0
       	CMPL	g(CX), SI
       	JNE	2(PC)
       	INT	$3
      +	
      +	MOVL	DX, m_cret(BX)
       
      -	// frame size in DX
      +	// frame size in DI
       	// arg size in AX
       	// Save in m.
      -	MOVL	DX, m_moreframesize(BX)
      +	MOVL	DI, m_moreframesize(BX)
       	MOVL	AX, m_moreargsize(BX)
       
       	// Called from f.
      @@ -441,11 +444,11 @@ TEXT runtime·atomicstore64(SB), 7, $0
       // 2. sub 5 bytes from the callers return
       // 3. jmp to the argument
       TEXT runtime·jmpdefer(SB), 7, $0
      -	MOVL	4(SP), AX	// fn
      +	MOVL	4(SP), DX	// fn
       	MOVL	8(SP), BX	// caller sp
       	LEAL	-4(BX), SP	// caller sp after CALL
       	SUBL	$5, (SP)	// return to CALL again
      -	MOVL	0(AX), BX
      +	MOVL	0(DX), BX
       	JMP	BX	// but first run the deferred function
       
       // Dummy function to use in saved gobuf.PC,
      
  • src/pkg/runtime/asm_amd64.s (64-bit x86 アセンブリ):

    • runtime·gogocallおよびruntime·gogocallfnのシグネチャ変更と、DXレジスタへのコンテキスト引数r0のロード、gobuf_gのロード先レジスタをDXからDIへ変更、g(CX)へのストア元レジスタをDXからAXへ変更。
    • runtime·morestackm_cret(BX)DXを保存する行を追加。
    • runtime·jmpdeferAXからDXへのレジスタ変更。
      --- a/src/pkg/runtime/asm_amd64.s
      +++ b/src/pkg/runtime/asm_amd64.s
      @@ -121,16 +121,17 @@ TEXT runtime·gogo(SB), 7, $0
       	MOVQ	gobuf_pc(BX), BX
       	JMP	BX
       
      -// void gogocall(Gobuf*, void (*fn)(void))
      +// void gogocall(Gobuf*, void (*fn)(void), uintptr r0)
       // restore state from Gobuf but then call fn.
       // (call fn, returning to state in Gobuf)
       TEXT runtime·gogocall(SB), 7, $0
      +	MOVQ	24(SP), DX	// context
       	MOVQ	16(SP), AX		// fn
       	MOVQ	8(SP), BX		// gobuf
      -	MOVQ	gobuf_g(BX), DX
      +	MOVQ	gobuf_g(BX), DI
       	get_tls(CX)
      -	MOVQ	DX, g(CX)
      -	MOVQ	0(DX), CX	// make sure g != nil
      +	MOVQ	DI, g(CX)
      +	MOVQ	0(DI), CX	// make sure g != nil
       	MOVQ	gobuf_sp(BX), SP	// restore SP
       	MOVQ	gobuf_pc(BX), BX
       	PUSHQ	BX
      @@ -141,16 +142,16 @@ TEXT runtime·gogocall(SB), 7, $0
       // restore state from Gobuf but then call fn.
       // (call fn, returning to state in Gobuf)
       TEXT runtime·gogocallfn(SB), 7, $0
      -	MOVQ	16(SP), AX		// fn
      +	MOVQ	16(SP), DX		// fn
       	MOVQ	8(SP), BX		// gobuf
      -	MOVQ	gobuf_g(BX), DX
      +	MOVQ	gobuf_g(BX), AX
       	get_tls(CX)
      -	MOVQ	DX, g(CX)
      -	MOVQ	0(DX), CX	// make sure g != nil
      +	MOVQ	AX, g(CX)
      +	MOVQ	0(AX), CX	// make sure g != nil
       	MOVQ	gobuf_sp(BX), SP	// restore SP
       	MOVQ	gobuf_pc(BX), BX
       	PUSHQ	BX
      -	MOVQ	0(AX), BX
      +	MOVQ	0(DX), BX
       	JMP	BX
       	POPQ	BX	// not reached
       
      @@ -195,6 +196,8 @@ TEXT runtime·morestack(SB),7,$0
       	CMPQ	g(CX), SI
       	JNE	2(PC)
       	INT	$3
      +	
      +	MOVQ	DX, m_cret(BX)
       
       	// Called from f.
       	// Set m->morebuf to f's caller.
      @@ -471,11 +474,11 @@ TEXT runtime·atomicstore64(SB), 7, $0
       // 2. sub 5 bytes from the callers return
       // 3. jmp to the argument
       TEXT runtime·jmpdefer(SB), 7, $0
      -	MOVQ	8(SP), AX	// fn
      +	MOVQ	8(SP), DX	// fn
       	MOVQ	16(SP), BX	// caller sp
       	LEAQ	-8(BX), SP	// caller sp after CALL
       	SUBQ	$5, (SP)	// return to CALL again
      -	MOVQ	0(AX), BX
      +	MOVQ	0(DX), BX
       	JMP	BX	// but first run the deferred function
       
       // Dummy function to use in saved gobuf.PC,
      
  • src/pkg/runtime/asm_arm.s (ARM アセンブリ):

    • runtime·gogocallのシグネチャ変更と、R0レジスタへのコンテキスト引数r0のロード。
    • runtime·morestackm_cret(m)R0を保存する行を追加。
      --- a/src/pkg/runtime/asm_arm.s
      +++ b/src/pkg/runtime/asm_arm.s
      @@ -112,19 +112,19 @@ TEXT runtime·gogo(SB), 7, $-4
       	MOVW	gobuf_sp(R1), SP	// restore SP
       	MOVW	gobuf_pc(R1), PC
       
      -// void gogocall(Gobuf*, void (*fn)(void))
      +// void gogocall(Gobuf*, void (*fn)(void), uintptr r0)
       // restore state from Gobuf but then call fn.
       // (call fn, returning to state in Gobuf)
       // using frame size $-4 means do not save LR on stack.
       TEXT runtime·gogocall(SB), 7, $-4
       	MOVW	0(FP), R3		// gobuf
       	MOVW	4(FP), R1		// fn
      -	MOVW	8(FP), R2		// fp offset
       	MOVW	gobuf_g(R3), g
       	MOVW	0(g), R0		// make sure g != nil
       	MOVW	cgo_save_gm(SB), R0
       	CMP 	$0, R0 // if in Cgo, we have to save g and m
       	BL.NE	(R0) // this call will clobber R0
      +	MOVW	8(FP), R0	// context
       	MOVW	gobuf_sp(R3), SP	// restore SP
       	MOVW	gobuf_pc(R3), LR
       	MOVW	R1, PC
      @@ -136,7 +136,6 @@ TEXT runtime·gogocall(SB), 7, $-4
       TEXT runtime·gogocallfn(SB), 7, $-4
       	MOVW	0(FP), R3		// gobuf
       	MOVW	4(FP), R1		// fn
      -	MOVW	8(FP), R2		// fp offset
       	MOVW	gobuf_g(R3), g
       	MOVW	0(g), R0		// make sure g != nil
       	MOVW	cgo_save_gm(SB), R0
      @@ -189,6 +188,7 @@ TEXT runtime·morestack(SB),7,$-4
       	BL.EQ	runtime·abort(SB)
       
       	// Save in m.
      +	MOVW	R0, m_cret(m) // function context
       	MOVW	R1, m_moreframesize(m)
       	MOVW	R2, m_moreargsize(m)
       
      
  • src/pkg/runtime/runtime.h:

    • runtime·gogocall関数のプロトタイプ宣言を更新。
      --- a/src/pkg/runtime/runtime.h
      +++ b/src/pkg/runtime/runtime.h
      @@ -615,7 +615,7 @@ int32	runtime·charntorune(int32*, uint8*, int32);
       #define FLUSH(x)	USED(x)
       
       void	runtime·gogo(Gobuf*, uintptr);
      -void	runtime·gogocall(Gobuf*, void(*)(void));
      +void	runtime·gogocall(Gobuf*, void(*)(void), uintptr);
       void	runtime·gogocallfn(Gobuf*, FuncVal*);
       void	runtime·gosave(Gobuf*);
       void	runtime·lessstack(void);
      
  • src/pkg/runtime/stack.c:

    • runtime·newstack関数内でruntime·gogocallを呼び出す際に、m->cretを新しい引数として渡すように変更。
      --- a/src/pkg/runtime/stack.c
      +++ b/src/pkg/runtime/stack.c
      @@ -276,7 +276,7 @@ runtime·newstack(void)\
       	if(reflectcall)\
       		runtime·gogocallfn(&label, (FuncVal*)m->morepc);\
       	else\
      -		runtime·gogocall(&label, m->morepc);\
      +		runtime·gogocall(&label, m->morepc, m->cret);\
       
       	*(int32*)345 = 123;\t// never return
       }
      

コアとなるコードの解説

このコミットの核となる変更は、Goランタイムが関数呼び出しのコンテキストをどのように管理し、レジスタをどのように利用するかという点にあります。

  1. レジスタの再割り当て: コンパイラ(6g, 8g)が生成するコードにおいて、間接呼び出しのアドレスを保持するレジスタがAXからDX(またはDI)に変更されました。これは、AXレジスタがGoランタイムの他の重要な部分(特にスタックのゼロクリア)で不可欠であり、その使用を減らすことでレジスタの競合を避け、より効率的なコード生成を可能にするためです。低レベルのアセンブリコードでは、レジスタの選択はパフォーマンスに直接影響するため、このような変更は重要です。

  2. runtime·gogocallへのコンテキスト引数追加: runtime·gogocallは、Goランタイムがgoroutineのコンテキストを切り替える際に使用する低レベルの関数です。この関数にuintptr r0という新しい引数が追加されたことは、Goランタイムが関数呼び出しの際に、より多くのコンテキスト情報を渡す必要があることを示唆しています。

    • gogocallは、Gobuf構造体から保存された状態(スタックポインタ、プログラムカウンタなど)を復元し、指定された関数fnを呼び出します。
    • 追加されたr0引数は、アセンブリコード内でDX(x86)またはR0(ARM)レジスタにロードされ、最終的にm構造体のcretフィールドに保存されます。
    • m構造体は、GoランタイムにおけるOSスレッド(M: Machine)の情報を保持しており、cretフィールドは「call return context」のような意味合いを持つと考えられます。これは、関数呼び出しの戻り値や、呼び出し元のコンテキストに関する追加情報がここに格納されることを示唆しています。
    • runtime·newstack関数(スタックの拡張が必要な場合に呼び出される)がgogocallを呼び出す際に、m->cretを渡すようになったことから、スタックの管理や関数呼び出しの際に、このcretが重要な役割を果たすことがわかります。

これらの変更は、Go 1.1で導入された新しい関数呼び出し規約(go11func)の準備段階として行われました。この規約変更は、Goの関数呼び出しのオーバーヘッドを削減し、特にクロージャやインターフェースメソッドの呼び出し性能を向上させることを目的としていました。レジスタの効率的な利用と、関数呼び出しコンテキストのより柔軟な管理は、これらの目標達成に不可欠な要素です。

関連リンク

  • Go 1.1 Release Notes: Go 1.1のリリースノートには、このコミットが関連する関数呼び出し規約の変更やパフォーマンス改善に関する情報が含まれている可能性があります。
  • Go Assembly Language: Goのアセンブリ言語に関する公式ドキュメントやチュートリアル。
  • Go Runtime Source Code: Goランタイムのソースコードは、これらの低レベルな変更の全体像を理解する上で不可欠です。

参考にした情報源リンク

  • http://golang.org/s/go11func: このURLは、Go 1.1における関数呼び出し規約の変更に関する提案ドキュメントを指しています。このコミットの背景を深く理解するために最も重要な情報源です。
  • x86 Instruction Set Reference: x86アーキテクチャのレジスタと命令に関する詳細な情報。
    • Intel 64 and IA-32 Architectures Software Developer's Manuals (Intelの公式ドキュメント)
  • ARM Architecture Reference Manual: ARMアーキテクチャのレジスタと命令に関する詳細な情報。
    • ARM Architecture Reference Manual (ARMの公式ドキュメント)
  • Goのソースコード: 特にsrc/cmd/, src/pkg/runtime/以下のファイル。
  • GoのIssue Tracker: 関連するバグ報告や機能リクエスト。
  • GoのCode Reviewシステム (Gerrit): コミットメッセージに記載されているhttps://golang.org/cl/7395050は、GoのGerritコードレビューシステムへのリンクです。ここには、この変更に関する議論や追加のコンテキストが含まれている可能性があります。
  • Goのメーリングリスト (golang-dev): 開発者間の議論。