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

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

このコミットは、Go言語のmisc/cgo/testディレクトリ内のテストスイートにおいて、gccgoコンパイラを使用した場合にテストが正しくパスするようにするための変更を導入しています。具体的には、cgoテストの一部がgcコンパイラとgccgoコンパイラで異なる振る舞いをする可能性があったため、コンパイラ固有のビルドタグ(+build gc+build gccgo)を活用して、それぞれのコンパイラに合わせた実装を提供することで、テストの互換性と信頼性を向上させています。

コミット

commit 538a58bb75ea86be27ba24597c4f45f0e84969ea
Author: Ian Lance Taylor <iant@golang.org>
Date:   Thu Nov 1 13:54:09 2012 -0700

    misc/cgo/test: changes to pass when using gccgo
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6821067

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

https://github.com/golang/go/commit/538a58bb75ea86be27ba24597c4f45f0e84969ea

元コミット内容

misc/cgo/test: changes to pass when using gccgo

変更の背景

Go言語には、主に2つの主要なコンパイラ実装が存在します。一つは公式のGoツールチェインでデフォルトで使用されるgc(Go Compiler)であり、もう一つはGCC(GNU Compiler Collection)のフロントエンドとして実装されているgccgoです。これら二つのコンパイラは、Go言語の仕様を実装していますが、内部的な最適化やランタイムの挙動に違いがある場合があります。

このコミットが行われた背景には、misc/cgo/testディレクトリ内のテストが、gcコンパイラでは問題なく動作するものの、gccgoコンパイラでは失敗する、あるいは意図しない結果を返すという問題があったと考えられます。特にcgo(GoとC言語の相互運用機能)に関連するテストは、Cコンパイラとの連携や、GoランタイムとCランタイムの間のインタフェースに依存するため、コンパイラの実装の違いが顕著に現れやすい領域です。

このコミットの目的は、gccgo環境下でもcgoテストが期待通りに動作し、Go言語の異なるコンパイラ実装間での互換性とテストカバレッジを保証することにあります。これは、Go言語の堅牢性を維持し、異なるビルド環境での一貫した動作を保証するために不可欠な作業です。

前提知識の解説

1. Go言語のコンパイラ: gcgccgo

  • gc (Go Compiler):

    • Go言語の公式ツールチェインに同梱されているデフォルトのコンパイラです。
    • Go言語の設計思想に合わせて最適化されており、高速なコンパイルが特徴です。
    • Go言語の新しい機能が最初に実装されることが多く、通常はgccgoよりもGo言語の最新機能への対応が先行します。
    • 生成されるバイナリは通常、静的にリンクされ、Goランタイムを含みます。
  • gccgo:

    • GCC(GNU Compiler Collection)の一部として提供されるGo言語のフロントエンドです。
    • GCCの強力な最適化バックエンドを利用できるため、CPUバウンドなアプリケーションではgcよりも高速な実行ファイルを生成する可能性があります。
    • GCCがサポートする幅広いアーキテクチャやオペレーティングシステムに対応しています。
    • libgoライブラリを動的にリンクすることで、より小さなバイナリを生成することも可能です。

両コンパイラはGo言語の同じ仕様を実装していますが、内部的な実装やランタイムの挙動に差異があるため、特に低レベルな処理やC言語との連携(cgo)においては、コンパイラ固有の調整が必要になることがあります。

2. cgo

cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoツールです。Goのソースファイルがimport "C"という擬似パッケージをインポートしている場合、そのファイルはcgoによって処理されます。cgoは、GoとCの間のデータ型変換や関数呼び出しの橋渡しを行い、GoプログラムにCライブラリの機能を取り込むことを可能にします。

3. Goのビルドタグ (// +build または //go:build)

Goのビルドタグ(またはビルド制約)は、Goソースファイルの先頭に記述される特別なコメントで、そのファイルが特定のビルド条件を満たす場合にのみコンパイル対象となるように指定します。これにより、オペレーティングシステム、アーキテクチャ、またはカスタムタグに基づいて、異なるコードパスを条件付きでコンパイルすることが可能になります。

  • 構文:
    • Go 1.17以降: //go:build <expression>
    • Go 1.16以前: // +build <expression> (後方互換性のために現在もサポートされています)
  • :
    • // +build linux:Linuxシステムでのみコンパイル。
    • // +build windows,amd64:WindowsかつAMD64アーキテクチャでのみコンパイル。
    • // +build gcgcコンパイラでビルドされる場合のみコンパイル。
    • // +build gccgogccgoコンパイラでビルドされる場合のみコンパイル。

このコミットでは、+build gc+build gccgoというビルドタグが使用されており、それぞれのコンパイラに特化したコードを条件付きで含めることで、コンパイラ間の差異を吸収しています。

技術的詳細

このコミットの技術的な核心は、Goのビルドタグを利用して、gcコンパイラとgccgoコンパイラそれぞれに特化したruntime.cの実装を提供することです。

Goのテストスイート、特にcgoに関連するテストでは、Goランタイムの内部関数や挙動にアクセスする必要がある場合があります。しかし、gcgccgoでは、これらの内部関数の名前付け規則や、C言語からGoランタイムの関数を呼び出す際のアセンブリレベルでのリンケージが異なることがあります。

misc/cgo/test/backdoorパッケージは、Goランタイムの特定の関数(この場合はruntime·lockedOSThread)をC言語側から「バックドア」的に呼び出すためのテストユーティリティとして機能します。これは、Goランタイムの低レベルな挙動をテストするために用いられます。

このコミットでは、以下の戦略が取られています。

  1. 既存のruntime.cの限定: 既存のmisc/cgo/test/backdoor/runtime.cファイルに// +build gcというビルドタグを追加することで、このファイルがgcコンパイラでビルドされる場合にのみコンパイルされるように変更しました。これにより、gcコンパイラ特有のランタイム関数へのアクセス方法がこのファイルにカプセル化されます。

  2. gccgo専用のruntime_gccgo.cの導入: 新たにmisc/cgo/test/backdoor/runtime_gccgo.cというファイルを作成し、これに// +build gccgoというビルドタグを付与しました。このファイルはgccgoコンパイラでビルドされる場合にのみコンパイルされます。このファイル内では、gccgoのランタイムが提供する_Bool runtime_lockedOSThread(void);という関数を、Go側から呼び出しやすいようにLockedOSThreadという名前でラップしています。asm(GOPKGPATH ".LockedOSThread")という構文は、Goのリンカに対して、このC関数がGoパッケージのLockedOSThreadとしてエクスポートされることを指示しています。

このアプローチにより、gcでビルドする際にはruntime.cが、gccgoでビルドする際にはruntime_gccgo.cがそれぞれコンパイルされ、各コンパイラのランタイム実装の差異を吸収し、LockedOSThreadという共通のインタフェースを通じてテストコードがランタイム機能にアクセスできるようになります。これにより、gccgo環境下でもcgoテストが正しく動作するようになります。

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

このコミットでは、以下の2つのファイルが変更されています。

  1. misc/cgo/test/backdoor/runtime.c:

    --- a/misc/cgo/test/backdoor/runtime.c
    +++ b/misc/cgo/test/backdoor/runtime.c
    @@ -6,6 +6,8 @@
     // Must be in a non-cgo-using package so that
     // the go command compiles this file with 6c, not gcc.
     
    +// +build gc
    +
     typedef char bool;
     
     bool runtime·lockedOSThread(void);
    
    • 6行目に// +build gcが追加されました。
  2. misc/cgo/test/backdoor/runtime_gccgo.c: (新規ファイル)

    --- /dev/null
    +++ b/misc/cgo/test/backdoor/runtime_gccgo.c
    @@ -0,0 +1,18 @@
    +// Copyright 2012 The Go Authors.  All rights reserved.
    +// Use of this source code is governed by a BSD-style
    +// license that can be found in the LICENSE file.
    +
    +// Expose some runtime functions for testing.
    +// This is the gccgo version of runtime.c.
    +
    +// +build gccgo
    +
    +_Bool runtime_lockedOSThread(void);
    +
    +_Bool LockedOSThread(void) asm(GOPKGPATH ".LockedOSThread");
    +
    +_Bool
    +LockedOSThread(void)
    +{
    +	return runtime_lockedOSThread();
    +}
    
    • 新規ファイルとして追加され、// +build gccgoタグが付与されています。
    • runtime_lockedOSThreadを呼び出すLockedOSThread関数が定義されています。

コアとなるコードの解説

misc/cgo/test/backdoor/runtime.c

// +build gc

typedef char bool;

bool runtime·lockedOSThread(void);

このファイルは、Goのデフォルトコンパイラであるgcでビルドされる場合にのみコンパイルされます。 // +build gcという行がその役割を果たしています。 runtime·lockedOSThreadは、gcコンパイラが生成するGoランタイムの内部関数runtime.lockedOSThreadに対応するC言語側の宣言です。Goの内部関数は、Goのリンカが認識できるように特殊な命名規則(パッケージ名·関数名)を持つことがあります。この宣言により、CコードからGoランタイムのこの関数を呼び出すことが可能になります。

misc/cgo/test/backdoor/runtime_gccgo.c

// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Expose some runtime functions for testing.
// This is the gccgo version of runtime.c.

// +build gccgo

_Bool runtime_lockedOSThread(void);

_Bool LockedOSThread(void) asm(GOPKGPATH ".LockedOSThread");

_Bool
LockedOSThread(void)
{
	return runtime_lockedOSThread();
}

このファイルは、gccgoコンパイラでビルドされる場合にのみコンパイルされます。 // +build gccgoという行がその役割を果たしています。 _Bool runtime_lockedOSThread(void);は、gccgoコンパイラが生成するGoランタイムの内部関数runtime.lockedOSThreadに対応するC言語側の宣言です。gccgoでは、gcとは異なるC言語の型(_Bool)や命名規則が使用される場合があります。

_Bool LockedOSThread(void) asm(GOPKGPATH ".LockedOSThread");の行は特に重要です。

  • _Bool LockedOSThread(void): LockedOSThreadというC関数を宣言しています。
  • asm(GOPKGPATH ".LockedOSThread"): これはGCCの拡張機能であり、このC関数がGoのパッケージパス(GOPKGPATH)と関数名(.LockedOSThread)を組み合わせたシンボル名でエクスポートされることをリンカに指示しています。これにより、Go側のテストコードがLockedOSThreadという名前でこのC関数を呼び出すことができるようになります。

そして、LockedOSThread関数の実装は、単にgccgoランタイムのruntime_lockedOSThreadを呼び出してその結果を返しています。

これらの変更により、gcgccgoという異なるコンパイラ環境下でも、misc/cgo/test/backdoorパッケージ内のテストが、GoランタイムのlockedOSThread機能に一貫した方法でアクセスできるようになり、テストの信頼性が向上しました。

関連リンク

参考にした情報源リンク