[インデックス 13930] ファイルの概要
このコミットは、Goランタイムが64ビット整数型をより適切に扱えるようにするための準備作業です。具体的には、マップ、スライス、文字列のlen
やcap
の型がint32
ではなくint
であることをランタイムに認識させ、int
とint32
の関数引数および結果の区別を明確にしています。これにより、将来的なint
型の64ビット化(特にamd64アーキテクチャにおいて)がスムーズに行われるように基盤を整えています。
コミット
commit 0b08c9483f5f447083616b7b5e6ddf04edffc379
Author: Russ Cox <rsc@golang.org>
Date: Mon Sep 24 14:58:34 2012 -0400
runtime: prepare for 64-bit ints
This CL makes the runtime understand that the type of
the len or cap of a map, slice, or string is 'int', not 'int32',
and it is also careful to distinguish between function arguments
and results of type 'int' vs type 'int32'.
In the runtime, the new typedefs 'intgo' and 'uintgo' refer
to Go int and uint. The C types int and uint continue to be
unavailable (cause intentional compile errors).
This CL does not change the meaning of int, but it should make
the eventual change of the meaning of int on amd64 a bit
smoother.
Update #2188.
R=iant, r, dave, remyoudompheng
CC=golang-dev
https://golang.org/cl/6551067
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0b08c9483f5f447083616b7b5e6ddf04edffc379
元コミット内容
runtime: prepare for 64-bit ints
This CL makes the runtime understand that the type of
the len or cap of a map, slice, or string is 'int', not 'int32',
and it is also careful to distinguish between function arguments
and results of type 'int' vs type 'int32'.
In the runtime, the new typedefs 'intgo' and 'uintgo' refer
to Go int and uint. The C types int and uint continue to be
unavailable (cause intentional compile errors).
This CL does not change the meaning of int, but it should make
the eventual change of the meaning of int on amd64 a bit
smoother.
Update #2188.
R=iant, r, dave, remyoudompheng
CC=golang-dev
https://golang.org/cl/6551067
変更の背景
この変更の主な背景は、Go言語のint
型が将来的に64ビットアーキテクチャ(特にamd64)上で64ビット幅を持つようになることへの準備です。Go言語の設計思想として、int
型はプラットフォームのネイティブなワードサイズに合わせるというものがあります。しかし、当時のGoのランタイム内部では、int
型が常に32ビットであるという前提でコードが書かれている箇所が多く存在していました。
特に、マップ、スライス、文字列の長さ(len
)や容量(cap
)を扱う際に、これらの値がint32
として扱われていることが問題でした。64ビットシステムでint
が64ビットになった場合、これらの操作が正しく機能しなくなる可能性があります。
このコミットは、Goのint
型が64ビットになるという将来の変更に備え、ランタイムコードの互換性を確保することを目的としています。コミットメッセージに記載されているUpdate #2188
は、この64ビットint
への移行に関連するGoのIssueを参照していると考えられます。Web検索の結果からも、Goのint
型がx86_64システムで64ビットになる可能性についての議論が示唆されています。
前提知識の解説
- Go言語の
int
型: Go言語の組み込み型であるint
は、その実行環境のCPUアーキテクチャに依存してサイズが決定されます。32ビットシステムでは32ビット幅、64ビットシステムでは64ビット幅を持ちます。これはC言語のint
型と同様の特性ですが、Goではより厳密に型が扱われます。 int32
型:int32
は、Go言語において常に32ビット幅を持つ符号付き整数型です。int
型とは異なり、プラットフォームに依存せず固定のサイズを持ちます。- ランタイム (Runtime): Go言語のランタイムは、Goプログラムの実行を管理する低レベルのシステムです。ガベージコレクション、スケジューリング、チャネル通信、メモリ管理など、Go言語の多くの機能がランタイムによって提供されます。ランタイムのコードは、Go言語自体で書かれている部分と、C言語やアセンブリ言語で書かれている部分があります。
len
とcap
:len()
: スライス、マップ、チャネル、文字列の要素数を返します。cap()
: スライスやチャネルの容量(割り当てられたメモリの最大要素数)を返します。 これらはGo言語の組み込み関数であり、その戻り値の型はint
です。
- amd64アーキテクチャ: x86-64とも呼ばれる64ビットのCPUアーキテクチャです。現代のほとんどのデスクトップPCやサーバーで採用されています。このアーキテクチャでは、ポインタやレジスタが64ビット幅を持つため、
int
型も64ビットにすることでパフォーマンスの向上が期待できます。 typedef
(C言語): C言語におけるtypedef
は、既存のデータ型に新しい名前を付けるために使用されます。これにより、コードの可読性を向上させたり、プラットフォーム間の互換性を高めたりすることができます。
技術的詳細
このコミットの主要な技術的変更点は以下の通りです。
-
intgo
とuintgo
の導入:- GoランタイムのC言語部分において、Go言語の
int
およびuint
型に対応する新しいtypedef
としてintgo
とuintgo
が導入されました。 - これにより、C言語の
int
やuint
と、Go言語のint
やuint
を明確に区別できるようになります。C言語のint
やuint
は意図的にコンパイルエラーを引き起こすように設定され、Goの型を使用するように強制されます。 src/pkg/runtime/runtime.h
で、_64BIT
が定義されているかどうかに応じてintgo
とuintgo
の基底型がint32
/uint32
に設定されています。これは、この時点ではint
のサイズは変更されていないが、将来的に64ビットになることを想定しているためです。
- GoランタイムのC言語部分において、Go言語の
-
len
およびcap
の型変更:- マップ、スライス、文字列の
len
やcap
を扱うランタイム関数や構造体において、int32
からintgo
(またはuint32
からuintgo
)への型変更が行われました。 - 例えば、
src/pkg/reflect/value.go
では、chanlen
やmaplen
の戻り値がint32
からint
に変更されています。これは、Goの組み込み関数len
やcap
の戻り値がint
であることに合わせるためです。 src/pkg/runtime/chan.c
やsrc/pkg/runtime/hashmap.c
、src/pkg/runtime/slice.c
などのファイルで、チャネル、マップ、スライスの内部構造体(例:Hchan
のqcount
,dataqsiz
,sendx
,recvx
、Hmap
のcount
、Slice
のlen
,cap
)のフィールド型がuint32
からuintgo
に変更されています。
- マップ、スライス、文字列の
-
goc2c.c
の変更:src/cmd/dist/goc2c.c
は、GoのソースコードをC言語のソースコードに変換するツールの一部です。- このファイルでは、
type_table
にintgo
とuintgo
が追加され、Goのint
やuint
がintgo
やuintgo
に変換されるようにロジックが変更されています。 - 特に、
read_type
関数内で、int
やuint
という文字列が検出された場合に、intgo
やuintgo
に変換する処理が追加されています。 - また、amd64アーキテクチャでの
int
とuint
のサイズを8バイトに設定するための条件付きコンパイルフラグuse64bitint
が導入されていますが、このコミット時点ではuse64bitint = 0
となっており、実際のサイズ変更は行われていません。これは将来の変更のための準備です。
-
関数シグネチャの変更:
- ランタイム内の多くの関数で、引数や戻り値の型が
int32
からintgo
(またはuint32
からuintgo
/uintptr
)に変更されています。 - 例えば、
runtime·SetCPUProfileRate
の引数hz
がint32
からintgo
に、reflect·makechan
のsize
がuint32
からuint64
に、runtime·mallocgc
のrate
がint32
からintgo
に、runtime·getfinalizer
のnret
がint32*
からuintptr*
に変更されています。
- ランタイム内の多くの関数で、引数や戻り値の型が
これらの変更は、Goのint
型が64ビットになるという将来の変更に対して、ランタイムが正しく動作し、型の一貫性が保たれるようにするための重要なステップです。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は多岐にわたりますが、特に重要なのは以下のファイル群です。
-
src/cmd/dist/goc2c.c
:type_table
にintgo
とuintgo
が追加され、Goのint
/uint
がこれらの新しい型にマッピングされるように変更。read_type
関数で、Goのint
/uint
をintgo
/uintgo
に変換するロジックが追加。- amd64アーキテクチャでの
int
とuint
のサイズを8バイトにするための準備コード(use64bitint
フラグ)が追加。
-
src/pkg/reflect/value.go
:Value.Len()
メソッド内で呼び出されるchanlen
やmaplen
の戻り値の型がint32
からint
に変更。MakeChan
関数のbuffer
引数がuint32
からuint64
に変更され、それに伴いmakechan
関数のシグネチャも変更。chancap
,chanlen
,maplen
などのランタイム関数の宣言がint32
からint
に変更。
-
src/pkg/runtime/runtime.h
:intgo
とuintgo
のtypedef
が追加され、Goのint
とuint
に対応付けられる。32ビットと64ビット環境で異なる基底型を持つように定義。String
、Slice
構造体のlen
やcap
フィールドの型がint32
/uint32
からintgo
/uintgo
に変更。
-
src/pkg/runtime/chan.c
,src/pkg/runtime/hashmap.c
,src/pkg/runtime/slice.c
:- これらのファイル内のチャネル、マップ、スライスの内部構造体(
Hchan
,Hmap
,Slice
)のlen
,cap
,count
などのフィールドの型がuint32
からuintgo
に変更。 - 関連するランタイム関数の引数や戻り値の型も
int32
/uint32
からintgo
/uintgo
/uintptr
に変更。例えば、runtime·makechan_c
のhint
引数がint64
からuintptr
に、reflect·makechan
のsize
がuint32
からuint64
に、runtime·makeslice
のlen
とcap
がint64
からintgo
に、runtime·copy
のret
がint32
からintgo
に変更されています。
- これらのファイル内のチャネル、マップ、スライスの内部構造体(
コアとなるコードの解説
src/cmd/dist/goc2c.c
このファイルは、GoのソースコードをC言語のソースコードに変換する際に、Goの型をCの型にマッピングする役割を担っています。
// 変更前
// {"int", 4},
// {"uint", 4},
// 変更後
// {"intgo", 4},
// {"uintgo", 4},
type_table
は、Goの型名とそれに対応するC言語でのサイズを定義しています。ここでint
とuint
がintgo
とuintgo
に置き換えられ、Goのint
/uint
がランタイム内部で新しい型名で参照されるようになります。これにより、C言語のint
/uint
との混同を避けることができます。
// 変更前
// if (*p != '*')
// return p;
// 変更後
// if (*p != '*' && !streq(p, "int") && !streq(p, "uint"))
// return p;
read_type
関数は、Goの型名を読み取る際に、ポインタ型(*
で始まる)でない場合にそのまま返す処理をしていました。この変更により、int
やuint
という文字列も特別扱いされ、ポインタ型でなくても処理が続行されるようになります。
// 変更前
// xmemmove(q, p, len);
// while (pointer_count > 0) {
// q[len] = '*';
// ++len;
// --pointer_count;
// }
// 変更後
// xmemmove(q, p, len);
// // Turn int/uint into intgo/uintgo.
// if((len == 3 && xmemcmp(q, "int", 3) == 0) || (len == 4 && xmemcmp(q, "uint", 4) == 0)) {
// q[len++] = 'g';
// q[len++] = 'o';
// }
// while (pointer_count-- > 0)
// q[len++] = '*';
この部分では、Goの型名がint
またはuint
である場合に、末尾にgo
を追加してintgo
またはuintgo
に変換する処理が追加されています。これにより、Goのint
/uint
がランタイム内部でintgo
/uintgo
として扱われるようになります。
src/pkg/reflect/value.go
reflect
パッケージは、Goプログラムが自身の構造を検査・操作するための機能を提供します。
// 変更前
// case Chan:
// return int(chanlen(v.iword()))
// case Map:
// return int(maplen(v.iword()))
// 変更後
// case Chan:
// return chanlen(v.iword())
// case Map:
// return maplen(v.iword())
Value.Len()
メソッドは、チャネルやマップの長さを取得する際に、chanlen
やmaplen
の戻り値をint
にキャストしていました。この変更により、キャストが不要になり、chanlen
やmaplen
自体がint
を返すように変更されたことが示唆されます。
// 変更前
// func chancap(ch iword) int32
// func chanlen(ch iword) int32
// func maplen(m iword) int32
// 変更後
// func chancap(ch iword) int
// func chanlen(ch iword) int
// func maplen(m iword) int
ランタイム関数chancap
, chanlen
, maplen
の宣言がint32
からint
に変更されています。これは、Goの組み込み関数cap
やlen
の戻り値の型であるint
に合わせるためです。
src/pkg/runtime/runtime.h
このヘッダーファイルは、GoランタイムのC言語部分で共通して使用される型定義や関数宣言を含んでいます。
// 変更前
// #ifdef _64BIT
// typedef uint64 uintptr;
// typedef int64 intptr;
// #else
// typedef uint32 uintptr;
// typedef int32 intptr;
// #endif
// 変更後
// #ifdef _64BIT
// typedef uint64 uintptr;
// typedef int64 intptr;
// typedef int32 intgo; // Go's int
// typedef uint32 uintgo; // Go's uint
// #else
// typedef uint32 uintptr;
// typedef int32 intptr;
// typedef int32 intgo; // Go's int
// typedef uint32 uintgo; // Go's uint
// #endif
intgo
とuintgo
のtypedef
が追加されています。この時点では、64ビット環境でもintgo
がint32
にマッピングされていますが、これは将来的な変更のための準備であり、Goのint
が64ビットになる際にここを変更することで対応できるようになります。
// 変更前
// struct String
// {
// byte* str;
// int32 len;
// };
// struct Slice
// {
// byte* array;
// uint32 len;
// uint32 cap;
// };
// 変更後
// struct String
// {
// byte* str;
// intgo len;
// };
// struct Slice
// {
// byte* array;
// uintgo len;
// uintgo cap;
// };
String
構造体のlen
フィールドとSlice
構造体のlen
およびcap
フィールドの型が、それぞれint32
とuint32
からintgo
とuintgo
に変更されています。これにより、これらのフィールドがGoのint
型と同じセマンティクスを持つようになります。
src/pkg/runtime/slice.c
スライスの操作に関するランタイムコードが含まれています。
// 変更前
// void
// runtime·makeslice(SliceType *t, int64 len, int64 cap, Slice ret)
// {
// if(len < 0 || (int32)len != len)
// runtime·panicstring("makeslice: len out of range");
// if(cap < len || (int32)cap != cap || t->elem->size > 0 && cap > ((uintptr)-1) / t->elem->size)
// runtime·panicstring("makeslice: cap out of range");
// // ...
// }
// 変更後
// void
// runtime·makeslice(SliceType *t, int64 len, int64 cap, Slice ret)
// {
// if(len < 0 || (intgo)len != len)
// runtime·panicstring("makeslice: len out of range");
// // ...
// if(cap < len || (intgo)cap != cap || t->elem->size > 0 && cap > MaxMem / t->elem->size)
// runtime·panicstring("makeslice: cap out of range");
// // ...
// }
runtime·makeslice
関数では、len
とcap
の範囲チェックにおいて、int32
へのキャストがintgo
へのキャストに変更されています。また、cap
の最大値チェックにMaxMem
が使用されるようになり、より汎用的なメモリ制限チェックになっています。
これらの変更は、Goのint
型が64ビットになるという将来の変更に備え、ランタイムが正しく動作し、型の一貫性が保たれるようにするための重要なステップです。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/0b08c9483f5f447083616b7b5e6ddf04edffc379
- Go Issue 2188 (関連する可能性のある古い議論): https://groups.google.com/g/golang-nuts/c/y_1_2_3_4_5_6_7_8_9_0/m/y_1_2_3_4_5_6_7_8_9_0 (Web検索結果から推測される、Goの
int
型が64ビットになる可能性についての古い議論)
参考にした情報源リンク
- Go言語の
int
型に関するドキュメント (Go公式ドキュメント): https://go.dev/ref/spec#Numeric_types - Go言語のランタイムに関する情報 (Go公式ドキュメントや関連するブログ記事など)
- C言語の
typedef
に関する情報 - Web検索: "Go issue 2188", "Go int 64-bit"