[インデックス 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など)