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

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

このコミットは、Go言語のツールチェイン(特にアセンブラ 8a とリンカ 8l)に RDTSC (Read Time-Stamp Counter) 命令のサポートを追加し、Goランタイムがこの命令を利用するように変更したものです。これにより、Goプログラム内でCPUのタイムスタンプカウンタを直接読み取ることが可能になり、より高精度な時間計測やプロファイリングの基盤が提供されます。

コミット

commit 6392b43a1583f5ccf5a3f7c38f096e8dd5403b0d
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Mon Feb 6 12:49:28 2012 -0500

    8a, 8l: implement support for RDTSC instruction.
            Also modify runtime/asm_386.s to use it.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5634043

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

https://github.com/golang/go/commit/6392b43a1583f5ccf5a3f7c38f096e8dd5403b0d

元コミット内容

8a, 8l: implement support for RDTSC instruction.
        Also modify runtime/asm_386.s to use it.

R=rsc
CC=golang-dev
https://golang.org/cl/5634043

変更の背景

この変更の背景には、Go言語のランタイムがより低レベルで高精度な時間計測機能を利用できるようにするという目的があります。RDTSC命令は、CPUのタイムスタンプカウンタを直接読み取るための命令であり、非常に細かい粒度での時間計測を可能にします。

Go言語のランタイムは、スケジューラやガベージコレクタなど、内部的な処理で時間計測を必要とします。また、ユーザーがプログラムのパフォーマンスをプロファイリングする際にも、高精度な時間情報が求められることがあります。既存の時間計測メカニズムでは不十分な場合や、特定のハードウェア特性を利用してより効率的な時間計測を行いたい場合に、RDTSC命令のサポートが重要になります。

このコミット以前は、Goのアセンブラ 8aRDTSC 命令を直接サポートしていませんでした。そのため、runtime/asm_386.s のようなアセンブリファイルで RDTSC を使用するには、BYTE $0x0F; BYTE $0x31; のようにバイトコードを直接記述する必要がありました。これは可読性が低く、保守性も劣ります。このコミットは、ツールチェインレベルで RDTSC を正式にサポートすることで、この問題を解決し、よりクリーンなアセンブリコードの記述を可能にしています。

前提知識の解説

RDTSC (Read Time-Stamp Counter) 命令

RDTSC は、IntelおよびAMDのx86アーキテクチャのプロセッサが提供するCPU命令です。この命令は、プロセッサが起動してからのクロックサイクル数をカウントする64ビットのレジスタである「タイムスタンプカウンタ (TSC)」の値を読み取ります。

  • 機能: RDTSC 命令を実行すると、TSCの現在の値が EDX:EAX レジスタペア(上位32ビットが EDX、下位32ビットが EAX)に格納されます。
  • 用途:
    • 高精度な時間計測: ナノ秒レベルの非常に細かい時間計測が可能です。マイクロベンチマークやプロファイリングにおいて、特定のコードブロックの実行時間を正確に測定するために使用されます。
    • 乱数生成のシード: TSCの値は予測が難しいため、乱数生成器のシードとして利用されることがあります。
    • イベントのタイムスタンプ: システム内のイベント発生時刻を記録するために使用されます。
  • 注意点:
    • 周波数変動: 現代のCPUでは、省電力機能やターボブーストなどによりCPUの動作周波数が動的に変動することがあります。この場合、TSCのカウントは一定の時間を表さない可能性があります。そのため、正確な壁時計時間(リアルタイム)を測定する目的には適さない場合があります。
    • マルチコア/マルチプロセッサ: 複数のコアやプロセッサを持つシステムでは、各コア/プロセッサが独立したTSCを持つ場合があり、それらの同期が保証されないことがあります。これにより、異なるコアで実行されるスレッド間でTSCの値が比較できない問題が発生する可能性があります。
    • 仮想化環境: 仮想マシン環境では、ハイパーバイザがTSCの動作をエミュレートまたは仮想化するため、物理ハードウェアとは異なる挙動を示すことがあります。

Go言語のツールチェイン

Go言語は、独自のコンパイラ、アセンブラ、リンカを含む完全なツールチェインを持っています。

  • 8a (アセンブラ): x86アーキテクチャ(32ビット)向けのアセンブラです。アセンブリ言語で書かれたソースコード(.s ファイル)をオブジェクトファイルに変換します。
  • 8l (リンカ): x86アーキテクチャ(32ビット)向けのリンカです。オブジェクトファイルを結合し、実行可能ファイルを生成します。
  • runtime パッケージ: Go言語のランタイムシステムを実装しているパッケージです。ガベージコレクタ、スケジューラ、メモリ管理、システムコールインターフェースなど、Goプログラムの実行に必要な低レベルな機能を提供します。runtime パッケージ内には、特定のアーキテクチャ向けのアセンブリコード(例: asm_386.s)が含まれることがあります。

技術的詳細

このコミットは、Go言語のツールチェインとランタイムにおいて、RDTSC 命令をネイティブにサポートするための変更を導入しています。

  1. アセンブラ 8a での RDTSC 命令の認識:

    • src/cmd/8a/lex.c は、アセンブラの字句解析器(lexer)のソースコードです。このファイルに RDTSC という新しいキーワードが追加され、アセンブラがこの命令を認識できるようになります。
    • LTYPE0 は、オペランドを持たない命令(ゼロオペランド命令)であることを示します。
    • ARDTSC は、RDTSC 命令に対応する内部的なオペレーションコード(opcode)の定数です。
  2. リンカ 8l での ARDTSC オペコードの定義:

    • src/cmd/8l/8.out.h は、リンカが使用するオペレーションコードの定義を含むヘッダファイルです。ここに ARDTSC が新しいエントリとして追加されます。これにより、リンカが ARDTSC オペコードを理解し、適切に処理できるようになります。
  3. リンカ 8l での RDTSC 命令の機械語コードへの変換規則の追加:

    • src/cmd/8l/optab.c は、リンカがアセンブリ命令を対応する機械語コード(バイト列)に変換するためのテーブル(Optab)を定義しています。
    • ARDTSC 命令に対して、機械語コード 0x0f, 0x31 が割り当てられます。これは RDTSC 命令の実際のオペコードです。
    • ynone は、この命令がオペランドを持たないことを示します。
    • Pm は、命令が特定のプレフィックス(ここでは 0x0F)を持つことを示します。
  4. ランタイムのアセンブリコード asm_386.s の変更:

    • src/pkg/runtime/asm_386.s は、Goランタイムの32ビットx86アーキテクチャ向けアセンブリコードです。
    • 以前は runtime·cputicks 関数内で RDTSC 命令を使用するために BYTE $0x0F; BYTE $0x31; のように直接バイトコードを記述していました。これは、アセンブラが RDTSC を直接認識しなかったためです。
    • このコミットにより、ツールチェインが RDTSC をサポートするようになったため、より可読性の高い RDTSC というニーモニックを直接使用できるようになりました。

これらの変更により、Goのツールチェインは RDTSC 命令をネイティブにサポートし、開発者はアセンブリコード内でこの命令を直接、かつクリーンに記述できるようになります。これにより、GoランタイムがCPUのタイムスタンプカウンタをより効率的かつ安全に利用できるようになり、高精度な時間計測機能の基盤が強化されます。

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

src/cmd/8a/lex.c

--- a/src/cmd/8a/lex.c
+++ b/src/cmd/8a/lex.c
@@ -445,6 +445,7 @@ struct
 	"RCRB",		LTYPE3,	ARCRB,
 	"RCRL",		LTYPE3,	ARCRL,
 	"RCRW",		LTYPE3,	ARCRW,
+	"RDTSC",	LTYPE0,	ARDTSC,
 	"REP",		LTYPE0,	AREP,
 	"REPN",		LTYPE0,	AREPN,
 	"RET",		LTYPE0,	ARET,

src/cmd/8l/8.out.h

--- a/src/cmd/8l/8.out.h
+++ b/src/cmd/8l/8.out.h
@@ -395,7 +395,9 @@ enum	as
 	ACMPXCHGL,
 	ACMPXCHGW,
 	ACMPXCHG8B,
-\t
+
+	ARDTSC,
+
 	AXADDB,
 	AXADDL,
 	AXADDW,

src/cmd/8l/optab.c

--- a/src/cmd/8l/optab.c
+++ b/src/cmd/8l/optab.c
@@ -707,6 +707,8 @@ Optab optab[] =
 	{ ACMPXCHGW,	yrl_ml,	Pm, 0xb1 },
 	{ ACMPXCHG8B,	yscond,	Pm, 0xc7,(01) },
 
+\t{ ARDTSC,	ynone,	Pm, 0x31 },
+
 	{ AXADDB,	yrb_mb,	Pb, 0x0f,0xc0 },
 	{ AXADDL,	yrl_ml,	Pm, 0xc1 },
 	{ AXADDW,	yrl_ml,	Pe, 0x0f,0xc1 },

src/pkg/runtime/asm_386.s

--- a/src/pkg/runtime/asm_386.s
+++ b/src/pkg/runtime/asm_386.s
@@ -527,7 +527,7 @@ TEXT runtime·getcallersp(SB), 7, $0
 // int64 runtime·cputicks(void), so really
 // void runtime·cputicks(int64 *ticks)
 TEXT runtime·cputicks(SB),7,$0
-\tBYTE\t$0x0F; BYTE $0x31;     // RDTSC; not supported by 8a
+\tRDTSC
 	MOVL	ret+0(FP), DI
 	MOVL	AX, 0(DI)
 	MOVL	DX, 4(DI)

コアとなるコードの解説

src/cmd/8a/lex.c の変更

  • {"RDTSC", LTYPE0, ARDTSC,} というエントリが keywords 構造体配列に追加されました。
  • これは、アセンブラ 8aRDTSC という文字列を認識し、それをオペランドを持たない命令 (LTYPE0) として扱い、内部的には ARDTSC というオペコードにマッピングすることを示します。これにより、アセンブリソースコードで RDTSC と記述できるようになります。

src/cmd/8l/8.out.h の変更

  • enum as 列挙型に ARDTSC, が追加されました。
  • この列挙型は、リンカ 8l が認識するすべてのアセンブリ命令の内部表現を定義しています。ARDTSC の追加により、リンカが RDTSC 命令を処理するための準備が整いました。

src/cmd/8l/optab.c の変更

  • Optab optab[] 配列に { ARDTSC, ynone, Pm, 0x31 }, という新しいエントリが追加されました。
  • これは、リンカが ARDTSC オペコードを実際の機械語コードに変換するための規則を定義しています。
    • ARDTSC: 内部オペコード。
    • ynone: オペランドがないことを示す。
    • Pm: 命令が 0x0F というプレフィックスを持つことを示す。
    • 0x31: 0x0F プレフィックスに続く命令のオペコード。
  • したがって、RDTSC 命令は 0x0F 0x31 という2バイトの機械語コードに変換されます。

src/pkg/runtime/asm_386.s の変更

  • runtime·cputicks 関数内で、以前は BYTE $0x0F; BYTE $0x31; と直接バイトコードを記述して RDTSC 命令を生成していました。これは、アセンブラが RDTSC ニーモニックを直接サポートしていなかったためです。
  • このコミットにより、ツールチェインが RDTSC を認識するようになったため、より簡潔で可読性の高い RDTSC というニーモニックに置き換えられました。
  • この関数は、CPUのタイムスタンプカウンタを読み取り、その値を ret+0(FP) (フレームポインタからのオフセットで指定される戻り値のアドレス) に格納します。AX レジスタにはTSCの下位32ビット、DX レジスタには上位32ビットが格納されるため、これらを結合して64ビットの値を返す処理が行われています。

これらの変更により、Goのツールチェインは RDTSC 命令を完全にサポートし、ランタイムがこの命令をより自然な形で利用できるようになりました。

関連リンク

参考にした情報源リンク