[インデックス 17062] ファイルの概要
このコミットは、Goランタイムにおける文字列操作に関連する内部変数 runtime·maxstring
および関数 runtime·concatstring
の型定義を修正するものです。具体的には、uint32
型から uintptr
型への変更が行われています。これにより、異なるアーキテクチャ間での互換性と正確性が向上し、特にポインタサイズが32ビットを超えるシステムでの潜在的な問題が解消されます。
コミット
commit 98a80b95b462f2c7c0cfdb4557eb08a5d6d891f2
Author: Rob Pike <r@golang.org>
Date: Wed Aug 7 06:49:11 2013 +1000
runtime: use correct types for maxstring and concatstring
Updates #6046.
This CL just does maxstring and concatstring. There are other functions
to fix but doing them a few at a time will help isolate any (unlikely)
breakages these changes bring up in architectures I can't test
myself.
R=golang-dev, dave, iant
CC=golang-dev
https://golang.org/cl/12519044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/98a80b95b462f2c7c0cfdb4557eb08a5d6d891f2
元コミット内容
このコミットの元のメッセージは以下の通りです。
runtime: use correct types for maxstring and concatstring
Updates #6046.
This CL just does maxstring and concatstring. There are other functions
to fix but doing them a few at a time will help isolate any (unlikely)
breakages these changes bring up in architectures I can't test
myself.
R=golang-dev, dave, iant
CC=golang-dev
https://golang.org/cl/12519044
このメッセージは、maxstring
と concatstring
の型を修正することが目的であり、関連するIssue #6046を更新することを示しています。また、一度にすべての修正を行うのではなく、段階的に適用することで、テストが困難なアーキテクチャでの潜在的な問題を特定しやすくする意図が述べられています。
変更の背景
この変更の背景には、Goランタイムが様々なアーキテクチャ(32ビット、64ビットなど)で動作する際に、ポインタのサイズやメモリのアドレス空間の表現に関する課題がありました。
Goのランタイム内部では、文字列の長さやメモリのアドレスを扱う際に、プラットフォームに依存しない適切な型を使用することが重要です。以前は uint32
が使用されていましたが、これは32ビットシステムでは問題ありませんが、64ビットシステムではアドレス空間全体を表現するには不十分です。特に、非常に長い文字列や、大きなメモリ領域を扱う場合、uint32
では表現しきれない可能性があります。
コミットメッセージで参照されているIssue #6046は、まさにこの問題、すなわちGoランタイムが内部でポインタやサイズを扱う際に、プラットフォームのワードサイズ(ポインタサイズ)に依存しない uintptr
を使用すべきであるという議論に関連しています。uint32
は32ビット固定幅の整数型であり、64ビットシステムでは最大値が約4GBに制限されます。これに対し、uintptr
はポインタを保持するのに十分な大きさの符号なし整数型であり、システムのアーキテクチャ(32ビットか64ビットか)に応じてサイズが変化します。
このコミットは、ランタイムの堅牢性と将来の拡張性を確保するために、これらの型をより汎用的な uintptr
に変更する一連の修正の初期段階として位置づけられています。
前提知識の解説
Goランタイム (Go Runtime)
Goランタイムは、Goプログラムの実行を管理する低レベルのシステムです。これには、ガベージコレクション、スケジューラ(ゴルーチンの管理)、メモリ割り当て、プリミティブな型操作などが含まれます。ランタイムのコードは、Go言語自体とC言語(またはPlan 9 C)で書かれており、GoプログラムがOS上で効率的に動作するための基盤を提供します。
uint32
と uintptr
uint32
: 32ビット幅の符号なし整数型です。最大値は約42億9千万(2^32 - 1)です。これは、32ビットシステムではメモリのアドレス空間全体を表現できますが、64ビットシステムではアドレス空間の一部しか表現できません。uintptr
: ポインタを保持するのに十分な大きさの符号なし整数型です。そのサイズは、実行されているシステムのアーキテクチャ(32ビットまたは64ビット)に依存します。32ビットシステムでは32ビット、64ビットシステムでは64ビットになります。Goの仕様では、uintptr
はポインタを整数に変換したり、整数をポインタに変換したりする際に使用されますが、その値が指すメモリの内容を直接操作することは推奨されていません。主に、低レベルのシステムプログラミングや、C言語との相互運用性において、ポインタのサイズを抽象化するために使用されます。
String
型 (Goランタイム内部)
Go言語の組み込み string
型は、内部的にはポインタと長さのペアとして表現されます。ランタイム内部では、この構造体が String
型として定義されており、文字列データへのポインタと、その文字列のバイト長を保持します。
runtime·cas
と runtime·casp
これらはGoランタイム内部で使用されるアトミック操作の関数です。
runtime·cas
(Compare And Swap): 比較と交換のアトミック操作です。特定のメモリ位置の値が期待する値と一致する場合にのみ、そのメモリ位置の値を新しい値に更新します。これは、複数のゴルーチンが共有データに同時にアクセスする際に、競合状態を防ぐために使用されます。通常、runtime·cas(&ptr, oldval, newval)
のように使用され、ptr
がoldval
と等しければnewval
に更新します。runtime·casp
(Compare And Swap Pointer):runtime·cas
のポインタ版です。ポインタの値をアトミックに比較し、交換します。このコミットでは、uint32
からuintptr
への型変更に伴い、uint32
を扱うruntime·cas
から、ポインタ(またはポインタサイズの値)を扱うruntime·casp
へと変更されています。これは、runtime·maxstring
がuintptr
になったため、その値をアトミックに更新する際にuintptr
を引数として受け取るruntime·casp
が適切だからです。
runtime·maxstring
Goランタイム内部で使用される変数で、主に文字列の最大長に関するヒントとして機能します。特に、runtime·printstring
のようなデバッグや診断目的の関数で、非常に長い文字列が出力されるのを防ぐために使用されます。この値を超過する文字列は、切り詰められたり、特別な表示(例: [string too long]
)に置き換えられたりすることがあります。
runtime·concatstring
Goランタイム内部で使用される関数で、複数の文字列を連結する処理を効率的に行います。Go言語の +
演算子による文字列連結や、strings.Builder
のような標準ライブラリの背後で、この低レベルのランタイム関数が利用されることがあります。
技術的詳細
このコミットの核心は、Goランタイムが内部で文字列の長さやポインタサイズに依存する値を扱う際に、より適切な型である uintptr
を使用するように変更することです。
-
runtime·maxstring
の型変更:src/pkg/runtime/runtime.h
において、extern uint32 runtime·maxstring;
がextern uintptr runtime·maxstring;
に変更されました。src/pkg/runtime/string.goc
において、uint32 runtime·maxstring = 256;
がuintptr runtime·maxstring = 256;
に変更されました。- これにより、
runtime·maxstring
が32ビット固定ではなく、実行環境のポインタサイズに応じた幅を持つようになります。これは、特に64ビットシステムで、より大きな文字列長を適切に扱うために重要です。
-
gostringsize
関数内の型変更とアトミック操作の変更:src/pkg/runtime/string.goc
のgostringsize
関数内で、ローカル変数ms
の型がuint32
からuintptr
に変更されました。- 最も重要な変更は、
runtime·cas
の呼び出しがruntime·casp
に変更された点です。- 変更前:
if((uint32)l <= ms || runtime·cas(&runtime·maxstring, ms, (uint32)l))
- 変更後:
if((uintptr)l <= ms || runtime·casp((void**)&runtime·maxstring, (void*)ms, (void*)l))
runtime·maxstring
がuintptr
型になったため、その値をアトミックに更新する際には、ポインタを扱うruntime·casp
が必要になります。runtime·casp
は第一引数にvoid**
を、第二・第三引数にvoid*
を期待するため、型キャストが行われています。これは、uintptr
がポインタサイズと同じ幅を持つため、ポインタとして扱うことができるというGoランタイムの設計思想に基づいています。
- 変更前:
-
runtime·concatstring
の引数型変更:src/pkg/runtime/string.goc
において、runtime·concatstring
関数の第一引数n
の型がint32
からintgo
に変更されました。intgo
はGoランタイム内部で使用される型で、Go言語のint
型に対応します。int
型は、実行環境のワードサイズ(32ビットまたは64ビット)に応じてサイズが変化します。これにより、concatstring
が受け取る文字列の数n
が、プラットフォームに依存しない適切なサイズで扱われるようになります。
-
runtime·printstring
からのruntime·maxstring
宣言の削除:src/pkg/runtime/print.c
からextern uint32 runtime·maxstring;
の行が削除されました。これは、runtime·maxstring
の宣言がruntime.h
に一元化されたため、冗長な宣言が不要になったためです。
これらの変更は、Goランタイムがメモリサイズやポインタを扱う際の内部的な整合性を高め、特に64ビットアーキテクチャでの潜在的なバグを防ぐことを目的としています。
コアとなるコードの変更箇所
src/pkg/runtime/runtime.h
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -706,7 +706,7 @@ extern int8* runtime·goos;
extern int32 runtime·ncpu;
extern bool runtime·iscgo;
extern void (*runtime·sysargs)(int32, uint8**);
-extern uint32 truntime·maxstring;
+extern uintptr truntime·maxstring;
extern uint32 truntime·Hchansize;
extern uint32 truntime·cpuid_ecx;
extern uint32 truntime·cpuid_edx;
src/pkg/runtime/string.goc
--- a/src/pkg/runtime/string.goc
+++ b/src/pkg/runtime/string.goc
@@ -35,13 +35,13 @@ runtime·findnullw(uint16 *s)
return l;
}
-uint32 runtime·maxstring = 256; // a hint for print
+uintptr runtime·maxstring = 256; // a hint for print
static String
gostringsize(intgo l)
{
String s;
- uint32 ms;
+ uintptr ms;
if(l == 0)
return runtime·emptystring;
@@ -51,7 +51,7 @@ gostringsize(intgo l)
s.str[l] = 0;
for(;;) {
ms = runtime·maxstring;
- if((uint32)l <= ms || runtime·cas(&runtime·maxstring, ms, (uint32)l))
+ if((uintptr)l <= ms || runtime·casp((void**)&runtime·maxstring, (void*)ms, (void*)l))
break;
}
return s;
@@ -176,7 +176,7 @@ concatstring(intgo n, String *s)
// not have a fixed size argument count.
#pragma textflag 7
void
-runtime·concatstring(int32 n, String s1, ...)
+runtime·concatstring(intgo n, String s1, ...)
{
(&s1)[n] = concatstring(n, &s1);
}
src/pkg/runtime/print.c
--- a/src/pkg/runtime/print.c
+++ b/src/pkg/runtime/print.c
@@ -350,8 +350,6 @@ runtime·printpointer(void *p)
void
runtime·printstring(String v)
{
- extern uint32 runtime·maxstring;
-
if(v.len > runtime·maxstring) {
gwrite("[string too long]", 17);
return;
コアとなるコードの解説
runtime.h
の変更
runtime.h
はGoランタイムのグローバルな定義や外部変数を宣言するヘッダーファイルです。ここで runtime·maxstring
の型が uint32
から uintptr
に変更されたことは、この変数がシステム全体のポインタサイズに適合するように意図されていることを明確に示しています。これにより、32ビットシステムでは32ビット、64ビットシステムでは64ビットのサイズを持つようになり、メモリのアドレス空間全体を表現できるようになります。
string.goc
の変更
string.goc
はGoランタイムにおける文字列操作の低レベルな実装を含むファイルです。
-
runtime·maxstring
の初期化と型変更:uint32 runtime·maxstring = 256;
からuintptr runtime·maxstring = 256;
への変更は、宣言だけでなく初期化時にもuintptr
型が適用されることを示しています。これは、runtime·maxstring
が文字列の長さを制限する「ヒント」として機能する際に、より大きな値を扱えるようにするためです。 -
gostringsize
関数内のms
変数とruntime·casp
の使用:gostringsize
関数は、指定された長さl
の文字列を割り当てる際に使用されます。この関数内で、runtime·maxstring
の値を一時的に保持するローカル変数ms
の型もuint32
からuintptr
に変更されています。 最も重要なのは、runtime·cas
からruntime·casp
への変更です。runtime·cas(&runtime·maxstring, ms, (uint32)l)
は、runtime·maxstring
の値がms
と等しい場合に、その値を(uint32)l
にアトミックに更新しようとします。 しかし、runtime·maxstring
がuintptr
になったため、ポインタサイズの値のアトミックな比較と交換にはruntime·casp
が必要になります。runtime·casp((void**)&runtime·maxstring, (void*)ms, (void*)l)
は、runtime·maxstring
のアドレスをvoid**
にキャストし、ms
とl
の値をvoid*
にキャストして渡しています。これは、uintptr
がポインタと同じサイズを持つため、ポインタとして扱うことができるというGoランタイムの内部的な慣習に基づいています。この変更により、runtime·maxstring
の更新が64ビットシステムでも正しく、かつアトミックに行われることが保証されます。 -
runtime·concatstring
の引数型変更:runtime·concatstring
は可変引数を受け取るC言語スタイルの関数です。第一引数n
は連結する文字列の数を表します。このn
の型がint32
からintgo
に変更されました。intgo
はGoのint
型に対応するランタイム内部の型であり、システムのワードサイズに依存します。これにより、連結する文字列の数が32ビットの範囲に限定されず、より多くの文字列を連結できるようになる可能性があります(ただし、実際にはGoの関数呼び出し規約やスタックフレームの制約により、連結できる文字列の数には実用的な上限があります)。この変更は、ランタイム内部の型の一貫性を保ち、プラットフォーム非依存性を高めるためのものです。
print.c
の変更
print.c
はGoランタイムのデバッグ出力に関連する関数を含むファイルです。
runtime·printstring
関数から extern uint32 runtime·maxstring;
の行が削除されました。これは、runtime·maxstring
の宣言が runtime.h
に一元化されたため、各ファイルで個別に extern
宣言する必要がなくなったためです。これにより、コードの重複が減り、保守性が向上します。
関連リンク
- Go Issue #6046: https://github.com/golang/go/issues/6046
- このIssueは、Goランタイムが内部でポインタやサイズを扱う際に、プラットフォームのワードサイズに依存しない
uintptr
を使用すべきであるという広範な議論の一部です。このコミットは、そのIssueに対する具体的な修正の一つです。
- このIssueは、Goランタイムが内部でポインタやサイズを扱う際に、プラットフォームのワードサイズに依存しない
参考にした情報源リンク
- Go言語のソースコード (特に
src/pkg/runtime
ディレクトリ) - Go言語のドキュメント (特に
unsafe
パッケージやuintptr
に関する記述) - Go言語のIssueトラッカー (Issue #6046)
- アトミック操作に関する一般的な知識 (Compare-and-swapなど)