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

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

このコミットは、Goコンパイラの一部である cmd/gc における builtin.c ファイルの再構築に関するものです。具体的には、builtin.cruntime.go と同期していなかった状態を修正し、いくつかの組み込み関数のシグネチャが更新されています。

コミット

commit 922c0b47557db7d686bfebe0e128a7a1dd6116e3
Author: Russ Cox <rsc@golang.org>
Date:   Sun Oct 21 17:15:56 2012 -0400

    cmd/gc: rebuild builtin.c
    
    Was not in sync with runtime.go, but the diffs
    didn't really matter, so nothing broke.
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/6733057

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

https://github.com/golang/go/commit/922c0b47557db7d686bfebe0e128a7a1dd6116e3

元コミット内容

cmd/gc: rebuild builtin.c

Was not in sync with runtime.go, but the diffs
didn't really matter, so nothing broke.

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

変更の背景

このコミットの主な背景は、Goコンパイラの cmd/gc ディレクトリにある builtin.c ファイルが、Goランタイムの runtime.go ファイルと同期が取れていなかったことです。コミットメッセージによると、この同期のずれは「差分が実際には重要ではなかったため、何も壊れていなかった」とされています。しかし、コードベースの一貫性と正確性を保つために、この builtin.cruntime.go の最新の状態に合わせて再構築する必要がありました。

builtin.c は、Go言語の組み込み関数(append, copy, new など)の内部的な宣言や、コンパイラがこれらの関数をどのように扱うかを定義するC言語のファイルです。一方、runtime.go はGoランタイムの主要な部分を定義しており、Goプログラムの実行環境、ガベージコレクション、ゴルーチンのスケジューリングなどを管理します。これら二つのファイルは密接に関連しており、Go言語の組み込み関数の動作や、それらがランタイムとどのように連携するかに影響を与えます。

同期のずれは、Go言語の進化に伴う内部的な変更、特にランタイムの型定義や関数のシグネチャの変更が builtin.c に適切に反映されていなかったことを示唆しています。このコミットは、将来的な問題を防ぎ、コンパイラとランタイム間の整合性を確保するためのメンテナンス作業の一環として行われました。

前提知識の解説

このコミットを理解するためには、以下のGo言語の内部構造と型に関する知識が役立ちます。

cmd/gcbuiltin.c

cmd/gc はGo言語の公式コンパイラの一部です。Goのソースコードを機械語に変換する役割を担っています。builtin.c は、このコンパイラ内部で使用されるC言語のファイルで、Go言語の組み込み関数(例: append, copy, len など)の内部的な宣言や、それらの関数がコンパイラによってどのように処理されるかに関する情報を含んでいます。これらの組み込み関数は、Go言語の仕様の一部であり、明示的なインポートなしに利用できます。コンパイラは、これらの組み込み関数への呼び出しを、より最適化された内部関数やランタイム関数への呼び出しに変換することがよくあります。

runtime.go

runtime.go はGoランタイムの主要なソースファイルの一つです。Goランタイムは、Goプログラムの実行に不可欠な部分であり、ゴルーチンのスケジューリング、ガベージコレクション、メモリ管理、チャネル操作、システムコールとの連携など、低レベルの機能を提供します。Goランタイムは主にGo言語で書かれていますが、一部の低レベルな処理にはC言語やアセンブリ言語も使用されます。builtin.cruntime.go は、Goの組み込み関数がどのように実装され、ランタイムとどのように連携するかという点で密接に関連しています。

uint32uintptr

Go言語には、様々な数値型があります。

  • uint32: 符号なし32ビット整数型です。0から4,294,967,295までの値を保持できます。特定のビット幅が必要な場合(例: ネットワークプロトコル、ファイルフォーマット、C言語との相互運用)に使用されます。
  • uintptr: ポインタのビットパターンを保持するのに十分な大きさの符号なし整数型です。これは、メモリアドレスを数値として表現するために使用されます。通常のGoのポインタ (*T) とは異なり、uintptr は型情報を持たず、Goのガベージコレクタによって追跡されません。主に低レベルのプログラミング(unsafe パッケージと組み合わせて直接メモリを操作する場合など)や、Cライブラリとのインターフェースで使用されます。uintptr のサイズは、実行環境のポインタサイズ(32ビットシステムでは32ビット、64ビットシステムでは64ビット)に依存します。

このコミットでは、copy 関数の内部的な引数型が uint32 から uintptr に変更されています。これは、コピー操作のサイズ指定が、固定長の32ビット整数から、ポインタサイズに合わせたより柔軟な型に変更されたことを意味します。これにより、特に64ビットシステムでのメモリ操作の正確性が向上する可能性があります。

intint32

Go言語の整数型には、以下のような違いがあります。

  • int: 符号付き整数型で、そのサイズはプラットフォームに依存します。32ビットシステムでは32ビット、64ビットシステムでは64ビットの幅を持ちます。Go言語では、特に指定がない限り、ほとんどの場面で int を使用することが推奨されます。
  • int32: 符号付き32ビット整数型で、常に32ビットの幅を持ちます。-2,147,483,648から2,147,483,647までの値を保持できます。int32 のような固定サイズの整数型は、C言語との相互運用、バイナリデータフォーマットの処理、または特定のメモリ最適化が必要な場合に主に使用されます。

Goの型システムは厳格であり、intint32 は異なる型として扱われます。暗黙的な型変換は行われず、必要に応じて明示的な型変換を行う必要があります。このコミットでは、newselect 関数の内部的な引数型が int から int32 に変更されています。これは、newselect が扱うサイズ情報が、プラットフォーム依存の int ではなく、常に32ビットの int32 として扱われるようになったことを示しています。これにより、異なるアーキテクチャ間での挙動の一貫性が向上する可能性があります。

Goの select ステートメントと newselect システムコール

Go言語には、チャネル操作のための select ステートメントがあります。これは、複数のチャネル操作のうち、準備ができたものを待機し、実行するための高レベルな並行処理プリミティブです。

一方、newselect は、Linuxなどのオペレーティングシステムにおけるシステムコールの一つで、I/O多重化(複数のファイルディスクリプタを監視し、I/O操作の準備ができたものを検出する)のために使用されます。これは、Goの select ステートメントとは直接関係ありません。Goランタイムは、内部的に epoll (Linux), kqueue (macOS/BSD), select/newselect (古いシステムや特定のアーキテクチャ) といった効率的なI/O多重化システムコールを使用してI/Oイベントを処理しますが、Goの select ステートメント自体は、Goランタイムのスケジューラによって管理される高レベルな抽象化です。

このコミットで変更されている newselect は、Goランタイム内部の関数であり、おそらくGoの select ステートメントの内部実装や、ランタイムがI/O多重化を扱うための低レベルなメカニズムに関連していると考えられます。

技術的詳細

このコミットは、src/cmd/gc/builtin.c ファイルに対して行われた変更であり、GoコンパイラがGoの組み込み関数をどのように認識し、処理するかを更新しています。変更は主に以下の3点です。

  1. slicestring 関連関数の削除:

    • func @\"\\\".slicestring(? string, ? int, ? int) (? string)\\n"
    • func @\"\\\".slicestring1(? string, ? int) (? string)\\n" これらの関数は builtin.c から削除されました。Go言語において文字列のスライスは [low:high] 構文によって直接行われ、slicestring という名前の組み込み関数は存在しません。これらの関数は、Goコンパイラの初期開発段階における内部的な実装の一部であった可能性があり、Go言語の進化に伴い不要になったか、より効率的な内部メカニズムに置き換えられたと考えられます。Goの文字列スライスは、言語の基本的な機能として提供されており、内部的には最適化されたランタイム関数によって処理されます。
  2. copy 関数の引数型の変更:

    • 変更前: func @\"\\\".copy(@\"\\\".to any, @\"\\\".fr any, @\"\\\".wid uint32) (? int)\\n"
    • 変更後: func @\"\\\".copy(@\"\\\".to any, @\"\\\".fr any, @\"\\\".wid uintptr) (? int)\\n" copy 組み込み関数の内部的な宣言において、第3引数である wid の型が uint32 から uintptr に変更されました。
    • uint32 から uintptr への変更の理由: copy 関数は、スライスや文字列の要素をコピーする際に、コピーするバイト数や要素数を指定するためにこの wid 引数を使用します。uint32 は固定の32ビット幅ですが、uintptr はポインタのサイズに合わせた幅を持つ整数型です。64ビットシステムでは uintptr は64ビット幅になります。この変更は、特に64ビットアーキテクチャにおいて、より大きなメモリ領域を正確にコピーできるようにするため、またはメモリのアドレスやサイズを扱う際に、ポインタのサイズに合わせた型を使用することで、より効率的かつ安全なメモリ操作を可能にするためのものです。Goランタイムがメモリを直接操作する際に、uintptr を使用することは一般的であり、この変更はその慣習に合わせたものと考えられます。
  3. newselect 関数の引数型の変更:

    • 変更前: func @\"\\\".newselect(@\"\\\".size int) (@\"\\\".sel *byte)\\n"
    • 変更後: func @\"\\\".newselect(@\"\\\".size int32) (@\"\\\".sel *byte)\\n" newselect 関数の内部的な宣言において、第1引数である size の型が int から int32 に変更されました。
    • int から int32 への変更の理由: int 型はプラットフォーム依存のサイズ(32ビットまたは64ビット)を持ちますが、int32 は常に32ビットの固定サイズです。この変更は、newselect 関数が扱うサイズ情報が、異なるアーキテクチャ間でも一貫して32ビットとして扱われることを保証するためと考えられます。これは、Goランタイムが内部的に使用するデータ構造やシステムコールとのインターフェースにおいて、特定のビット幅を要求される場合に重要となります。例えば、特定のシステムコールが32ビットの整数を期待する場合、int が64ビットシステムで64ビットになってしまうと問題が発生する可能性があります。int32 を使用することで、このようなクロスプラットフォームでの互換性や予測可能性が向上します。

これらの変更は、Goコンパイラとランタイムの内部的な整合性を保ち、Go言語の進化に合わせて低レベルの最適化と正確性を向上させるためのものです。

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

--- a/src/cmd/gc/builtin.c
+++ b/src/cmd/gc/builtin.c
@@ -28,8 +28,6 @@ char *runtimeimport =
 	"func @\"\\\".appendstr(@\"\\\".typ *byte, @\"\\\".x []byte, @\"\\\".y string) (? []byte)\\n"
 	"func @\"\\\".cmpstring(? string, ? string) (? int)\\n"
 	"func @\"\\\".eqstring(? string, ? string) (? bool)\\n"
-"	func @\"\\\".slicestring(? string, ? int, ? int) (? string)\\n"
-"	func @\"\\\".slicestring1(? string, ? int) (? string)\\n"
 	"func @\"\\\".intstring(? int64) (? string)\\n"
 	"func @\"\\\".slicebytetostring(? []byte) (? string)\\n"
 	"func @\"\\\".slicerunetostring(? []rune) (? string)\\n"
@@ -37,7 +35,7 @@ char *runtimeimport =
 	"func @\"\\\".stringtoslicerune(? string) (? []rune)\\n"
 	"func @\"\\\".stringiter(? string, ? int) (? int)\\n"
 	"func @\"\\\".stringiter2(? string, ? int) (@\"\\\".retk int, @\"\\\".retv rune)\\n"
-"	func @\"\\\".copy(@\"\\\".to any, @\"\\\".fr any, @\"\\\".wid uint32) (? int)\\n"
+"	func @\"\\\".copy(@\"\\\".to any, @\"\\\".fr any, @\"\\\".wid uintptr) (? int)\\n"
 	"func @\"\\\".slicestringcopy(@\"\\\".to any, @\"\\\".fr any) (? int)\\n"
 	"func @\"\\\".typ2Itab(@\"\\\".typ *byte, @\"\\\".typ2 *byte, @\"\\\".cache **byte) (@\"\\\".ret *byte)\\n"
 	"func @\"\\\".convI2E(@\"\\\".elem any) (@\"\\\".ret any)\\n"
@@ -79,7 +77,7 @@ char *runtimeimport =
 	"func @\"\\\".selectnbsend(@\"\\\".chanType *byte, @\"\\\".hchan chan<- any, @\"\\\".elem any) (? bool)\\n"
 	"func @\"\\\".selectnbrecv(@\"\\\".chanType *byte, @\"\\\".elem *any, @\"\\\".hchan <-chan any) (? bool)\\n"
 	"func @\"\\\".selectnbrecv2(@\"\\\".chanType *byte, @\"\\\".elem *any, @\"\\\".received *bool, @\"\\\".hchan <-chan any) (? bool)\\n"
-"	func @\"\\\".newselect(@\"\\\".size int) (@\"\\\".sel *byte)\\n"
+"	func @\"\\\".newselect(@\"\\\".size int32) (@\"\\\".sel *byte)\\n"
 	"func @\"\\\".selectsend(@\"\\\".sel *byte, @\"\\\".hchan chan<- any, @\"\\\".elem *any) (@\"\\\".selected bool)\\n"
 	"func @\"\\\".selectrecv(@\"\\\".sel *byte, @\"\\\".hchan <-chan any, @\"\\\".elem *any) (@\"\\\".selected bool)\\n"
 	"func @\"\\\".selectrecv2(@\"\\\".sel *byte, @\"\\\".hchan <-chan any, @\"\\\".elem *any, @\"\\\".received *bool) (@\"\\\".selected bool)\\n"

コアとなるコードの解説

このコミットは、src/cmd/gc/builtin.c ファイル内の runtimeimport というC言語の文字列リテラルを修正しています。この文字列は、GoコンパイラがGoランタイムの内部関数を認識するために使用する、特殊な形式の関数宣言のリストを含んでいます。

変更された各行について詳しく見ていきましょう。

  1. slicestring 関連関数の削除:

    -	"func @\"\\\".slicestring(? string, ? int, ? int) (? string)\\n"
    -	"func @\"\\\".slicestring1(? string, ? int) (? string)\\n"
    

    この2行は、slicestring および slicestring1 という名前の内部関数宣言を削除しています。これらの関数は、Go言語の文字列スライス操作に関連していたと考えられますが、Goの言語仕様では文字列スライスは [low:high] 構文によって直接サポートされており、これらの内部関数はもはや必要とされなくなったか、より抽象化された形で処理されるようになったことを示唆しています。Goコンパイラは、Goのソースコードを解析する際に、これらの内部関数宣言を参照して、適切なランタイム関数呼び出しに変換します。これらの宣言が削除されたということは、コンパイラがこれらの特定の内部関数を直接参照する必要がなくなったことを意味します。

  2. copy 関数の引数型の変更:

    -	"func @\"\\\".copy(@\"\\\".to any, @\"\\\".fr any, @\"\\\".wid uint32) (? int)\\n"
    +	"func @\"\\\".copy(@\"\\\".to any, @\"\\\".fr any, @\"\\\".wid uintptr) (? int)\\n"
    

    この変更は、Goの組み込み関数 copy の内部的な宣言における第3引数 wid の型を uint32 から uintptr に変更しています。

    • @\"\\\".copy: これはGoの組み込み copy 関数に対応する内部的な名前です。
    • @\"\\\".to any, @\"\\\".fr any: copy 関数の最初の2つの引数は、コピー元とコピー先を表し、any は任意の型を受け入れることを示します(Go 1.18以前では interface{} に相当)。
    • @\"\\\".wid uint32 から @\"\\\".wid uintptr へ: ここが変更点です。wid は "width" または "size" を意味し、コピーする要素の数やバイト数を表すと考えられます。
      • uint32: 符号なし32ビット整数。
      • uintptr: ポインタのサイズに合わせた符号なし整数。 この変更は、特に64ビットシステムにおいて、copy 関数がより大きなメモリ領域を効率的かつ正確に扱えるようにするためのものです。uintptr はポインタのサイズと同じ幅を持つため、メモリのアドレスやサイズを扱う際に、システムアーキテクチャに合わせた最適なサイズで情報を渡すことができます。これにより、メモリ操作の正確性とパフォーマンスが向上する可能性があります。
  3. newselect 関数の引数型の変更:

    -	"func @\"\\\".newselect(@\"\\\".size int) (@\"\\\".sel *byte)\\n"
    +	"func @\"\\\".newselect(@\"\\\".size int32) (@\"\\\".sel *byte)\\n"
    

    この変更は、Goランタイム内部の newselect 関数の宣言における第1引数 size の型を int から int32 に変更しています。

    • @\"\\\".newselect: これはGoランタイムが内部的に使用する select 関連の関数に対応する名前です。Goの select ステートメントの内部実装や、I/O多重化のメカニズムに関連している可能性があります。
    • @\"\\\".size int から @\"\\\".size int32 へ: ここが変更点です。size は、newselect が扱う何らかのサイズ情報(例: 監視するチャネルの数など)を表すと考えられます。
      • int: プラットフォーム依存の符号付き整数(32ビットまたは64ビット)。
      • int32: 常に32ビットの符号付き整数。 この変更は、newselect 関数が扱うサイズ情報が、異なるアーキテクチャ間でも一貫して32ビットとして扱われることを保証するためのものです。これにより、クロスプラットフォームでの互換性が向上し、特定のシステムコールや内部データ構造が32ビット整数を期待する場合の潜在的な問題を回避できます。

これらの変更は、Goコンパイラとランタイムの内部的な整合性を維持し、Go言語の進化と異なるアーキテクチャへの対応を考慮した、低レベルなメンテナンスと最適化の一環として行われたものです。

関連リンク

参考にした情報源リンク