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

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

このコミットは、Go言語のランタイムにおけるOpenBSD/amd64アーキテクチャでのnanotime関数の修正に関するものです。具体的には、gettimeofdayシステムコールから返されるマイクロ秒の値を受け取るレジスタが誤っていたのを修正し、高精度な時間計測の正確性を向上させています。

コミット

commit cb2040b2ab68ab7d2863e65920749ed1266f7acd
Author: Russ Cox <rsc@golang.org>
Date:   Fri Nov 4 07:24:39 2011 -0400

    runtime: fix openbsd nanotime?
    
    TBR=r
    CC=golang-dev
    https://golang.org/cl/5353041
---
 src/pkg/runtime/openbsd/amd64/sys.s | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n
diff --git a/src/pkg/runtime/openbsd/amd64/sys.s b/src/pkg/runtime/openbsd/amd64/sys.s
index dfbb2547fa..7bb44d6a95 100644
--- a/src/pkg/runtime/openbsd/amd64/sys.s
+++ b/src/pkg/runtime/openbsd/amd64/sys.s
@@ -139,7 +139,7 @@ TEXT runtime·nanotime(SB),7,$32
  	MOVL	$116, AX		// sys_gettimeofday
  	SYSCALL
  	MOVQ	8(SP), AX		// sec
-\tMOVL	16(SP), BX		// usec
+\tMOVL	16(SP), DX		// usec
  
  	// sec is in AX, usec in DX
  	// return nsec in AX

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

https://github.com/golang/go/commit/cb2040b2ab68ab7d2863e65920749ed1266f7acd

元コミット内容

コミットメッセージは「runtime: fix openbsd nanotime?」と非常に簡潔です。これは、GoランタイムにおけるOpenBSD環境でのnanotime関数の修正を示唆しています。末尾の疑問符は、この修正が問題の解決策として適切であるか、あるいはさらなる検証が必要であるかを示唆している可能性があります。TBR=rCC=golang-devは、コードレビューの担当者とメーリングリストを示しています。https://golang.org/cl/5353041は、この変更に対応するGerritのチェンジリストへのリンクです。

変更の背景

Go言語のruntime.nanotime関数は、プログラムの実行時間を高精度で計測するために使用されます。これは、プロファイリング、ベンチマーク、または時間ベースのアルゴリズムにおいて非常に重要です。多くのオペレーティングシステムでは、clock_gettimeのようなシステムコールがナノ秒単位の精度を提供しますが、古いシステムや特定の環境ではgettimeofdayが使用されることがあります。

gettimeofdayシステムコールは、通常、秒とマイクロ秒(100万分の1秒)の精度で時刻を返します。このコミットの背景には、OpenBSD/amd64環境において、gettimeofdayシステムコールが返したマイクロ秒の値が、Goランタイムのアセンブリコードで誤ったレジスタに読み込まれていたという問題があったと考えられます。これにより、nanotime関数が不正確な値を返し、時間計測の信頼性が損なわれる可能性がありました。

アセンブリ言語レベルでのレジスタの誤用は、システムコール規約の誤解、または特定のOSバージョンやアーキテクチャにおけるレジスタ使用法の違いに起因することがあります。この修正は、OpenBSD/amd64におけるgettimeofdayの戻り値の処理が、Goランタイムの期待と一致するように調整されたことを示しています。

前提知識の解説

  • Go言語のruntime: Goプログラムは、Goランタイムと呼ばれる独自の実行環境上で動作します。ランタイムは、ガベージコレクション、スケジューリング、システムコールインターフェースなど、プログラムの実行に必要な低レベルの機能を提供します。nanotimeのような関数は、このランタイムの一部として実装されています。
  • nanotime: Go言語の内部関数で、ナノ秒単位の精度でモノトニックな時間(システムの起動からの経過時間など、システム時刻の変更に影響されない時間)を返します。これは、プログラムのパフォーマンス測定や、時間差の計算に利用されます。
  • OpenBSD: セキュリティを重視したUNIX系オペレーティングシステムです。異なるOSは、システムコールやアセンブリレベルでの規約が異なる場合があります。
  • amd64アーキテクチャ: IntelおよびAMDの64ビットプロセッサアーキテクチャです。x86-64とも呼ばれます。このアーキテクチャでは、システムコールや関数呼び出しにおいて、特定のレジスタが引数や戻り値の受け渡しに使用されるという規約(Calling Convention)があります。
  • システムコール (SYSCALL): ユーザー空間で動作するプログラムが、カーネル(OSの核心部分)が提供するサービス(ファイルI/O、メモリ管理、時間取得など)を利用するためのメカニズムです。SYSCALL命令は、CPUを特権モードに切り替え、カーネル内の対応するハンドラを実行します。
  • gettimeofday: UNIX系システムで利用されるシステムコールの一つで、現在の時刻を秒とマイクロ秒の精度で取得します。通常、struct timeval構造体に秒とマイクロ秒が格納されて返されます。
  • アセンブリ言語: コンピュータのプロセッサが直接理解できる機械語に非常に近い低レベルのプログラミング言語です。レジスタ(AX, BX, DXなど)は、CPU内部の高速な記憶領域であり、データの一時的な格納や演算に使用されます。
    • AX (Accumulator Register): 演算結果や関数の戻り値を格納するためによく使われます。
    • BX (Base Register): ベースアドレスの指定など、汎用的に使われます。
    • DX (Data Register): 汎用的に使われますが、AXと組み合わせて32ビットや64ビットの演算結果を格納したり、I/Oポートのアドレス指定に使われたりすることもあります。システムコールによっては、特定の戻り値を格納するレジスタとして使用されることがあります。
    • MOVL: 32ビットの値を移動するアセンブリ命令です。
    • MOVQ: 64ビットの値を移動するアセンブリ命令です。
    • SP (Stack Pointer): スタックの現在のトップを指すレジスタです。8(SP)16(SP)は、スタックポインタからのオフセットを示し、スタック上のメモリ位置にアクセスします。

技術的詳細

このコミットは、src/pkg/runtime/openbsd/amd64/sys.sファイル内のruntime·nanotime関数のアセンブリコードを修正しています。この関数は、OpenBSD/amd64環境でgettimeofdayシステムコールを使用して時刻を取得しています。

元のコードでは、gettimeofdayシステムコールが実行された後、秒の値はAXレジスタに、マイクロ秒の値はBXレジスタに読み込まれると想定されていました。

 	MOVL	$116, AX		// sys_gettimeofday
 	SYSCALL
 	MOVQ	8(SP), AX		// sec
 	MOVL	16(SP), BX		// usec  <-- ここが問題

しかし、OpenBSD/amd64のシステムコール規約、またはgettimeofdayシステムコールの特定の動作において、マイクロ秒の値がBXではなくDXレジスタに格納されることが判明したと考えられます。この誤ったレジスタからの読み込みが、nanotimeが不正確なマイクロ秒の値を使用する原因となっていました。

修正は、マイクロ秒の値をBXからDXに読み込むように変更することで、このレジスタの不一致を解消しています。

 	MOVL	$116, AX		// sys_gettimeofday
 	SYSCALL
 	MOVQ	8(SP), AX		// sec
 	MOVL	16(SP), DX		// usec  <-- 修正後

この変更により、nanotime関数はgettimeofdayから返される正しいマイクロ秒の値を取得できるようになり、結果としてより正確な時間計測が可能になります。Goランタイムは、取得した秒とマイクロ秒の値をナノ秒に変換して返します。

Web検索の結果によると、現代のGoランタイムは通常、より高精度なclock_gettimeを使用していますが、このコミットは2011年のものであり、当時のOpenBSD環境でのgettimeofdayの利用と、そのアセンブリレベルでの正確なレジスタハンドリングの重要性を示しています。

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

--- a/src/pkg/runtime/openbsd/amd64/sys.s
+++ b/src/pkg/runtime/openbsd/amd64/sys.s
@@ -139,7 +139,7 @@ TEXT runtime·nanotime(SB),7,$32
  	MOVL	$116, AX		// sys_gettimeofday
  	SYSCALL
  	MOVQ	8(SP), AX		// sec
-\tMOVL	16(SP), BX		// usec
+\tMOVL	16(SP), DX		// usec
  
  	// sec is in AX, usec in DX
  	// return nsec in AX

コアとなるコードの解説

変更は、src/pkg/runtime/openbsd/amd64/sys.sファイル内のruntime·nanotime関数にあります。

  • TEXT runtime·nanotime(SB),7,$32: runtime·nanotime関数の定義。SBはシンボルベース、7はフラグ、$32はスタックフレームサイズを示します。
  • MOVL $116, AX: AXレジスタに116をロードします。116はOpenBSD/amd64におけるgettimeofdayシステムコールの番号です。
  • SYSCALL: システムコールを実行します。これにより、カーネルがgettimeofday関数を実行し、結果をレジスタやスタックに配置します。
  • MOVQ 8(SP), AX: スタックポインタSPから8バイトオフセットしたメモリ位置から64ビットの値(秒)をAXレジスタに移動します。これはgettimeofdayが返す秒の部分です。
  • - MOVL 16(SP), BX // usec: 変更前の行です。スタックポインタSPから16バイトオフセットしたメモリ位置から32ビットの値(マイクロ秒)をBXレジスタに移動しようとしていました。
  • + MOVL 16(SP), DX // usec: 変更後の行です。同じメモリ位置からマイクロ秒の値をDXレジスタに移動します。この変更が、OpenBSD/amd64のシステムコール規約に合致し、正しいマイクロ秒の値を取得できるようにします。
  • // sec is in AX, usec in DX: コメントは、秒がAXに、マイクロ秒がDXにあることを示しており、修正後の状態を反映しています。
  • // return nsec in AX: 最終的に、秒とマイクロ秒から計算されたナノ秒の値がAXレジスタに格納されて関数から返されます。

この修正は、システムコールから返されるデータが、アセンブリコードが期待するレジスタに正しく配置されるようにするための、低レベルかつ重要な調整です。

関連リンク

参考にした情報源リンク