[インデックス 1469] ファイルの概要
このコミットは、Go言語のランタイムにおいて、GoとC言語の混在コードを扱うための新しいツール cgo2c
を導入するものです。これにより、Goの内部Cコンパイラである6c
と、一般的なCコンパイラであるgcc
の間で存在した、関数の戻り値の扱いの非互換性を単一のソースファイルで吸収できるようになります。具体的には、.cgo
という拡張子を持つファイルをC言語のソースファイルに変換する役割を担います。
コミット
commit 2b57a1124e87b0dc8bc1ff6899297b4d7d6e74f2
Author: Ian Lance Taylor <iant@golang.org>
Date: Wed Jan 14 08:21:25 2009 -0800
Add cgo2c program to translate mixed Go/C code into C. This
lets us use a single source file for both 6c and gcc, handling
the incompatible handling of return values.
R=rsc
DELTA=649 (613 added, 35 deleted, 1 changed)
OCL=22682
CL=22730
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2b57a1124e87b0dc8bc1ff6899297b4d7d6e74f2
元コミット内容
Add cgo2c program to translate mixed Go/C code into C. This
lets us use a single source file for both 6c and gcc, handling
the incompatible handling of return values.
変更の背景
Go言語の初期開発段階において、Goのランタイムや標準ライブラリの一部はC言語で記述されており、GoコードとCコードが混在する状況がありました。当時、Goには独自のCコンパイラである6c
(64ビットアーキテクチャ向け)が存在し、これと一般的なgcc
コンパイラの両方でコードをコンパイルする必要がありました。
しかし、6c
とgcc
の間には、特に複数戻り値を持つ関数の呼び出し規約に関して非互換性がありました。Go言語の関数は複数の戻り値を返すことができますが、C言語の関数は通常単一の戻り値しか持ちません。GoのCgoメカニズムは、このGoの複数戻り値をC側でどのように表現し、受け渡すかについて、コンパイラ間で異なるアプローチを取っていました。
この非互換性のため、開発者は6c
とgcc
の両方でコンパイル可能なように、同じロジックを異なる方法で記述するか、条件付きコンパイル(#ifdef
など)を多用する必要がありました。これはコードの重複、保守性の低下、そして開発の複雑化を招いていました。
このコミットの目的は、この問題を解決し、GoとCの混在コードを単一のソースファイルで記述できるようにすることです。cgo2c
というツールを導入することで、Goの関数シグネチャとCコードが混在する.cgo
ファイルを、6c
とgcc
のどちらでもコンパイル可能な標準的なCコードに変換し、コンパイラ間の戻り値の扱いの違いを吸収します。これにより、GoランタイムのC部分のコードベースを簡素化し、保守性を向上させることが可能になりました。
前提知識の解説
Go言語のCgo
Cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGo言語の機能です。Goのソースファイル内にimport "C"
という特殊なインポート文を記述し、その後にC言語のコードを直接埋め込むことで、GoとCの相互運用を可能にします。Cgoは、GoとCの異なる型システムやメモリ管理、関数呼び出し規約の間の橋渡しを行います。
6c (Goの内部Cコンパイラ)
6c
は、Go言語の初期のツールチェーンに含まれていたCコンパイラの一つです。Goプロジェクトは、独自のコンパイラ群(6g
for Go, 6c
for C, 6a
for assemblerなど)を持っていました。これらはGo言語の特定のニーズに合わせて設計されており、クロスコンパイルの容易さや、Goランタイムとの密接な連携を可能にしていました。しかし、6c
は標準的なCコンコンパイラ(GCCなど)とは異なる挙動や最適化を行うことがあり、特にABI(Application Binary Interface)の面で非互換性が生じることがありました。
GCC (GNU Compiler Collection)
GCCは、C、C++、Objective-C、Fortran、Ada、Goなどの多くのプログラミング言語をサポートする、広く使われているコンパイラコレクションです。Go言語のプロジェクトでは、特定のプラットフォームや環境で6c
が利用できない場合や、既存のCライブラリとの連携のためにgcc
を使用する必要がありました。
GoとCの関数呼び出し規約の違い(特に複数戻り値)
Go言語の大きな特徴の一つは、関数が複数の値を戻り値として返せることです。例えば、func foo() (int, error)
のように記述できます。しかし、C言語の関数は通常、単一の戻り値しか持ちません。CgoがGoの複数戻り値をC側で扱う場合、いくつかの方法が考えられます。
- 構造体による戻り値: 複数の戻り値をCの構造体としてまとめ、その構造体を返す。
- ポインタ引数による戻り値: 戻り値を格納するためのポインタを引数として渡し、関数内でそのポインタが指すメモリに値を書き込む。
6c
とgcc
は、これらのアプローチにおいて異なるABIを採用していたため、同じGoの関数シグネチャから生成されるCの関数定義が異なり、直接的な相互運用が困難でした。
.cgo
ファイルとプリプロセッサ/トランスレータ
このコミットで導入される.cgo
ファイルは、Goの関数シグネチャとC言語のコードブロックを組み合わせた特殊な形式のソースファイルです。cgo2c
は、この.cgo
ファイルを読み込み、Goの関数シグネチャをC言語の関数プロトタイプと定義に変換し、埋め込まれたCコードをそのまま出力するプリプロセッサ(またはトランスレータ)として機能します。これにより、Goの複数戻り値の概念をCの構造体やポインタ引数にマッピングする処理をcgo2c
が担当し、最終的に生成されるCファイルは6c
とgcc
のどちらでも正しくコンパイルできるようになります。
技術的詳細
cgo2c
プログラムは、標準入力から.cgo
ファイルを読み込み、標準出力にC言語のソースコードを出力するシンプルなコマンドラインツールです。その主な機能は以下の通りです。
- Goパッケージ名の読み込み:
.cgo
ファイルの先頭にあるpackage PACKAGENAME
を読み込み、C言語の関数名にGoのパッケージ名をプレフィックスとして付与するために使用します(例:package·functionName
)。 - プリプロセッサディレクティブのコピー:
# line
のようなプリプロセッサディレクティブをそのまま出力します。 - Go関数シグネチャの解析:
func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] {
の形式で記述されたGoの関数定義を解析します。- 関数名、引数リスト、戻り値リストを抽出します。
- Goの型(例:
*byte
)をCの型(例:byte *
)に変換します。
- C言語関数定義の生成:
6c
向け:void\npackage·name(param1 type1, param2 type2, ...)
の形式で関数を定義します。Goの複数戻り値は、Cの引数リストの末尾にポインタとして追加されます。関数本体のCコードの後に、戻り値のポインタをフラッシュするFLUSH
マクロが挿入されます。gcc
向け: Goの複数戻り値がある場合、それらを格納するためのCのstruct
型を定義します(例:struct package_name_ret { type1 name1; type2 name2; };
)。関数は、この構造体を戻り値として返すように定義されます。Goの関数名とCの関数名をasm
キーワードでマッピングします(例:void package_name() asm ("package.name");
)。
- Cコードブロックのコピー: Go関数シグネチャの後に続くCコードブロック(
{...}
で囲まれた部分)を、ネストされたブレース、コメント、文字列リテラルを正しく処理しながら、そのまま出力します。これにより、Goの関数内でCのロジックを自由に記述できます。 - コマンドライン引数によるコンパイラ選択:
cgo2c
は--6g
または--gcc
というコマンドライン引数を受け取り、どちらのコンパイラ向けのCコードを生成するかを切り替えます。これにより、ビルドシステムが適切なコンパイラに合わせてcgo2c
の出力を調整できます。
このメカニズムにより、Goのランタイムコードは、Goの複数戻り値のセマンティクスを維持しつつ、6c
とgcc
の両方でコンパイル可能な単一のCソースファイルとして記述できるようになりました。
src/runtime/Makefile
の変更は、このcgo2c
ツールのビルドと利用をビルドプロセスに組み込むものです。
cgo2c: cgo2c.c
ルールが追加され、cgo2c.c
からcgo2c
実行ファイルをビルドします。%.c: %.cgo cgo2c
ルールが追加され、.cgo
ファイルがcgo2c
によって対応する.c
ファイルに変換されるようにします。clean
ターゲットにcgo2c
の削除が追加されます。
src/runtime/malloc.c
からGo関数スタブが削除され、それらがsrc/runtime/malloc_go.cgo
に移行されたのは、malloc
関連のGoとCの混在コードをcgo2c
の管理下に置くためです。これにより、malloc
のGoインターフェースが、cgo2c
によって生成されるCコードを介して、6c
とgcc
の両方で一貫して動作するようになります。
コアとなるコードの変更箇所
このコミットの主要な変更は以下のファイルに集中しています。
src/runtime/cgo2c.c
(新規追加):cgo2c
プログラムのC言語ソースコード。GoとCの混在コードをCコードに変換するロジックが実装されています。src/runtime/Makefile
(変更):cgo2c
のビルドと、.cgo
ファイルを.c
ファイルに変換するルールが追加されました。src/runtime/malloc.c
(変更):malloc_Alloc
,malloc_Free
,malloc_Lookup
,malloc_GetStats
といったGo関数スタブが削除されました。これらの機能はcgo2c
を介して提供されるようになります。src/runtime/malloc.h
(変更):malloc
,free
,mlookup
の関数プロトタイプが追加されました。src/runtime/malloc_go.cgo
(新規追加):malloc
関連のGo関数とCコードを混在させた新しいソースファイル。cgo2c
によって処理されます。
コアとなるコードの解説
src/runtime/cgo2c.c
このファイルは、Goの関数シグネチャとCコードを解析し、ターゲットコンパイラ(6c
またはgcc
)に応じたCコードを生成する主要なロジックを含んでいます。
main
関数:- コマンドライン引数
--6g
または--gcc
を解析し、flag_gcc
を設定します。 process_file
関数を呼び出し、ファイル全体の処理を開始します。
- コマンドライン引数
process_file
関数:read_package()
でGoのパッケージ名を読み込みます。read_preprocessor_lines()
でプリプロセッサディレクティブをコピーします。- ループ内で
read_func_header()
を呼び出し、Goの関数定義を一つずつ解析します。 - 解析した関数情報(名前、引数、戻り値)に基づいて、
write_func_header()
でCの関数ヘッダを生成します。 copy_body()
でGo関数内のCコードブロックをコピーします。write_func_trailer()
でCの関数トレーラ(戻り値の処理など)を生成します。- 解析したメモリを解放します。
read_token
/read_token_no_eof
:- 入力ストリームからトークン(識別子、区切り文字など)を読み込みます。
- コメント(
//
と/* */
)をスキップするロジックが含まれています。
read_type
:- Goの型表現(例:
*byte
)をCの型表現(例:byte *
)に変換します。ポインタの数を正しく処理します。
- Goの型表現(例:
read_params
:- Goの関数引数または戻り値のリストを解析し、名前と型のペアのリンクリストとして返します。
read_func_header
:func
キーワードから始まり、関数名、引数リスト、戻り値リスト、そして{
までのGo関数シグネチャを解析します。
write_6g_func_header
/write_6g_func_trailer
:6c
コンパイラ向けのC関数ヘッダとトレーラを生成します。Goの複数戻り値は、Cの引数として渡されるポインタとして扱われ、トレーラでFLUSH
マクロが呼び出されます。
define_gcc_return_type
/write_gcc_return_type
/write_gcc_func_header
/write_gcc_func_trailer
:gcc
コンパイラ向けのC関数ヘッダとトレーラを生成します。- Goの複数戻り値がある場合、それらをカプセル化するためのCの
struct
型を定義し、関数はその構造体を戻り値として返します。 asm
キーワードを使用して、Goの関数名とCの関数名をマッピングします。
copy_body
:- Go関数定義内のCコードブロックを、ブレースのネストレベルを考慮しながら、そのまま出力します。文字列リテラルやコメント内のブレースを誤って解釈しないように注意深く処理されます。
src/runtime/malloc_go.cgo
このファイルは、cgo2c
によって処理されるGoとCの混在コードの具体例です。
package malloc
#include "runtime.h"
#include "malloc.h"
func Alloc(n uintptr) (p *byte) {
p = malloc(n);
}
func Free(p *byte) {
free(p);
}
func Lookup(p *byte) (base *byte, size uintptr) {
mlookup(p, &base, &size);
}
func GetStats() (s *MStats) {
s = &mstats;
}
このファイルでは、malloc
パッケージのGo関数Alloc
, Free
, Lookup
, GetStats
が定義されています。それぞれのGo関数の中身はC言語のコードで記述されており、malloc
, free
, mlookup
といったCの関数を呼び出しています。特にLookup
関数はbase
とsize
という2つの戻り値を持っており、cgo2c
がこれを6c
とgcc
の両方で正しく扱えるCコードに変換する役割を担います。
関連リンク
特になし。
参考にした情報源リンク
- Go言語のCgoに関する公式ドキュメントやブログ記事 (当時の情報を見つけるのは困難なため、一般的なCgoの概念に限定)
- Go言語の初期のコンパイラに関する議論やドキュメント (同上)
- GCCのABIに関する一般的な情報
cgo2c.c
のソースコード自体の詳細な分析
[インデックス 1469] ファイルの概要
このコミットは、Go言語のランタイムにおいて、GoとC言語の混在コードを扱うための新しいツール cgo2c
を導入するものです。これにより、Goの内部Cコンパイラである6c
と、一般的なCコンパイラであるgcc
の間で存在した、関数の戻り値の扱いの非互換性を単一のソースファイルで吸収できるようになります。具体的には、.cgo
という拡張子を持つファイルをC言語のソースファイルに変換する役割を担います。
コミット
commit 2b57a1124e87b0dc8bc1ff6899297b4d7d6e74f2
Author: Ian Lance Taylor <iant@golang.org>
Date: Wed Jan 14 08:21:25 2009 -0800
Add cgo2c program to translate mixed Go/C code into C. This
lets us use a single source file for both 6c and gcc, handling
the incompatible handling of return values.
R=rsc
DELTA=649 (613 added, 35 deleted, 1 changed)
OCL=22682
CL=22730
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2b57a1124e87b0dc8bc1ff6899297b4d7d6e74f2
元コミット内容
Add cgo2c program to translate mixed Go/C code into C. This
lets us use a single source file for both 6c and gcc, handling
the incompatible handling of return values.
変更の背景
Go言語の初期開発段階において、Goのランタイムや標準ライブラリの一部はC言語で記述されており、GoコードとCコードが混在する状況がありました。当時、Goには独自のCコンパイラである6c
(64ビットアーキテクチャ向け)が存在し、これと一般的なgcc
コンパイラの両方でコードをコンパイルする必要がありました。
しかし、6c
とgcc
の間には、特に複数戻り値を持つ関数の呼び出し規約に関して非互換性が存在していました。Go言語の関数は複数の戻り値を返すことができますが、C言語の関数は通常単一の戻り値しか持ちません。CgoメカニズムがGoの複数戻り値をC側でどのように表現し、受け渡すかについて、コンパイラ間で異なるABI(Application Binary Interface)を採用していたため、問題が生じていました。
この非互換性のため、開発者は6c
とgcc
の両方でコンパイル可能なように、同じロジックを異なる方法で記述するか、条件付きコンパイル(#ifdef
など)を多用する必要がありました。これはコードの重複、保守性の低下、そして開発の複雑化を招いていました。
このコミットの目的は、この問題を解決し、GoとCの混在コードを単一のソースファイルで記述できるようにすることです。cgo2c
というツールを導入することで、Goの関数シグネチャとCコードが混在する.cgo
ファイルを、6c
とgcc
のどちらでもコンパイル可能な標準的なCコードに変換し、コンパイラ間の戻り値の扱いの違いを吸収します。これにより、GoランタイムのC部分のコードベースを簡素化し、保守性を向上させることが可能になりました。
前提知識の解説
Go言語のCgo
Cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGo言語の機能です。Goのソースファイル内にimport "C"
という特殊なインポート文を記述し、その後にC言語のコードを直接埋め込むことで、GoとCの相互運用を可能にします。Cgoは、GoとCの異なる型システムやメモリ管理、関数呼び出し規約の間の橋渡しを行います。
6c (Goの内部Cコンパイラ)
6c
は、Go言語の初期のツールチェーンに含まれていたCコンパイラの一つです。Goプロジェクトは、独自のコンパイラ群(6g
for Go, 6c
for C, 6a
for assemblerなど)を持っていました。これらはGo言語の特定のニーズに合わせて設計されており、クロスコンパイルの容易さや、Goランタイムとの密接な連携を可能にしていました。しかし、6c
は標準的なCコンパイラ(GCCなど)とは異なる挙動や最適化を行うことがあり、特にABI(Application Binary Interface)の面で非互換性が生じることがありました。
GCC (GNU Compiler Collection)
GCCは、C、C++、Objective-C、Fortran、Ada、Goなどの多くのプログラミング言語をサポートする、広く使われているコンパイラコレクションです。Go言語のプロジェクトでは、特定のプラットフォームや環境で6c
が利用できない場合や、既存のCライブラリとの連携のためにgcc
を使用する必要がありました。
GoとCの関数呼び出し規約の違い(特に複数戻り値)
Go言語の大きな特徴の一つは、関数が複数の値を戻り値として返せることです。例えば、func foo() (int, error)
のように記述できます。しかし、C言語の関数は通常、単一の戻り値しか持ちません。CgoがGoの複数戻り値をC側で扱う場合、いくつかの方法が考えられます。
- 構造体による戻り値: 複数の戻り値をCの構造体としてまとめ、その構造体を返す。
- ポインタ引数による戻り値: 戻り値を格納するためのポインタを引数として渡し、関数内でそのポインタが指すメモリに値を書き込む。
6c
とgcc
は、これらのアプローチにおいて異なるABIを採用していたため、同じGoの関数シグネチャから生成されるCの関数定義が異なり、直接的な相互運用が困難でした。このコミットは、この歴史的な非互換性に対処するために導入されました。
.cgo
ファイルとプリプロセッサ/トランスレータ
このコミットで導入される.cgo
ファイルは、Goの関数シグネチャとC言語のコードブロックを組み合わせた特殊な形式のソースファイルです。cgo2c
は、この.cgo
ファイルを読み込み、Goの関数シグネチャをC言語の関数プロトタイプと定義に変換し、埋め込まれたCコードをそのまま出力するプリプロセッサ(またはトランスレータ)として機能します。これにより、Goの複数戻り値の概念をCの構造体やポインタ引数にマッピングする処理をcgo2c
が担当し、最終的に生成されるCファイルは6c
とgcc
のどちらでも正しくコンパイルできるようになります。
技術的詳細
cgo2c
プログラムは、標準入力から.cgo
ファイルを読み込み、標準出力にC言語のソースコードを出力するシンプルなコマンドラインツールです。その主な機能は以下の通りです。
- Goパッケージ名の読み込み:
.cgo
ファイルの先頭にあるpackage PACKAGENAME
を読み込み、C言語の関数名にGoのパッケージ名をプレフィックスとして付与するために使用します(例:package·functionName
)。 - プリプロセッサディレクティブのコピー:
# line
のようなプリプロセッサディレクティブをそのまま出力します。 - Go関数シグネチャの解析:
func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] {
の形式で記述されたGoの関数定義を解析します。- 関数名、引数リスト、戻り値リストを抽出します。
- Goの型(例:
*byte
)をCの型(例:byte *
)に変換します。
- C言語関数定義の生成:
6c
向け:void\npackage·name(param1 type1, param2 type2, ...)
の形式で関数を定義します。Goの複数戻り値は、Cの引数リストの末尾にポインタとして追加されます。関数本体のCコードの後に、戻り値のポインタをフラッシュするFLUSH
マクロが挿入されます。gcc
向け: Goの複数戻り値がある場合、それらを格納するためのCのstruct
型を定義します(例:struct package_name_ret { type1 name1; type2 name2; };
)。関数は、この構造体を戻り値として返すように定義されます。Goの関数名とCの関数名をasm
キーワードでマッピングします(例:void package_name() asm ("package.name");
)。
- Cコードブロックのコピー: Go関数シグネチャの後に続くCコードブロック(
{...}
で囲まれた部分)を、ネストされたブレース、コメント、文字列リテラルを正しく処理しながら、そのまま出力します。これにより、Goの関数内でCのロジックを自由に記述できます。 - コマンドライン引数によるコンパイラ選択:
cgo2c
は--6g
または--gcc
というコマンドライン引数を受け取り、どちらのコンパイラ向けのCコードを生成するかを切り替えます。これにより、ビルドシステムが適切なコンパイラに合わせてcgo2c
の出力を調整できます。
このメカニズムにより、Goのランタイムコードは、Goの複数戻り値のセマンティクスを維持しつつ、6c
とgcc
の両方でコンパイル可能な単一のCソースファイルとして記述できるようになりました。
src/runtime/Makefile
の変更は、このcgo2c
ツールのビルドと利用をビルドプロセスに組み込むものです。
cgo2c: cgo2c.c
ルールが追加され、cgo2c.c
からcgo2c
実行ファイルをビルドします。%.c: %.cgo cgo2c
ルールが追加され、.cgo
ファイルがcgo2c
によって対応する.c
ファイルに変換されるようにします。clean
ターゲットにcgo2c
の削除が追加されます。
src/runtime/malloc.c
からGo関数スタブが削除され、それらがsrc/runtime/malloc_go.cgo
に移行されたのは、malloc
関連のGoとCの混在コードをcgo2c
の管理下に置くためです。これにより、malloc
のGoインターフェースが、cgo2c
によって生成されるCコードを介して、6c
とgcc
の両方で一貫して動作するようになります。
コアとなるコードの変更箇所
このコミットの主要な変更は以下のファイルに集中しています。
src/runtime/cgo2c.c
(新規追加):cgo2c
プログラムのC言語ソースコード。GoとCの混在コードをCコードに変換するロジックが実装されています。src/runtime/Makefile
(変更):cgo2c
のビルドと、.cgo
ファイルを.c
ファイルに変換するルールが追加されました。src/runtime/malloc.c
(変更):malloc_Alloc
,malloc_Free
,malloc_Lookup
,malloc_GetStats
といったGo関数スタブが削除されました。これらの機能はcgo2c
を介して提供されるようになります。src/runtime/malloc.h
(変更):malloc
,free
,mlookup
の関数プロトタイプが追加されました。src/runtime/malloc_go.cgo
(新規追加):malloc
関連のGo関数とCコードを混在させた新しいソースファイル。cgo2c
によって処理されます。
コアとなるコードの解説
src/runtime/cgo2c.c
このファイルは、Goの関数シグネチャとCコードを解析し、ターゲットコンパイラ(6c
またはgcc
)に応じたCコードを生成する主要なロジックを含んでいます。
main
関数:- コマンドライン引数
--6g
または--gcc
を解析し、flag_gcc
を設定します。 process_file
関数を呼び出し、ファイル全体の処理を開始します。
- コマンドライン引数
process_file
関数:read_package()
でGoのパッケージ名を読み込みます。read_preprocessor_lines()
でプリプロセッサディレクティブをコピーします。- ループ内で
read_func_header()
を呼び出し、Goの関数定義を一つずつ解析します。 - 解析した関数情報(名前、引数、戻り値)に基づいて、
write_func_header()
でCの関数ヘッダを生成します。 copy_body()
でGo関数内のCコードブロックをコピーします。write_func_trailer()
でCの関数トレーラ(戻り値の処理など)を生成します。- 解析したメモリを解放します。
read_token
/read_token_no_eof
:- 入力ストリームからトークン(識別子、区切り文字など)を読み込みます。
- コメント(
//
と/* */
)をスキップするロジックが含まれています。
read_type
:- Goの型表現(例:
*byte
)をCの型表現(例:byte *
)に変換します。ポインタの数を正しく処理します。
- Goの型表現(例:
read_params
:- Goの関数引数または戻り値のリストを解析し、名前と型のペアのリンクリストとして返します。
read_func_header
:func
キーワードから始まり、関数名、引数リスト、戻り値リスト、そして{
までのGo関数シグネチャを解析します。
write_6g_func_header
/write_6g_func_trailer
:6c
コンパイラ向けのC関数ヘッダとトレーラを生成します。Goの複数戻り値は、Cの引数として渡されるポインタとして扱われ、トレーラでFLUSH
マクロが呼び出されます。
define_gcc_return_type
/write_gcc_return_type
/write_gcc_func_header
/write_gcc_func_trailer
:gcc
コンパイラ向けのC関数ヘッダとトレーラを生成します。- Goの複数戻り値がある場合、それらをカプセル化するためのCの
struct
型を定義し、関数はその構造体を戻り値として返します。 asm
キーワードを使用して、Goの関数名とCの関数名をマッピングします。
copy_body
:- Go関数定義内のCコードブロックを、ブレースのネストレベルを考慮しながら、そのまま出力します。文字列リテラルやコメント内のブレースを誤って解釈しないように注意深く処理されます。
src/runtime/malloc_go.cgo
このファイルは、cgo2c
によって処理されるGoとCの混在コードの具体例です。
package malloc
#include "runtime.h"
#include "malloc.h"
func Alloc(n uintptr) (p *byte) {
p = malloc(n);
}
func Free(p *byte) {
free(p);
}
func Lookup(p *byte) (base *byte, size uintptr) {
mlookup(p, &base, &size);
}
func GetStats() (s *MStats) {
s = &mstats;
}
このファイルでは、malloc
パッケージのGo関数Alloc
, Free
, Lookup
, GetStats
が定義されています。それぞれのGo関数の中身はC言語のコードで記述されており、malloc
, free
, mlookup
といったCの関数を呼び出しています。特にLookup
関数はbase
とsize
という2つの戻り値を持っており、cgo2c
がこれを6c
とgcc
の両方で正しく扱えるCコードに変換する役割を担います。
関連リンク
特になし。
参考にした情報源リンク
- Go言語のCgoに関する公式ドキュメント (現在のCgoの動作は、このコミット当時のものとは異なる可能性があります)
- Go言語の初期のコンパイラに関する議論やドキュメント (当時の情報を見つけるのは困難なため、一般的なCgoの概念に限定)
- GCCのABIに関する一般的な情報
cgo2c.c
のソースコード自体の詳細な分析- Go language 6c compiler vs gcc return value incompatibility - Google Search