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

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

コミット

commit 0bd53d2ce09222075ff366b657cbba344fc4581b
Author: Rob Pike <r@golang.org>
Date:   Wed Feb 8 15:26:36 2012 +1100

    runtime/cgo: silence warning on windows
    It appears to want a * on an indirect function call (assembly language)
    
    TBR=rsc
    
    R=golang-dev
    CC=golang-dev
    https://golang.org/cl/5643054

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

https://github.com/golang/go/commit/0bd53d2ce09222075ff366b657cbba344fc4581b

元コミット内容

runtime/cgo: silence warning on windows
It appears to want a * on an indirect function call (assembly language)

TBR=rsc

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

変更の背景

このコミットは、Go言語のランタイムにおけるCgo(C言語との相互運用機能)関連のコードで、Windows環境において発生していたコンパイラ警告を解消するために行われました。具体的には、runtime/cgo/gcc_amd64.Sというアセンブリ言語ファイル内で、間接関数呼び出しの構文がWindowsのコンパイラ(おそらくMinGWやCygwin環境下のGCC)の期待する形式と異なっていたことが原因です。

Go言語はクロスプラットフォーム対応を重視しており、様々なオペレーティングシステムやアーキテクチャで動作します。そのため、各プラットフォーム固有のコンパイラやリンカの挙動に合わせた調整が必要となることがあります。このケースでは、Windows環境でのアセンブリコードの解釈に関する微妙な違いが警告として現れており、それを修正することでビルドプロセスのクリーンさを保ち、将来的な問題を防ぐことが目的でした。警告は通常、潜在的なバグや非推奨の構文を示唆するため、開発プロセスにおいてこれらを解消することは重要です。

前提知識の解説

  • Go言語のランタイム (runtime): Goプログラムが実行される際に、メモリ管理(ガベージコレクション)、ゴルーチン(軽量スレッド)のスケジューリング、チャネル通信、システムコールなど、低レベルな操作を司る部分です。多くの場合、C言語やアセンブリ言語で記述されています。
  • Cgo: Go言語からC言語のコードを呼び出したり、C言語のコードからGo言語のコードを呼び出したりするためのGoの機能です。これにより、既存のCライブラリを利用したり、パフォーマンスが重要な部分をCで記述したりすることが可能になります。Cgoは、GoとCの間の呼び出し規約の変換や、スタックの管理など、複雑な処理を内部で行います。
  • アセンブリ言語 (Assembly Language): コンピュータのプロセッサが直接理解できる機械語に非常に近い低レベルなプログラミング言語です。特定のCPUアーキテクチャ(例: AMD64/x86-64)に特化しており、レジスタ操作、メモリアクセス、ジャンプ命令などを直接記述します。Go言語のランタイムやCgoの内部では、パフォーマンスが要求される部分や、OSとのインタフェース部分でアセンブリ言語が使用されることがあります。
  • 間接関数呼び出し (Indirect Function Call): 関数を呼び出す際に、関数のアドレスがレジスタやメモリに格納されており、そのレジスタやメモリの内容を介して関数を呼び出す方式です。例えば、C言語の関数ポインタを使った呼び出しや、動的リンクライブラリ(DLL/SO)の関数呼び出しなどで用いられます。アセンブリ言語では、call命令に直接関数のラベルを指定する代わりに、レジスタやメモリの内容をオペランドとして指定します。
  • x86-64 (AMD64) アーキテクチャ: 64ビットの汎用プロセッサアーキテクチャで、IntelとAMDのCPUで広く採用されています。レジスタの数やサイズ、命令セットなどが32ビットのx86アーキテクチャから拡張されています。
  • %rcx レジスタ: x86-64アーキテクチャにおける汎用レジスタの一つです。Microsoft Windows x64呼び出し規約では、関数呼び出しの最初の整数引数(またはポインタ引数)を渡すために使用されます。
  • コンパイラの警告 (Compiler Warning): プログラムのコンパイル時にコンパイラが出力するメッセージで、コードが文法的に正しいものの、潜在的な問題(非効率なコード、移植性の問題、未定義の動作の可能性など)を含んでいる可能性があることを示します。警告はエラーとは異なり、通常はプログラムの実行ファイルを生成できますが、無視すべきではありません。

技術的詳細

このコミットの技術的詳細は、x86-64アセンブリ言語における間接関数呼び出しの構文、特にWindows環境でのコンパイラの解釈の違いに集約されます。

x86-64アセンブリ言語では、間接関数呼び出しは通常、call *operandまたはcall operandの形式で記述されます。ここでoperandは、呼び出す関数のアドレスが格納されているレジスタまたはメモリ位置を示します。

  • call *%rcx: この構文は、%rcxレジスタに格納されているアドレスを間接的に参照し、そのアドレスにある関数を呼び出すことを明示的に示します。アスタリスク(*)は、オペランドが指すメモリ位置の内容をデリファレンス(間接参照)することを意味します。アセンブラによっては、レジスタをオペランドとする場合はアスタリスクが不要な場合もありますが、明示的に間接呼び出しであることを示すために使用されることがあります。
  • call %rcx: この構文は、%rcxレジスタに格納されているアドレスを直接呼び出すことを意味します。多くのアセンブラでは、レジスタをオペランドとする間接呼び出しの場合、アスタリスクは省略可能です。

問題は、Windows環境で使用される特定のコンパイラ(おそらくMinGW/GCC)のアセンブラが、call %rcxという構文を、%rcxレジスタに格納されている値を「即値」として解釈しようとしたか、あるいは間接呼び出しとして認識するために明示的なアスタリスクを要求した、という点にあります。

Goのランタイムコードでは、crosscall2という関数内で、Cgoのコールバック関数を呼び出すために%rcxレジスタに関数ポインタをロードし、それを呼び出していました。元のコードcall %rcxは、LinuxやmacOSなどの他のUnix系システムでは問題なく動作していた可能性がありますが、Windowsのコンパイラはこれを警告としてフラグ立てしました。

この警告は、アセンブラが命令の意図を正確に解釈できない、あるいは特定のプラットフォームの慣習に合致しない場合に発生します。call *%rcxと修正することで、アセンブラに対して「%rcxレジスタの内容をアドレスとして扱い、そのアドレスが指す関数を呼び出す」という意図をより明確に伝えることができ、結果として警告が解消されました。これは、コードの機能には影響を与えず、コンパイラの解釈を修正する純粋な構文上の変更です。

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

変更は src/pkg/runtime/cgo/gcc_amd64.S ファイルの1箇所のみです。

--- a/src/pkg/runtime/cgo/gcc_amd64.S
+++ b/src/pkg/runtime/cgo/gcc_amd64.S
@@ -70,7 +70,7 @@ EXT(crosscall2):\n 	movq  %rdx, 0(%rsp)	/* arg */\n 	movq  %r8, 8(%rsp)	/* argsize (includes padding) */\n 	\n-\tcall %rcx	/* fn */\n+\tcall *%rcx	/* fn */\n #else\n 	movq  %rsi, 0(%rsp)	/* arg */\n 	movq  %rdx, 8(%rsp)	/* argsize (includes padding) */

具体的には、73行目のcall %rcxcall *%rcxに変更されています。

コアとなるコードの解説

変更されたコードは、GoランタイムのCgo部分で、C言語からGo言語へのコールバックを行うためのアセンブリルーチンの一部です。crosscall2というラベルは、CgoがGo関数を呼び出す際に使用するエントリポイントを示しています。

このアセンブリコードのセクションは、以下の処理を行っています(関連部分のみ抜粋):

  1. movq %rdx, 0(%rsp): %rdxレジスタの内容(おそらく引数)をスタックポインタ%rspが指すアドレス(スタックのトップ)に格納します。
  2. movq %r8, 8(%rsp): %r8レジスタの内容(おそらく引数のサイズ)をスタックポインタ%rspから8バイトオフセットしたアドレスに格納します。
  3. call *%rcx: ここが変更点です。
    • 変更前: call %rcx
    • 変更後: call *%rcx

%rcxレジスタには、呼び出すGo関数のアドレスが格納されています。このcall命令は、%rcxに格納されたアドレスにある関数を呼び出すためのものです。

Windows環境のコンパイラが、call %rcxを間接呼び出しとして正しく解釈せず、警告を出していたため、アスタリスク*を追加してcall *%rcxとすることで、%rcxレジスタの内容を「アドレス」として間接的に参照し、そのアドレスにある関数を呼び出すという意図を明示しました。これにより、コンパイラは警告を出さなくなり、コードの機能は変わらずに、ビルド時のクリーンさが保たれました。

この変更は、Goのクロスプラットフォーム対応において、特定OSのアセンブラの挙動に合わせた微調整がいかに重要であるかを示しています。

関連リンク

参考にした情報源リンク