[インデックス 16528] ファイルの概要
このコミットは、Go言語のcgo
ツールに関連する変更です。具体的には、src/cmd/cgo/out.go
ファイルが修正されています。このファイルは、cgo
がCコードをGoコードと連携させるために生成するCのラッパーコードの出力ロジックを扱っています。特に、構造体のメモリレイアウトやアライメントに関する属性の付与に関わる部分が変更されています。
コミット
cmd/cgo
: __gcc_struct__
属性を使用しないことでclangを満足させる。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b637135003cd1b3dbc3cffe2c63752888713503d
元コミット内容
cmd/cgo: makes clang happy by not using __gcc_struct__ attribute.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/10150043
変更の背景
このコミットの背景には、cgo
が生成するCコードが特定のコンパイラ(特にClang)で問題を引き起こす可能性があったという事情があります。
元々、cgo
はGoの構造体とCの構造体間でデータをやり取りする際に、メモリレイアウトの一貫性を保つために__attribute__((__packed__))
というGCC拡張属性を使用していました。さらに、特定のGCCのバグ(GCC PR52991)やGoの内部的な問題(Go issue 5603)を回避するために、__gcc_struct__
という属性も併用されていました。
しかし、この__gcc_struct__
属性はGCC特有のものであり、Clangのような他のCコンパイラでは認識されないか、異なる解釈をされる可能性がありました。結果として、Clangでコンパイルする際に警告やエラーが発生し、ビルドプロセスが中断される、あるいは予期せぬ動作を引き起こす可能性がありました。
このコミットは、Clangを使用している環境でもcgo
が生成するコードが問題なくコンパイルされるように、__gcc_struct__
属性の適用をGCCに限定することで、コンパイラの互換性問題を解決することを目的としています。
前提知識の解説
cgo
cgo
はGo言語に組み込まれているツールで、GoプログラムからC言語のコードを呼び出したり、逆にC言語のコードからGoの関数を呼び出したりするためのメカニズムを提供します。これにより、既存のCライブラリをGoプロジェクトで再利用したり、パフォーマンスが重要な部分をCで記述したりすることが可能になります。cgo
は、GoとCの間のデータ型変換や関数呼び出しのための接着コード(binding code)を生成します。
__attribute__((__packed__))
これはGCC(GNU Compiler Collection)およびClangがサポートするコンパイラ拡張属性の一つです。構造体や共用体にこの属性を付与すると、コンパイラはメンバー間のパディング(メモリのアライメントのために挿入される未使用バイト)を最小限に抑え、可能な限りメモリを密に配置しようとします。これにより、構造体のサイズが小さくなり、CとGoのような異なる言語間で構造体を共有する際に、メモリレイアウトの不一致による問題を避けるのに役立ちます。
__gcc_struct__
属性
これもGCCのコンパイラ拡張属性の一つです。この属性は、特定の構造体に対してGCCのデフォルトの構造体パディングルールを強制するために使用されることがあります。通常、__packed__
属性と組み合わせて使用されることが多く、特定のコンパイラのバグや挙動の差異を回避するために用いられることがあります。このコミットの文脈では、GCCの特定のバグ(PR52991)とGoの関連する問題(issue 5603)のワークアラウンドとして使用されていました。
GCC PR52991
これはGCCのバグトラッカーに報告された問題で、printf
やscanf
といった標準入出力関数が__int128
型(128ビット整数型)を正しく扱えないというものです。このバグは、特に__int128
型を含む構造体が__packed__
属性を持つ場合に、メモリレイアウトの解釈に影響を与える可能性がありました。__gcc_struct__
属性は、この問題に対する特定のワークアラウンドとして導入された可能性があります。
Go issue 5603
このGoのイシューは、cgo
と構造体のパディング、特に__packed__
属性に関連する問題を示唆しています。具体的な内容はコミットメッセージからは読み取れませんが、GCC PR52991と同様に、異なるコンパイラやアーキテクチャ間での構造体のアライメントとパディングの挙動の不一致が原因で発生する問題であったと推測されます。
Clang
Clangは、LLVMプロジェクトの一部として開発されているC、C++、Objective-C、Objective-C++コンパイラのフロントエンドです。GCCと互換性のある多くのコンパイラ拡張をサポートしていますが、すべてのGCC拡張を完全にサポートしているわけではありません。特に、__gcc_struct__
のようなGCC固有の属性は、Clangでは認識されないか、異なる警告やエラーを引き起こす可能性があります。
技術的詳細
このコミットが行われる前は、cgo
はGoのamd64
および386
アーキテクチャ向けにCコードを生成する際に、常に__attribute__((__packed__, __gcc_struct__))
という属性を構造体に付与していました。これは、前述のGCCのバグ(PR52991)とGoのイシュー(5603)に対するワークアラウンドとして機能していました。
しかし、このアプローチはClangコンパイラを使用する環境で問題を引き起こしました。Clangは__gcc_struct__
属性を認識しないため、コンパイル時に警告やエラーを発生させ、ビルドの失敗につながる可能性がありました。
このコミットの解決策は、__gcc_struct__
属性を適用する条件をより厳密にすることです。具体的には、cgo
が現在使用しているCコンパイラがClangではない場合にのみ、この属性を付与するように変更されました。これにより、GCCを使用している環境では引き続きワークアラウンドが適用され、Clangを使用している環境では不要な属性が取り除かれ、コンパイルが正常に行われるようになります。
この変更は、cgo
が内部的に使用するCコンパイラの名前をチェックし、その名前が"clang"を含まない場合にのみ__gcc_struct__
属性を追加するというロジックによって実現されています。
コアとなるコードの変更箇所
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -488,7 +488,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
// Use __gcc_struct__ to work around http://gcc.gnu.org/PR52991 on x86,
// and http://golang.org/issue/5603.
extraAttr := ""
- if goarch == "amd64" || goarch == "386" {
+ if !strings.Contains(p.gccName(), "clang") && (goarch == "amd64" || goarch == "386") {
extraAttr = ", __gcc_struct__"
}
fmt.Fprintf(fgcc, "\t%s __attribute__((__packed__%v)) *a = v;\\n", ctype, extraAttr)
コアとなるコードの解説
変更はsrc/cmd/cgo/out.go
ファイルのwriteOutputFunc
関数内で行われています。
元のコードでは、extraAttr
という文字列変数が、goarch
(Goのターゲットアーキテクチャ)がamd64
または386
の場合にのみ", __gcc_struct__"
という値に設定されていました。これは、x86系のアーキテクチャでGCCのバグとGoのイシューを回避するためのものでした。
変更後のコードでは、この条件に新たなチェックが追加されています。
!strings.Contains(p.gccName(), "clang")
p.gccName()
: これはcgo
が現在使用しているCコンパイラの名前を返すメソッドです。例えば、"gcc"や"clang"といった文字列が返されます。strings.Contains(p.gccName(), "clang")
: これは、コンパイラ名に"clang"という部分文字列が含まれているかどうかをチェックします。!strings.Contains(...)
: この!
は論理否定であり、「コンパイラ名に"clang"が含まれていない場合」という条件を意味します。
したがって、新しいif
文の条件は以下のようになります。
「使用しているCコンパイラがClangではなく、かつターゲットアーキテクチャがamd64
または386
である場合」
この条件が真である場合にのみ、extraAttr
に", __gcc_struct__"
が設定され、結果として生成されるCコードの構造体に__gcc_struct__
属性が付与されます。これにより、Clangを使用している環境ではこの属性が生成されなくなり、コンパイルエラーや警告が回避されるようになります。
関連リンク
- Go issue 5603: https://golang.org/issue/5603 (このリンクはコミットメッセージに記載されていますが、現在のGoのイシュートラッカーでは直接見つからない可能性があります。古いイシューはアーカイブされているか、別の番号に統合されている場合があります。)
- GCC Bug 52991: http://gcc.gnu.org/PR52991 (これは
__int128
とprintf
/scanf
に関するバグです。) - Go CL 10150043: https://golang.org/cl/10150043 (Gerrit Code Reviewの変更リストへのリンク)
参考にした情報源リンク
- GCC Bugzilla: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 (GCC PR52991の詳細情報)
- Go issue tracker: https://github.com/golang/go/issues (Goのイシューを検索するための一般的なリンク)
- GCC Attributes: https://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html (GCCの属性に関する公式ドキュメント)
- Clang Attributes: https://clang.llvm.org/docs/AttributeReference.html (Clangの属性に関する公式ドキュメント)
strings.Contains
Go documentation: https://pkg.go.dev/strings#Contains